From 37dd7de28972a74979259b64f51898e716ae4c60 Mon Sep 17 00:00:00 2001 From: Nora Abi Akar <nora.abiakar@gmail.com> Date: Fri, 27 Aug 2021 14:25:15 +0200 Subject: [PATCH] Check that mechanisms have the right kind (#1633) - Expose `arb_mechanism_kind` to `mechanism_info`. - While building mechanism data in `fvm_build_mechanism_data`, check that synapses, painted region dynamics and reversal potential methods have the expected `arb_mechanism_kind`. - Add unit test. --- arbor/fvm_layout.cpp | 19 +++++++++++++++---- arbor/include/arbor/mechanism_abi.h | 9 +++++++++ arbor/include/arbor/mechinfo.hpp | 3 +++ arbor/mechinfo.cpp | 1 + doc/python/mechanisms.rst | 10 ++++++++++ python/mechanism.cpp | 6 ++++++ test/unit/test_fvm_layout.cpp | 25 +++++++++++++++++++++++++ 7 files changed, 69 insertions(+), 4 deletions(-) diff --git a/arbor/fvm_layout.cpp b/arbor/fvm_layout.cpp index 3261726f..d2ee99ce 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 ce85067f..83770e2e 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 3e4ff24b..904fa90d 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 abfe6c97..341568a7 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 54c6a32e..cb19925f 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 2a27eccb..16f9f25e 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 6d4e3a5a..981b291a 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; -- GitLab