diff --git a/arbor/fvm_layout.cpp b/arbor/fvm_layout.cpp index 3261726fec562f8f7f0fa87132b327538ffb6c92..d2ee99ce301ddf0c16f93dcba030223c3e69afc1 100644 --- a/arbor/fvm_layout.cpp +++ b/arbor/fvm_layout.cpp @@ -858,14 +858,17 @@ fvm_mechanism_data fvm_build_mechanism_data(const cable_cell_global_properties& const std::string& name = entry.first; mechanism_info info = catalogue[name]; - std::vector<double> param_dflt; fvm_mechanism_config config; - config.kind = arb_mechanism_kind_density; + if (info.kind != arb_mechanism_kind_density) { + throw cable_cell_error("expected density mechanism, got " +name +" which has " +arb_mechsnism_kind_str(info.kind)); + } + config.kind = info.kind; std::vector<std::string> param_names; assign(param_names, util::keys(info.parameters)); sort(param_names); + std::vector<double> param_dflt; std::size_t n_param = param_names.size(); param_dflt.reserve(n_param); config.param_values.reserve(n_param); @@ -963,6 +966,10 @@ fvm_mechanism_data fvm_build_mechanism_data(const cable_cell_global_properties& const std::string& name = entry.first; mechanism_info info = catalogue[name]; + if (info.kind != arb_mechanism_kind_point) { + throw cable_cell_error("expected point mechanism, got " +name +" which has " +arb_mechsnism_kind_str(info.kind)); + } + post_events |= info.post_events; std::size_t n_param = info.parameters.size(); std::size_t n_inst = entry.second.size(); @@ -1044,7 +1051,7 @@ fvm_mechanism_data fvm_build_mechanism_data(const cable_cell_global_properties& bool coalesce = catalogue[name].linear && gprop.coalesce_synapses; fvm_mechanism_config config; - config.kind = arb_mechanism_kind_point; + config.kind = info.kind; for (auto& kv: info.parameters) { config.param_values.emplace_back(kv.first, std::vector<value_type>{}); if (!coalesce) { @@ -1210,6 +1217,10 @@ fvm_mechanism_data fvm_build_mechanism_data(const cable_cell_global_properties& { const mechanism_desc& revpot = *maybe_revpot; mechanism_info info = catalogue[revpot.name()]; + if (info.kind != arb_mechanism_kind_reversal_potential) { + throw cable_cell_error("expected reversal potential mechanism for ion " +ion +", got "+ revpot.name() +" which has " +arb_mechsnism_kind_str(info.kind)); + } + verify_mechanism(info, revpot); revpot_specified.insert(ion); @@ -1248,7 +1259,7 @@ fvm_mechanism_data fvm_build_mechanism_data(const cable_cell_global_properties& } else { fvm_mechanism_config config; - config.kind = arb_mechanism_kind_reversal_potential; + config.kind = info.kind; config.cv = M.ions[ion].cv; config.norm_area.assign(config.cv.size(), 1.); diff --git a/arbor/include/arbor/mechanism_abi.h b/arbor/include/arbor/mechanism_abi.h index ce85067fbb15afd70c43329db8adb197e372c533..83770e2e4be45fb7b160332bd66f75ea5e8bf5b5 100644 --- a/arbor/include/arbor/mechanism_abi.h +++ b/arbor/include/arbor/mechanism_abi.h @@ -27,6 +27,15 @@ typedef uint32_t arb_backend_kind; #define arb_backend_kind_cpu 1 #define arb_backend_kind_gpu 2 +inline const char* arb_mechsnism_kind_str(const arb_mechanism_kind& mech) { + switch (mech) { + case arb_mechanism_kind_density: return "density mechanism kind"; + case arb_mechanism_kind_point: return "point mechanism kind"; + case arb_mechanism_kind_reversal_potential: return "reversal potential mechanism kind"; + default: return "unknown mechanism kind"; + } +} + // Ion state variables; view into shared_state typedef struct arb_ion_state { arb_value_type* current_density; diff --git a/arbor/include/arbor/mechinfo.hpp b/arbor/include/arbor/mechinfo.hpp index 3e4ff24b81f9ec5c87ee900bf52a1d391e41ceb9..904fa90d8ca103934bffdad261abc558fb7c5d40 100644 --- a/arbor/include/arbor/mechinfo.hpp +++ b/arbor/include/arbor/mechinfo.hpp @@ -58,6 +58,9 @@ struct mechanism_info { mechanism_info(const arb_mechanism_type&); mechanism_info() = default; + // Mechanism kind + arb_mechanism_kind kind; + // Global fields have one value common to an instance of a mechanism, are // constant in time and set at instantiation. std::unordered_map<std::string, mechanism_field_spec> globals; diff --git a/arbor/mechinfo.cpp b/arbor/mechinfo.cpp index abfe6c978e237dcf7b25b42ef53c2233b90ab95b..341568a78c3a6b3d5aa42617945a0023674fc973 100644 --- a/arbor/mechinfo.cpp +++ b/arbor/mechinfo.cpp @@ -4,6 +4,7 @@ namespace arb { mechanism_info::mechanism_info(const arb_mechanism_type& m) { + kind = m.kind; post_events = m.has_post_events; linear = m.is_linear; fingerprint = m.fingerprint; diff --git a/doc/python/mechanisms.rst b/doc/python/mechanisms.rst index 54c6a32efa401a12391a42e14340e4b93c057165..cb19925fcc711abec7d000719d0413153d4c008c 100644 --- a/doc/python/mechanisms.rst +++ b/doc/python/mechanisms.rst @@ -138,6 +138,11 @@ mechanism that is to be painted or placed on the cable cell. print(mech.parameters['tau'].default) # 2.0 + .. py:attribute:: kind + :type: string + + String representation of the kind of the mechanism: density, point or reversal potential. + .. py:attribute:: globals :type: dict[str, mechanism_field] @@ -163,6 +168,11 @@ mechanism that is to be painted or placed on the cable cell. True if a synapse mechanism has linear current contributions so that multiple instances on the same :term:`control volume` can be coalesced. + .. py:attribute:: post_events + :type: bool + + True if a synapse mechanism has a `POST_EVENT` procedure defined. + .. py:class:: ion_dependency diff --git a/python/mechanism.cpp b/python/mechanism.cpp index 2a27eccbcdfbb50dcdf295b719fdc3d14b22f2cd..16f9f25e8388f36135b79a2a9fccab956e1112e6 100644 --- a/python/mechanism.cpp +++ b/python/mechanism.cpp @@ -96,6 +96,12 @@ void register_mechanisms(pybind11::module& m) { "Ion dependencies.") .def_readonly("linear", &arb::mechanism_info::linear, "True if a synapse mechanism has linear current contributions so that multiple instances on the same compartment can be coalesced.") + .def_readonly("post_events", &arb::mechanism_info::post_events, + "True if a synapse mechanism has a `POST_EVENT` procedure defined.") + .def_property_readonly("kind", + [](const arb::mechanism_info& info) { + return arb_mechsnism_kind_str(info.kind); + }, "String representation of the kind of the mechanism.") .def("__repr__", [](const arb::mechanism_info& inf) { return util::pprintf("(arbor.mechanism_info)"); }) diff --git a/test/unit/test_fvm_layout.cpp b/test/unit/test_fvm_layout.cpp index 6d4e3a5a98b82b4a00473b486ece78759c00cdd0..981b291a064c42fb6b85e8e6209d715c37929381 100644 --- a/test/unit/test_fvm_layout.cpp +++ b/test/unit/test_fvm_layout.cpp @@ -129,6 +129,31 @@ namespace { } } // namespace +template<typename P> +void check_compatible_mechanism_failure(cable_cell_global_properties gprop, P painter) { + auto system = two_cell_system(); + + painter(system); + + auto cells = system.cells(); + check_two_cell_system(cells); + fvm_cv_discretization D = fvm_cv_discretize(cells, gprop.default_parameters); + + EXPECT_THROW(fvm_build_mechanism_data(gprop, cells, D), arb::cable_cell_error); +} + +TEST(fvm_layout, compatible_mechanisms) { + cable_cell_global_properties gprop; + gprop.default_parameters = neuron_parameter_defaults; + + check_compatible_mechanism_failure(gprop, [](auto& sys) {sys.descriptions[0].decorations.place(sys.builders[0].location({1, 0.4}), "hh", "syn0"); }); + check_compatible_mechanism_failure(gprop, [](auto& sys) {sys.descriptions[1].decorations.paint(sys.builders[1].cable(mcable{0}), "expsyn"); }); + check_compatible_mechanism_failure(gprop, [](auto& sys) {sys.descriptions[0].decorations.set_default(ion_reversal_potential_method{"na", "expsyn"}); }); + + gprop.default_parameters.reversal_potential_method["na"] = "pas"; + check_compatible_mechanism_failure(gprop, [](auto& sys) {}); +} + TEST(fvm_layout, mech_index) { auto system = two_cell_system(); auto& descriptions = system.descriptions;