-
Nora Abi Akar authored
Extend Ptyhon recipe wrapper to support setting of global cell properties. * Rename `pyrecipe::get_probes` to `py_recipe::probes` * Remove `global_properties_shim` : It's easy to make mistakes with the class, as it holds a `mechanism_catalogue` and a `cable_cell_global_properties` which holds a pointer to the catalogue. This would likely have caused issues with the users. * Expose `py_recipe::global_properties`
Unverified5fdfb02e
mechanism.cpp 9.26 KiB
#include <optional>
#include <stdexcept>
#include <pybind11/pybind11.h>
#include "pybind11/pytypes.h"
#include <pybind11/stl.h>
#include <arbor/cable_cell_param.hpp>
#include <arbor/mechanism.hpp>
#include <arbor/mechcat.hpp>
#include "arbor/mechinfo.hpp"
#include "conversion.hpp"
#include "strprintf.hpp"
namespace pyarb {
void apply_derive(arb::mechanism_catalogue& m,
const std::string& name,
const std::string& parent,
const std::unordered_map<std::string, double>& globals,
const std::unordered_map<std::string, std::string>& ions)
{
if (globals.empty() && ions.empty()) {
m.derive(name, parent);
return;
}
std::vector<std::pair<std::string, double>> G;
for (auto& g: globals) {
G.push_back({g.first, g.second});
}
std::vector<std::pair<std::string, std::string>> I;
for (auto& i: ions) {
I.push_back({i.first, i.second});
}
m.derive(name, parent, G, I);
}
void register_mechanisms(pybind11::module& m) {
using std::optional;
using namespace pybind11::literals;
pybind11::class_<arb::mechanism_field_spec> field_spec(m, "mechanism_field",
"Basic information about a mechanism field.");
field_spec
.def(pybind11::init<const arb::mechanism_field_spec&>())
.def_readonly("units", &arb::mechanism_field_spec::units)
.def_readonly("default", &arb::mechanism_field_spec::default_value)
.def_readonly("min", &arb::mechanism_field_spec::lower_bound)
.def_readonly("max", &arb::mechanism_field_spec::upper_bound)
.def("__repr__",
[](const arb::mechanism_field_spec& spec) {
return util::pprintf("{units: '{}', default: {}, min: {}, max: {}}",
(spec.units.size()? spec.units.c_str(): "1"), spec.default_value,
spec.lower_bound, spec.upper_bound); })
.def("__str__",
[](const arb::mechanism_field_spec& spec) {
return util::pprintf("{units: '{}', default: {}, min: {}, max: {}}",
(spec.units.size()? spec.units.c_str(): "1"), spec.default_value,
spec.lower_bound, spec.upper_bound); });
pybind11::class_<arb::ion_dependency> ion_dep(m, "ion_dependency",
"Information about a mechanism's dependence on an ion species.");
ion_dep
.def(pybind11::init<const arb::ion_dependency&>())
.def_readonly("write_int_con", &arb::ion_dependency::write_concentration_int)
.def_readonly("write_ext_con", &arb::ion_dependency::write_concentration_ext)
.def_readonly("write_rev_pot", &arb::ion_dependency::write_reversal_potential)
.def_readonly("read_rev_pot", &arb::ion_dependency::read_reversal_potential)
.def("__repr__",
[](const arb::ion_dependency& dep) {
auto tf = [](bool x) {return x? "True": "False";};
return util::pprintf("{write_int_con: {}, write_ext_con: {}, write_rev_pot: {}, read_rev_pot: {}}",
tf(dep.write_concentration_int), tf(dep.write_concentration_ext),
tf(dep.write_reversal_potential), tf(dep.read_reversal_potential)); })
.def("__str__",
[](const arb::ion_dependency& dep) {
auto tf = [](bool x) {return x? "True": "False";};
return util::pprintf("{write_int_con: {}, write_ext_con: {}, write_rev_pot: {}, read_rev_pot: {}}",
tf(dep.write_concentration_int), tf(dep.write_concentration_ext),
tf(dep.write_reversal_potential), tf(dep.read_reversal_potential)); })
;
pybind11::class_<arb::mechanism_info> mech_inf(m, "mechanism_info",
"Meta data about a mechanism's fields and ion dependendencies.");
mech_inf
.def(pybind11::init<const arb::mechanism_info&>())
.def_readonly("globals", &arb::mechanism_info::globals,
"Global fields have one value common to an instance of a mechanism, are constant in time and set at instantiation.")
.def_readonly("parameters", &arb::mechanism_info::parameters,
"Parameter fields may vary across the extent of a mechanism, but are constant in time and set at instantiation.")
.def_readonly("state", &arb::mechanism_info::state,
"State fields vary in time and across the extent of a mechanism, and potentially can be sampled at run-time.")
.def_readonly("ions", &arb::mechanism_info::ions,
"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 coalesed.")
.def("__repr__",
[](const arb::mechanism_info& inf) {
return util::pprintf("(arbor.mechanism_info)"); })
.def("__str__",
[](const arb::mechanism_info& inf) {
return util::pprintf("(arbor.mechanism_info)"); });
pybind11::class_<arb::mechanism_catalogue> cat(m, "catalogue");
cat
.def(pybind11::init<const arb::mechanism_catalogue&>())
.def("has", &arb::mechanism_catalogue::has,
"name"_a, "Is 'name' in the catalogue?")
.def("keys", &arb::mechanism_catalogue::mechanism_names,
"Return a list of all mechanisms in this catalogues.")
.def("is_derived", &arb::mechanism_catalogue::is_derived,
"name"_a, "Is 'name' a derived mechanism or can it be implicitly derived?")
.def("__getitem__",
[](arb::mechanism_catalogue& c, const char* name) {
try {
return c[name];
}
catch (...) {
throw std::runtime_error(util::pprintf("\nKeyError: '{}'", name));
}
})
.def("extend", &arb::mechanism_catalogue::import,
"other"_a, "Catalogue to import into self",
"prefix"_a, "Prefix for names in other",
"Import another catalogue, possibly with a prefix. Will overwrite in case of name collisions.")
.def("derive", &apply_derive,
"name"_a, "parent"_a,
"globals"_a=std::unordered_map<std::string, double>{},
"ions"_a=std::unordered_map<std::string, std::string>{})
.def("__repr__",
[](const arb::mechanism_catalogue& cat) {
return util::pprintf("<arbor.mechanism_catalogue>"); })
.def("__str__",
[](const arb::mechanism_catalogue& cat) {
return util::pprintf("<arbor.mechanism_catalogue>"); });
m.def("default_catalogue", [](){return arb::global_default_catalogue();});
m.def("allen_catalogue", [](){return arb::global_allen_catalogue();});
m.def("bbp_catalogue", [](){return arb::global_bbp_catalogue();});
// arb::mechanism_desc
// For specifying a mechanism in the cable_cell interface.
pybind11::class_<arb::mechanism_desc> mechanism_desc(m, "mechanism");
mechanism_desc
.def(pybind11::init([](const char* n) {return arb::mechanism_desc{n};}))
// allow construction of a description with parameters provided in a dictionary:
// mech = arbor.mechanism('mech_name', {'param1': 1.2, 'param2': 3.14})
.def(pybind11::init(
[](const char* name, std::unordered_map<std::string, double> params) {
arb::mechanism_desc md(name);
for (const auto& p: params) md.set(p.first, p.second);
return md;
}),
"name"_a, "The name of the mechanism",
"params"_a, "A dictionary of parameter values, with parameter name as key.",
"Example usage setting parameters:\n"
" m = arbor.mechanism('expsyn', {'tau': 1.4})\n"
"will create parameters for the 'expsyn' mechanism, with the provided value\n"
"for 'tau' overrides the default. If a parameter is not set, the default\n"
"(as defined in NMODL) is used.\n\n"
"Example overriding a global parameter:\n"
" m = arbor.mechanism('nernst/R=8.3145,F=96485')")
.def("set",
[](arb::mechanism_desc& md, std::string name, double value) {
md.set(name, value);
},
"name"_a, "value"_a, "Set parameter value.")
.def_property_readonly("name",
[](const arb::mechanism_desc& md) {
return md.name();
},
"The name of the mechanism.")
.def_property_readonly("values",
[](const arb::mechanism_desc& md) {
return md.values();
}, "A dictionary of parameter values with parameter name as key.")
.def("__repr__",
[](const arb::mechanism_desc& md) {
return util::pprintf("<arbor.mechanism: name '{}', parameters {}", md.name(), util::dictionary_csv(md.values())); })
.def("__str__",
[](const arb::mechanism_desc& md) {
return util::pprintf("('{}' {})", md.name(), util::dictionary_csv(md.values())); });
}
} // namespace pyarb