diff --git a/arbor/backends/gpu/mechanism.hpp b/arbor/backends/gpu/mechanism.hpp index 98e70ce382b99d94942dcc9cc705d92dd2add6e5..94144bf37ade1b0746229c58fbea858c327a7212 100644 --- a/arbor/backends/gpu/mechanism.hpp +++ b/arbor/backends/gpu/mechanism.hpp @@ -99,10 +99,10 @@ protected: using field_default_entry = std::pair<const char*, value_type>; using mechanism_field_default_table = std::vector<field_default_entry>; - using ion_state_entry = std::pair<ionKind, ion_state_view*>; + using ion_state_entry = std::pair<const char*, ion_state_view*>; using mechanism_ion_state_table = std::vector<ion_state_entry>; - using ion_index_entry = std::pair<ionKind, const index_type**>; + using ion_index_entry = std::pair<const char*, const index_type**>; using mechanism_ion_index_table = std::vector<ion_index_entry>; virtual void nrn_init() = 0; diff --git a/arbor/backends/gpu/shared_state.cpp b/arbor/backends/gpu/shared_state.cpp index 1f8c7331f32539d258a833fdd2ddb6dffd3e313d..149f0bd5034740d6ae493d103eb9918d4102896b 100644 --- a/arbor/backends/gpu/shared_state.cpp +++ b/arbor/backends/gpu/shared_state.cpp @@ -3,7 +3,7 @@ #include <arbor/constants.hpp> #include <arbor/fvm_types.hpp> -#include <arbor/ion.hpp> +#include <arbor/ion_info.hpp> #include "backends/event.hpp" #include "backends/gpu/gpu_store_types.hpp" @@ -132,13 +132,14 @@ shared_state::shared_state( {} void shared_state::add_ion( + const std::string& ion_name, ion_info info, const std::vector<fvm_index_type>& cv, const std::vector<fvm_value_type>& iconc_norm_area, const std::vector<fvm_value_type>& econc_norm_area) { ion_data.emplace(std::piecewise_construct, - std::forward_as_tuple(info.kind), + std::forward_as_tuple(ion_name), std::forward_as_tuple(info, cv, iconc_norm_area, econc_norm_area, 1u)); } @@ -210,7 +211,7 @@ std::ostream& operator<<(std::ostream& o, shared_state& s) { o << " current " << s.current_density << "\n"; o << " conductivity " << s.conductivity << "\n"; for (auto& ki: s.ion_data) { - auto kn = to_string(ki.first); + auto& kn = ki.first; auto& i = const_cast<ion_state&>(ki.second); o << " " << kn << ".current_density " << i.iX_ << "\n"; o << " " << kn << ".reversal_potential " << i.eX_ << "\n"; diff --git a/arbor/backends/gpu/shared_state.hpp b/arbor/backends/gpu/shared_state.hpp index fe66272003f7bd89192ef689b60e3f652062c290..27174810ba3c5f94183e4647020bc7b41bb8e0b2 100644 --- a/arbor/backends/gpu/shared_state.hpp +++ b/arbor/backends/gpu/shared_state.hpp @@ -6,7 +6,7 @@ #include <vector> #include <arbor/fvm_types.hpp> -#include <arbor/ion.hpp> +#include <arbor/ion_info.hpp> #include "backends/gpu/gpu_store_types.hpp" @@ -80,7 +80,7 @@ struct shared_state { array conductivity; // Maps CV index to membrane conductivity [kS/m²]. array temperature_degC; // Global temperature [°C] (length 1 array). - std::unordered_map<ionKind, ion_state> ion_data; + std::unordered_map<std::string, ion_state> ion_data; deliverable_event_stream deliverable_events; @@ -94,6 +94,7 @@ struct shared_state { ); void add_ion( + const std::string& ion_name, ion_info info, const std::vector<fvm_index_type>& cv, const std::vector<fvm_value_type>& iconc_norm_area, diff --git a/arbor/backends/multicore/mechanism.hpp b/arbor/backends/multicore/mechanism.hpp index f3819ab1f957ac3012b3762d7aec8494b73b89f2..8054c6568023a76dbbe615130626621e7a9a55a2 100644 --- a/arbor/backends/multicore/mechanism.hpp +++ b/arbor/backends/multicore/mechanism.hpp @@ -112,10 +112,10 @@ protected: using field_default_entry = std::pair<const char*, value_type>; using mechanism_field_default_table = std::vector<field_default_entry>; - using ion_state_entry = std::pair<ionKind, ion_state_view*>; + using ion_state_entry = std::pair<const char*, ion_state_view*>; using mechanism_ion_state_table = std::vector<ion_state_entry>; - using ion_index_entry = std::pair<ionKind, iarray*>; + using ion_index_entry = std::pair<const char*, iarray*>; using mechanism_ion_index_table = std::vector<ion_index_entry>; virtual void nrn_init() = 0; diff --git a/arbor/backends/multicore/shared_state.cpp b/arbor/backends/multicore/shared_state.cpp index 6af8064df4a8b249dbdc4f607ac1462c808038b5..c2917d2fd2ce867233ba8d8c286918ae4fc86cb2 100644 --- a/arbor/backends/multicore/shared_state.cpp +++ b/arbor/backends/multicore/shared_state.cpp @@ -10,7 +10,7 @@ #include <arbor/common_types.hpp> #include <arbor/constants.hpp> #include <arbor/fvm_types.hpp> -#include <arbor/ion.hpp> +#include <arbor/ion_info.hpp> #include <arbor/math.hpp> #include <arbor/simd/simd.hpp> @@ -149,13 +149,14 @@ shared_state::shared_state( } void shared_state::add_ion( + const std::string& ion_name, ion_info info, const std::vector<fvm_index_type>& cv, const std::vector<fvm_value_type>& iconc_norm_area, const std::vector<fvm_value_type>& econc_norm_area) { ion_data.emplace(std::piecewise_construct, - std::forward_as_tuple(info.kind), + std::forward_as_tuple(ion_name), std::forward_as_tuple(info, cv, iconc_norm_area, econc_norm_area, alignment)); } @@ -266,8 +267,8 @@ std::ostream& operator<<(std::ostream& out, const shared_state& s) { out << "voltage " << csv(s.voltage) << "\n"; out << "current " << csv(s.current_density) << "\n"; out << "conductivity " << csv(s.conductivity) << "\n"; - for (auto& ki: s.ion_data) { - auto kn = to_string(ki.first); + for (const auto& ki: s.ion_data) { + auto& kn = ki.first; auto& i = const_cast<ion_state&>(ki.second); out << kn << ".current_density " << csv(i.iX_) << "\n"; out << kn << ".reversal_potential " << csv(i.eX_) << "\n"; diff --git a/arbor/backends/multicore/shared_state.hpp b/arbor/backends/multicore/shared_state.hpp index be3b5725004eef768c594fc6d66626b373d94cef..bedfaf57366313ccaa25acd925b975b22ae24f4f 100644 --- a/arbor/backends/multicore/shared_state.hpp +++ b/arbor/backends/multicore/shared_state.hpp @@ -10,7 +10,7 @@ #include <arbor/assert.hpp> #include <arbor/common_types.hpp> #include <arbor/fvm_types.hpp> -#include <arbor/ion.hpp> +#include <arbor/ion_info.hpp> #include <arbor/simd/simd.hpp> #include "backends/event.hpp" @@ -42,15 +42,15 @@ namespace multicore { struct ion_state { unsigned alignment = 1; // Alignment and padding multiple. - iarray node_index_; // Instance to CV map. - array iX_; // (nA) current - array eX_; // (mV) reversal potential - array Xi_; // (mM) internal concentration - array Xo_; // (mM) external concentration - array weight_Xi_; // (1) concentration weight internal - array weight_Xo_; // (1) concentration weight external + iarray node_index_; // Instance to CV map. + array iX_; // (nA) current + array eX_; // (mV) reversal potential + array Xi_; // (mM) internal concentration + array Xo_; // (mM) external concentration + array weight_Xi_; // (1) concentration weight internal + array weight_Xo_; // (1) concentration weight external - int charge; // charge of ionic species + int charge; // charge of ionic species fvm_value_type default_int_concentration; // (mM) default internal concentration fvm_value_type default_ext_concentration; // (mM) default external concentration @@ -99,7 +99,7 @@ struct shared_state { array conductivity; // Maps CV index to membrane conductivity [kS/m²]. fvm_value_type temperature_degC; // Global temperature [°C]. - std::unordered_map<ionKind, ion_state> ion_data; + std::unordered_map<std::string, ion_state> ion_data; deliverable_event_stream deliverable_events; @@ -113,6 +113,7 @@ struct shared_state { ); void add_ion( + const std::string& ion_name, ion_info info, const std::vector<fvm_index_type>& cv, const std::vector<fvm_value_type>& iconc_norm_area, diff --git a/arbor/fvm_layout.cpp b/arbor/fvm_layout.cpp index 1612734baf6d208745bada5e1a0488dcc7149c49..144aaf5b6e1a62a0a33c800947e046e94350a933 100644 --- a/arbor/fvm_layout.cpp +++ b/arbor/fvm_layout.cpp @@ -306,10 +306,10 @@ fvm_mechanism_data fvm_build_mechanism_data(const mechanism_catalogue& catalogue // Record for each stimulus the CV and clamp data. std::vector<std::pair<size_type, i_clamp>> stimuli; - // Temporary table for presence of ion channels, mapping ionKind to _sorted_ + // Temporary table for presence of ion channels, mapping ion name to _sorted_ // collection of segment indices. - std::unordered_map<ionKind, std::set<size_type>> ion_segments; + std::unordered_map<std::string, std::set<size_type>> ion_segments; auto update_paramset_and_validate = [&catalogue] diff --git a/arbor/fvm_layout.hpp b/arbor/fvm_layout.hpp index 459fb5136e46ec2540355c6b8c9ab56a7198d16b..e470a7190c1a776c89607a7122c5fb1c0d3d5d21 100644 --- a/arbor/fvm_layout.hpp +++ b/arbor/fvm_layout.hpp @@ -133,8 +133,8 @@ struct fvm_mechanism_data { // Mechanism config, indexed by mechanism name. std::unordered_map<std::string, fvm_mechanism_config> mechanisms; - // Ion config, indexed by ionKind. - std::unordered_map<ionKind, fvm_ion_config> ions; + // Ion config, indexed by ion name. + std::unordered_map<std::string, fvm_ion_config> ions; // Total number of targets (point-mechanism points) std::size_t ntarget = 0; diff --git a/arbor/fvm_lowered_cell_impl.hpp b/arbor/fvm_lowered_cell_impl.hpp index c3e1d8da4f36f2e15cfa0de55322f0651717fc84..2e5574fc271aedbbdb4e708c5566791fa2d851d3 100644 --- a/arbor/fvm_lowered_cell_impl.hpp +++ b/arbor/fvm_lowered_cell_impl.hpp @@ -18,7 +18,6 @@ #include <arbor/assert.hpp> #include <arbor/common_types.hpp> -#include <arbor/ion.hpp> #include <arbor/recipe.hpp> #include "builtin_mechanisms.hpp" @@ -404,10 +403,13 @@ void fvm_lowered_cell_impl<B>::initialize( // Instantiate mechanisms and ions. for (auto& i: mech_data.ions) { - ionKind kind = i.first; + const std::string& ion_name = i.first; - if (auto ion = value_by_key(global_props.ion_default, to_string(kind))) { - state_->add_ion(ion.value(), i.second.cv, i.second.iconc_norm_area, i.second.econc_norm_area); + if (auto ion = value_by_key(global_props.ion_default, ion_name)) { + state_->add_ion(ion_name, ion.value(), i.second.cv, i.second.iconc_norm_area, i.second.econc_norm_area); + } + else { + throw cable_cell_error("unrecognized ion '"+ion_name+"' in mechanism"); } } diff --git a/arbor/include/arbor/cable_cell.hpp b/arbor/include/arbor/cable_cell.hpp index dd865de8cb4b0c90ac570c1079b2570f6c0024f8..14be4c2392c863126419f42b372cc60adf23843d 100644 --- a/arbor/include/arbor/cable_cell.hpp +++ b/arbor/include/arbor/cable_cell.hpp @@ -7,7 +7,7 @@ #include <arbor/arbexcept.hpp> #include <arbor/common_types.hpp> #include <arbor/constants.hpp> -#include <arbor/ion.hpp> +#include <arbor/ion_info.hpp> #include <arbor/mechcat.hpp> #include <arbor/morphology.hpp> #include <arbor/segment.hpp> @@ -81,11 +81,10 @@ struct cable_cell_global_properties { // // Defaults below chosen to match NEURON. - // Ion species currently limited to just "ca", "na", "k". std::unordered_map<std::string, ion_info> ion_default = { - {"ca", { ionKind::ca, 2, 5e-5, 2. }}, - {"na", { ionKind::na, 1, 10., 140.}}, - {"k", { ionKind::k, 1, 54.4, 2.5 }} + {"ca", { 2, 5e-5, 2. }}, + {"na", { 1, 10., 140.}}, + {"k", { 1, 54.4, 2.5 }} }; double temperature_K = constant::hh_squid_temp; // [K] diff --git a/arbor/include/arbor/ion.hpp b/arbor/include/arbor/ion.hpp deleted file mode 100644 index b0bd0c3c59591e14f2011a13d21dcf506caece33..0000000000000000000000000000000000000000 --- a/arbor/include/arbor/ion.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include <stdexcept> -#include <string> - -namespace arb { - -// Fixed set of ion species (to be generalized in the future): - -enum class ionKind {ca, na, k}; -inline std::string to_string(ionKind k) { - switch (k) { - case ionKind::ca: return "ca"; - case ionKind::na: return "na"; - case ionKind::k: return "k"; - default: throw std::out_of_range("unknown ionKind"); - } -} - -// Ion (species) description - -struct ion_info { - ionKind kind; - int charge; // charge of ionic species - double default_int_concentration; // (mM) default internal concentration - double default_ext_concentration; // (mM) default external concentration -}; - -} // namespace arb - diff --git a/arbor/include/arbor/ion_info.hpp b/arbor/include/arbor/ion_info.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a3e97263b091bc592cfdc7decc375e6fd4a24cf5 --- /dev/null +++ b/arbor/include/arbor/ion_info.hpp @@ -0,0 +1,11 @@ +#pragma once + +namespace arb { + +struct ion_info { + int charge; // charge of ionic species + double default_int_concentration; // (mM) default internal concentration + double default_ext_concentration; // (mM) default external concentration +}; + +} // namespace arb diff --git a/arbor/include/arbor/mechanism.hpp b/arbor/include/arbor/mechanism.hpp index 8e17ad66bc51c1b71f3dfd053857da7c6012c2c3..0fe32db4591920121f49d3e292e1cd1e4fbc1286 100644 --- a/arbor/include/arbor/mechanism.hpp +++ b/arbor/include/arbor/mechanism.hpp @@ -5,7 +5,6 @@ #include <vector> #include <arbor/fvm_types.hpp> -#include <arbor/ion.hpp> #include <arbor/mechinfo.hpp> namespace arb { diff --git a/arbor/include/arbor/mechinfo.hpp b/arbor/include/arbor/mechinfo.hpp index dce3c70dc24bd5b732f2ef251fa8417db9f49fdd..5e23e2ea38b5f9b6c8f7ebaf96ccbd0b82e19256 100644 --- a/arbor/include/arbor/mechinfo.hpp +++ b/arbor/include/arbor/mechinfo.hpp @@ -10,8 +10,6 @@ #include <utility> #include <vector> -#include <arbor/ion.hpp> - namespace arb { struct mechanism_field_spec { @@ -34,6 +32,7 @@ struct mechanism_field_spec { struct ion_dependency { bool write_concentration_int; bool write_concentration_ext; + bool write_reversal_potential; }; // A hash of the mechanism dynamics description is used to ensure that offline-compiled @@ -57,7 +56,7 @@ struct mechanism_info { std::unordered_map<std::string, mechanism_field_spec> state; // Ion dependencies. - std::unordered_map<ionKind, ion_dependency> ions; + std::unordered_map<std::string, ion_dependency> ions; mechanism_fingerprint fingerprint; diff --git a/modcc/blocks.hpp b/modcc/blocks.hpp index 868f60bd601ba914d8f86f07e6294bd353d12b19..118077dfbbee7e694c0069ccf07683da881d96a8 100644 --- a/modcc/blocks.hpp +++ b/modcc/blocks.hpp @@ -11,9 +11,6 @@ // describes a relationship with an ion channel struct IonDep { - ionKind kind() const { - return to_ionKind(name); - } std::string name; // name of ion channel std::vector<Token> read; // name of channels parameters to write std::vector<Token> write; // name of channels parameters to read @@ -40,6 +37,9 @@ struct IonDep { bool writes_concentration_ext() const { return writes_variable(name+"o"); }; + bool writes_rev_potential() const { + return writes_variable("e"+name); + }; bool reads_variable(const std::string& name) const { return std::find_if(read.begin(), read.end(), diff --git a/modcc/expression.cpp b/modcc/expression.cpp index 960339f80043842f6643de94c6e563e5679047cf..2d4accd191e66a60307dbe13f6fcf4872c6bc3a9 100644 --- a/modcc/expression.cpp +++ b/modcc/expression.cpp @@ -265,8 +265,8 @@ std::string VariableExpression::to_string() const { + colorize("write", is_writeable() ? stringColor::green : stringColor::red) + ", " + colorize("read", is_readable() ? stringColor::green : stringColor::red) + ", " + (is_range() ? "range" : "scalar") + ", " - + "ion" + colorize(::to_string(ion_channel()), - (ion_channel()==ionKind::none) ? stringColor::red : stringColor::green) + ", " + + "ion" + (is_ion()? colorize(ion_channel(), stringColor::green) + : colorize("none", stringColor::red)) + ", " + "vis " + ::to_string(visibility()) + ", " + "link " + ::to_string(linkage()) + ", " + colorize("state", is_state() ? stringColor::green : stringColor::red) + ")"; @@ -278,11 +278,11 @@ std::string VariableExpression::to_string() const { *******************************************************************************/ std::string IndexedVariable::to_string() const { - auto ch = ::to_string(ion_channel()); return blue("indexed") + " " + yellow(name()) + "->" + yellow(index_name()) + "(" + (is_write() ? " write-only" : " read-only") - + ", ion" + (ion_channel()==ionKind::none ? red(ch) : green(ch)) + ") "; + + ", ion" + (is_ion()? colorize(ion_channel(), stringColor::green) + : colorize("none", stringColor::red)) + ") "; } /******************************************************************************* diff --git a/modcc/expression.hpp b/modcc/expression.hpp index 12b9fc5f174aede12fbe6c67f59c1f235db044c7..2157d3965a1e66c3df53f3cf94bbfcd9082004fa 100644 --- a/modcc/expression.hpp +++ b/modcc/expression.hpp @@ -9,8 +9,8 @@ #include "error.hpp" #include "identifier.hpp" -#include "memop.hpp" #include "scope.hpp" +#include "token.hpp" #include "io/pprintf.hpp" @@ -455,8 +455,8 @@ public: void range(rangeKind r) { range_kind_ = r; } - void ion_channel(ionKind i) { - ion_channel_ = i; + void ion_channel(std::string i) { + ion_channel_ = std::move(i); } void state(bool s) { is_state_ = s; @@ -474,7 +474,7 @@ public: linkageKind linkage() const { return linkage_; } - ionKind ion_channel() const { + const std::string& ion_channel() const { return ion_channel_; } @@ -482,7 +482,7 @@ public: return shadows_; } - bool is_ion() const {return ion_channel_ != ionKind::none;} + bool is_ion() const {return !ion_channel_.empty();} bool is_state() const {return is_state_;} bool is_range() const {return range_kind_ == rangeKind::range;} bool is_scalar() const {return !is_range();} @@ -507,7 +507,7 @@ protected: visibilityKind visibility_ = visibilityKind::local; linkageKind linkage_ = linkageKind::external; rangeKind range_kind_ = rangeKind::range; - ionKind ion_channel_ = ionKind::none; + std::string ion_channel_ = ""; double value_ = std::numeric_limits<double>::quiet_NaN(); Symbol* shadows_ = nullptr; }; @@ -521,11 +521,11 @@ public: sourceKind data_source, accessKind acc, tok o=tok::eq, - ionKind channel=ionKind::none) + std::string channel="") : Symbol(std::move(loc), std::move(lookup_name), symbolKind::indexed_variable), access_(acc), - ion_channel_(channel), - index_name_(index_name), // (TODO: deprecate/remove this...) + ion_channel_(std::move(channel)), + index_name_(std::move(index_name)), // (TODO: deprecate/remove this...) data_source_(data_source), op_(o) { @@ -558,12 +558,13 @@ public: std::string to_string() const override; accessKind access() const { return access_; } - ionKind ion_channel() const { return ion_channel_; } + std::string ion_channel() const { return ion_channel_; } sourceKind data_source() const { return data_source_; } + void data_source(sourceKind k) { data_source_ = k; } std::string const& index_name() const { return index_name_; } tok op() const { return op_; } - bool is_ion() const { return ion_channel_ != ionKind::none; } + bool is_ion() const { return !ion_channel_.empty(); } bool is_read() const { return access_ == accessKind::read; } bool is_write() const { return access_ == accessKind::write; } @@ -573,7 +574,7 @@ public: ~IndexedVariable() {} protected: accessKind access_; - ionKind ion_channel_; + std::string ion_channel_; std::string index_name_; // hint to printer only sourceKind data_source_; tok op_; @@ -597,12 +598,11 @@ public : } bool is_indexed() const { - return external_!=nullptr && ion_channel()!=ionKind::nonspecific; + return external_!=nullptr && external_->data_source()!=sourceKind::no_source; } - ionKind ion_channel() const { - if(external_) return external_->ion_channel(); - return ionKind::none; + std::string ion_channel() const { + return external_? external_->ion_channel(): ""; } bool is_read() const { @@ -695,20 +695,20 @@ public: ConductanceExpression( Location loc, std::string name, - ionKind channel) - : Expression(loc), name_(std::move(name)), ion_channel_(channel) + std::string channel) + : Expression(loc), name_(std::move(name)), ion_channel_(std::move(channel)) {} std::string to_string() const override { return blue("conductance") + "(" + yellow(name_) + ", " - + green(::to_string(ion_channel_)) + ")"; + + green(ion_channel_.empty()? "none": ion_channel_) + ")"; } std::string const& name() const { return name_; } - ionKind ion_channel() const { + std::string const& ion_channel() const { return ion_channel_; } @@ -725,7 +725,7 @@ public: private: /// pointer to the variable symbol for the state variable to be solved for std::string name_; - ionKind ion_channel_; + std::string ion_channel_; }; //////////////////////////////////////////////////////////////////////////////// @@ -1041,8 +1041,6 @@ protected: class APIMethod : public ProcedureExpression { public: - using memop_type = MemOp<Symbol>; - APIMethod( Location loc, std::string name, std::vector<expression_ptr>&& args, diff --git a/modcc/identifier.hpp b/modcc/identifier.hpp index 891e6f49aec8de91f018ce79ed8a328049585d7f..58aea5691066793ece8e079584751dccdb290861 100644 --- a/modcc/identifier.hpp +++ b/modcc/identifier.hpp @@ -33,15 +33,6 @@ enum class linkageKind { external }; -/// ion channel that the variable belongs to -enum class ionKind { - none, ///< not an ion variable - nonspecific, ///< nonspecific current - Ca, ///< calcium ion - Na, ///< sodium ion - K ///< potassium ion -}; - /// possible external data source for indexed variables enum class sourceKind { voltage, @@ -64,25 +55,6 @@ inline std::string yesno(bool val) { // to_string functions convert types // to strings for printing diagnostics //////////////////////////////////////////// -inline std::string to_string(ionKind i) { - switch(i) { - case ionKind::Ca : return std::string("ca"); - case ionKind::Na : return std::string("na"); - case ionKind::K : return std::string("k"); - case ionKind::none : return std::string("none"); - case ionKind::nonspecific : return std::string("nonspecific"); - } - throw std::runtime_error("unknown ionKind"); -} - -inline ionKind to_ionKind(const std::string& s) { - if(s=="k") return ionKind::K; - if(s=="na") return ionKind::Na; - if(s=="ca") return ionKind::Ca; - if(s=="none") return ionKind::Ca; - if(s=="nonspecific") return ionKind::nonspecific; - throw std::runtime_error("invalid ion description string"); -} inline std::string to_string(visibilityKind v) { switch(v) { @@ -101,9 +73,6 @@ inline std::string to_string(linkageKind v) { } // ostream writers -inline std::ostream& operator<< (std::ostream& os, ionKind i) { - return os << to_string(i); -} inline std::ostream& operator<< (std::ostream& os, visibilityKind v) { return os << to_string(v); @@ -115,25 +84,12 @@ inline std::ostream& operator<< (std::ostream& os, linkageKind l) { /// ion variable to data source kind -inline sourceKind ion_source(ionKind i, const std::string& var) { - std::string ion = to_string(i); - if (var=="i"+ion) return sourceKind::ion_current; +inline sourceKind ion_source(const std::string& ion, const std::string& var) { + if (ion.empty()) return sourceKind::no_source; + else if (var=="i"+ion) return sourceKind::ion_current; else if (var=="e"+ion) return sourceKind::ion_revpot; else if (var==ion+"i") return sourceKind::ion_iconc; else if (var==ion+"e") return sourceKind::ion_econc; else return sourceKind::no_source; } -// TODO: deprecate; back-end dependent. -inline std::string ion_store(ionKind k) { - switch(k) { - case ionKind::Ca: - return "ion_ca"; - case ionKind::Na: - return "ion_na"; - case ionKind::K: - return "ion_k"; - default: - return ""; - } -} diff --git a/modcc/module.cpp b/modcc/module.cpp index 97debcad3542b94e58fb3732bbb40f33d203f877..3c86d0decd51364b5c45e40449f875f45467723c 100644 --- a/modcc/module.cpp +++ b/modcc/module.cpp @@ -26,15 +26,20 @@ class NrnCurrentRewriter: public BlockRewriterBase { return id(name, loc_); } - static ionKind is_ion_update(Expression* e) { + static sourceKind current_update(Expression* e) { if(auto a = e->is_assignment()) { if(auto sym = a->lhs()->is_identifier()->symbol()) { if(auto var = sym->is_local_variable()) { - return var->ion_channel(); + if(auto ext = var->external_variable()) { + sourceKind src = ext->data_source(); + if (src==sourceKind::current || src==sourceKind::ion_current) { + return src; + } + } } } } - return ionKind::none; + return sourceKind::no_source; } bool has_current_update_ = false; @@ -81,12 +86,20 @@ public: statements_.push_back(e->clone()); auto loc = e->location(); - auto update_kind = is_ion_update(e); - if (update_kind!=ionKind::none) { - if (update_kind!=ionKind::nonspecific) { + sourceKind current_source = current_update(e); + if (current_source != sourceKind::no_source) { + has_current_update_ = true; + + if (current_source==sourceKind::ion_current) { ion_current_vars_.insert(e->lhs()->is_identifier()->name()); } - has_current_update_ = true; + else { + // A 'nonspecific' current contribution. + // Remove data source; currents accumulated into `current_` instead. + + e->lhs()->is_identifier()->symbol()->is_local_variable() + ->external_variable()->data_source(sourceKind::no_source); + } linear_test_result L = linear_test(e->rhs(), {"v"}); if (!L.is_linear) { @@ -497,7 +510,7 @@ void Module::add_variables_to_symbols() { // add indexed variables to the table auto create_indexed_variable = [this] (std::string const& name, std::string const& indexed_name, sourceKind data_source, - tok op, accessKind acc, ionKind ch, Location loc) -> symbol_ptr& + tok op, accessKind acc, std::string ch, Location loc) -> symbol_ptr& { if(symbols_.count(name)) { throw compiler_exception( @@ -508,13 +521,13 @@ void Module::add_variables_to_symbols() { }; create_indexed_variable("current_", "vec_i", sourceKind::current, tok::plus, - accessKind::write, ionKind::none, Location()); + accessKind::write, "", Location()); create_indexed_variable("conductivity_", "vec_g", sourceKind::conductivity, tok::plus, - accessKind::write, ionKind::none, Location()); + accessKind::write, "", Location()); create_indexed_variable("v", "vec_v", sourceKind::voltage, tok::eq, - accessKind::read, ionKind::none, Location()); + accessKind::read, "", Location()); create_indexed_variable("dt", "vec_dt", sourceKind::dt, tok::eq, - accessKind::read, ionKind::none, Location()); + accessKind::read, "", Location()); // If we put back support for accessing cell time again from NMODL code, // add indexed_variable also for "time" with appropriate cell-index based @@ -537,7 +550,7 @@ void Module::add_variables_to_symbols() { if (id.name() == "celsius") { create_indexed_variable("celsius", "celsius", - sourceKind::temperature, tok::eq, accessKind::read, ionKind::none, Location()); + sourceKind::temperature, tok::eq, accessKind::read, "", Location()); } else { // Parameters are scalar by default, but may later be changed to range. @@ -575,7 +588,7 @@ void Module::add_variables_to_symbols() { // first the ION channels // add ion channel variables auto update_ion_symbols = [this, create_indexed_variable] - (Token const& tkn, accessKind acc, ionKind channel) + (Token const& tkn, accessKind acc, const std::string& channel) { std::string name = tkn.spelling; sourceKind data_source = ion_source(channel, name); @@ -610,18 +623,23 @@ void Module::add_variables_to_symbols() { } }; - // check for nonspecific current + // Nonspecific current variables are represented by an indexed variable + // with a 'current' data source. Assignments in the NrnCurrent block will + // later be rewritten so that these contributions are accumulated in `current_` + // (potentially saving some weight multiplications); at that point the + // data source for the nonspecific current variable will be reset to 'no_source'. + if( neuron_block_.has_nonspecific_current() ) { auto const& i = neuron_block_.nonspecific_current; - update_ion_symbols(i, accessKind::write, ionKind::nonspecific); + create_indexed_variable(i.spelling, "", sourceKind::current, tok::plus, accessKind::write, "", i.location); } for(auto const& ion : neuron_block_.ions) { for(auto const& var : ion.read) { - update_ion_symbols(var, accessKind::read, ion.kind()); + update_ion_symbols(var, accessKind::read, ion.name); } for(auto const& var : ion.write) { - update_ion_symbols(var, accessKind::write, ion.kind()); + update_ion_symbols(var, accessKind::write, ion.name); } } diff --git a/modcc/module.hpp b/modcc/module.hpp index dbe8c1ffcb6008bd0bcd26b48b103070fbece99e..4cf5e6ac5a1a0b8727a0c09042e5d1c9f8e631ff 100644 --- a/modcc/module.hpp +++ b/modcc/module.hpp @@ -91,16 +91,16 @@ public: // Perform semantic analysis pass. bool semantic(); - auto find_ion(ionKind k) -> decltype(ion_deps().begin()) { + auto find_ion(const std::string& ion_name) -> decltype(ion_deps().begin()) { auto& ions = neuron_block().ions; return std::find_if( ions.begin(), ions.end(), - [k](IonDep const& d) {return d.kind()==k;} + [&ion_name](IonDep const& d) {return d.name==ion_name;} ); }; - bool has_ion(ionKind k) { - return find_ion(k) != neuron_block().ions.end(); + bool has_ion(const std::string& ion_name) { + return find_ion(ion_name) != neuron_block().ions.end(); }; bool is_linear() const { return linear_; } diff --git a/modcc/parser.cpp b/modcc/parser.cpp index 1598711a9c919a37b8d8b74e33292e5af82e9a40..d2fd3a021a18bea25d967ea7fedfea8c90069ba8 100644 --- a/modcc/parser.cpp +++ b/modcc/parser.cpp @@ -304,16 +304,10 @@ void Parser::parse_neuron_block() { get_token(); // check this is an identifier token if(token_.type != tok::identifier) { - error(pprintf("invalid name for an ion chanel '%'", - token_.spelling)); - return; - } - // check that the ion type is valid (insist on lower case?) - if(!(token_.spelling == "k" || token_.spelling == "ca" || token_.spelling == "na")) { - error(pprintf("invalid ion type % must be on eof 'k' 'ca' or 'na'", - yellow(token_.spelling))); + error(pprintf("invalid name for an ion chanel '%'", token_.spelling)); return; } + ion.name = token_.spelling; get_token(); // consume the ion name @@ -1464,7 +1458,7 @@ expression_ptr Parser::parse_conductance() { int line = location_.line; Location loc = location_; // solve location for expression std::string name; - ionKind channel; + std::string channel; get_token(); // consume the CONDUCTANCE keyword @@ -1473,20 +1467,11 @@ expression_ptr Parser::parse_conductance() { name = token_.spelling; // save name of variable get_token(); // consume the variable identifier - if(token_.type != tok::useion) { // no ion channel was provided - // we set nonspecific not none because ionKind::none marks - // any variable that is not associated with an ion channel - channel = ionKind::nonspecific; - } - else { + if(token_.type == tok::useion) { get_token(); // consume the USEION keyword if(token_.type!=tok::identifier) goto conductance_statement_error; - if (token_.spelling == "na") channel = ionKind::Na; - else if(token_.spelling == "ca") channel = ionKind::Ca; - else if(token_.spelling == "k") channel = ionKind::K; - else goto conductance_statement_error; - + channel = token_.spelling; get_token(); // consume the ion channel type } // check that the rest of the line was empty diff --git a/modcc/printer/cprinter.cpp b/modcc/printer/cprinter.cpp index 1c1ce229350cefccddefc94de01ea3b92c945c95..5560ff22e819dab5c081f9b4c44963c0bbac49e1 100644 --- a/modcc/printer/cprinter.cpp +++ b/modcc/printer/cprinter.cpp @@ -216,7 +216,6 @@ std::string emit_cpp_source(const Module& module_, const printer_options& opt) { out << "\n" << popindent << "protected:\n" << indent << - "using ionKind = ::arb::ionKind;\n\n" "std::size_t object_sizeof() const override { return sizeof(*this); }\n"; io::separator sep("\n", ",\n"); @@ -280,14 +279,14 @@ std::string emit_cpp_source(const Module& module_, const printer_options& opt) { sep.reset(); for (const auto& dep: ion_deps) { - out << sep << "{ionKind::" << dep.name << ", &" << ion_state_field(dep.name) << "}"; + out << sep << "{\"" << dep.name << "\", &" << ion_state_field(dep.name) << "}"; } out << popindent << "\n};" << popindent << "\n}\n"; sep.reset(); out << "mechanism_ion_index_table ion_index_table() override {\n" << indent << "return {" << indent; for (const auto& dep: ion_deps) { - out << sep << "{ionKind::" << dep.name << ", &" << ion_state_index(dep.name) << "}"; + out << sep << "{\"" << dep.name << "\", &" << ion_state_index(dep.name) << "}"; } out << popindent << "\n};" << popindent << "\n}\n"; } diff --git a/modcc/printer/cudaprinter.cpp b/modcc/printer/cudaprinter.cpp index e84200f2c63e7a09fd58101cae60335dbc1e018f..91f69c03b6801ad07cfd36abf6b82e8adc2eaec2 100644 --- a/modcc/printer/cudaprinter.cpp +++ b/modcc/printer/cudaprinter.cpp @@ -40,11 +40,11 @@ std::string make_ppack_name(const std::string& module_name) { return make_class_name(module_name)+"_pp_"; } -static std::string ion_state_field(std::string ion_name) { +static std::string ion_state_field(const std::string& ion_name) { return "ion_"+ion_name+"_"; } -static std::string ion_state_index(std::string ion_name) { +static std::string ion_state_index(const std::string& ion_name) { return "ion_"+ion_name+"_index_"; } @@ -117,7 +117,6 @@ std::string emit_cuda_cpp_source(const Module& module_, const printer_options& o out << popindent << "protected:\n" << indent << - "using ionKind = ::arb::ionKind;\n\n" "std::size_t object_sizeof() const override { return sizeof(*this); }\n" "::arb::gpu::mechanism_ppack_base* ppack_ptr() { return &pp_; }\n\n"; @@ -183,14 +182,14 @@ std::string emit_cuda_cpp_source(const Module& module_, const printer_options& o sep.reset(); for (const auto& dep: ion_deps) { - out << sep << "{ionKind::" << dep.name << ", &pp_." << ion_state_field(dep.name) << "}"; + out << sep << "{\"" << dep.name << "\", &pp_." << ion_state_field(dep.name) << "}"; } out << popindent << "\n};" << popindent << "\n}\n"; sep.reset(); out << "mechanism_ion_index_table ion_index_table() override {\n" << indent << "return {" << indent; for (const auto& dep: ion_deps) { - out << sep << "{ionKind::" << dep.name << ", &pp_." << ion_state_index(dep.name) << "}"; + out << sep << "{\"" << dep.name << "\", &pp_." << ion_state_index(dep.name) << "}"; } out << popindent << "\n};" << popindent << "\n}\n"; } diff --git a/modcc/printer/infoprinter.cpp b/modcc/printer/infoprinter.cpp index 7c69918e834fb5b84b36eedbefe7481e493f4379..5e9fe0b979142ab8ccc872cadbebb314e81b926f 100644 --- a/modcc/printer/infoprinter.cpp +++ b/modcc/printer/infoprinter.cpp @@ -44,9 +44,10 @@ std::ostream& operator<<(std::ostream& out, const ion_dep_info& wrap) { const char* boolalpha[2] = {"false", "true"}; const IonDep& ion = wrap.ion; - return out << "{ionKind::" << ion.name << ", {" + return out << "{\"" << ion.name << "\", {" << boolalpha[ion.writes_concentration_int()] << ", " - << boolalpha[ion.writes_concentration_ext()] << "}}"; + << boolalpha[ion.writes_concentration_ext()] << ", " + << boolalpha[ion.writes_rev_potential()] << "}}"; } std::string build_info_header(const Module& m, const printer_options& opt) { @@ -73,7 +74,6 @@ std::string build_info_header(const Module& m, const printer_options& opt) { "\n" "inline const ::arb::mechanism_info& mechanism_" << name << "_info() {\n" << indent << - "using ::arb::ionKind;\n" "using spec = ::arb::mechanism_field_spec;\n" "static mechanism_info info = {\n" << indent << diff --git a/modcc/printer/printerutil.cpp b/modcc/printer/printerutil.cpp index f5f95cf5ce7c00a4bade28160ce8ef7fb1e57371..ca42489ca04faba6547fb3e028770f21b81ab709 100644 --- a/modcc/printer/printerutil.cpp +++ b/modcc/printer/printerutil.cpp @@ -118,7 +118,7 @@ indexed_variable_info decode_indexed_variable(IndexedVariable* sym) { std::string index_var = "node_index_"; if (sym->is_ion()) { - ion_pfx = "ion_"+to_string(sym->ion_channel())+"_"; + ion_pfx = "ion_"+sym->ion_channel()+"_"; index_var = ion_pfx+"index_"; } diff --git a/modcc/symdiff.cpp b/modcc/symdiff.cpp index f9f6a4bc16af84e827947beec7980c0fee4027a1..75a1b76a994d3924898dfa4243feed03f9d80c4a 100644 --- a/modcc/symdiff.cpp +++ b/modcc/symdiff.cpp @@ -8,6 +8,7 @@ #include "error.hpp" #include "expression.hpp" #include "symdiff.hpp" +#include "util.hpp" #include "visitor.hpp" class FindIdentifierVisitor: public Visitor { diff --git a/test/unit/test_fvm_layout.cpp b/test/unit/test_fvm_layout.cpp index 0b9496a0ced1a3a4b7aa86cffb0cb20a58877830..a4a1541fb9e0be6a8d9ac88e408e72fb021bf539 100644 --- a/test/unit/test_fvm_layout.cpp +++ b/test/unit/test_fvm_layout.cpp @@ -324,12 +324,12 @@ TEST(fvm_layout, mech_index) { // There should be a K and Na ion channel associated with each // hh mechanism node. - ASSERT_EQ(1u, M.ions.count(ionKind::na)); - ASSERT_EQ(1u, M.ions.count(ionKind::k)); - EXPECT_EQ(0u, M.ions.count(ionKind::ca)); + ASSERT_EQ(1u, M.ions.count("na"s)); + ASSERT_EQ(1u, M.ions.count("k"s)); + EXPECT_EQ(0u, M.ions.count("ca"s)); - EXPECT_EQ(ivec({0,5}), M.ions.at(ionKind::na).cv); - EXPECT_EQ(ivec({0,5}), M.ions.at(ionKind::k).cv); + EXPECT_EQ(ivec({0,5}), M.ions.at("na"s).cv); + EXPECT_EQ(ivec({0,5}), M.ions.at("k"s).cv); } TEST(fvm_layout, coalescing_synapses) { @@ -755,8 +755,8 @@ TEST(fvm_layout, ion_weights) { fvm_discretization D = fvm_discretize(cells); fvm_mechanism_data M = fvm_build_mechanism_data(global_default_catalogue(), cells, D); - ASSERT_EQ(1u, M.ions.count(ionKind::ca)); - auto& ca = M.ions.at(ionKind::ca); + ASSERT_EQ(1u, M.ions.count("ca"s)); + auto& ca = M.ions.at("ca"s); EXPECT_EQ(expected_ion_cv[run], ca.cv); EXPECT_TRUE(testing::seq_almost_eq<fvm_value_type>(expected_iconc_norm_area[run], ca.iconc_norm_area)); diff --git a/test/unit/test_fvm_lowered.cpp b/test/unit/test_fvm_lowered.cpp index cdc09371322f6e018aa5f9127c364ea1a3b10ce8..59fa34d1df9b66e0934dcc4378ccb2d82249c5f8 100644 --- a/test/unit/test_fvm_lowered.cpp +++ b/test/unit/test_fvm_lowered.cpp @@ -58,7 +58,7 @@ arb::mechanism* find_mechanism(fvm_cell& fvcell, const std::string& name) { using mechanism_global_table = std::vector<std::pair<const char*, arb::fvm_value_type*>>; using mechanism_field_table = std::vector<std::pair<const char*, arb::fvm_value_type**>>; -using mechanism_ion_index_table = std::vector<std::pair<arb::ionKind, backend::iarray*>>; +using mechanism_ion_index_table = std::vector<std::pair<const char*, backend::iarray*>>; ACCESS_BIND(\ mechanism_global_table (arb::multicore::mechanism::*)(),\ @@ -534,7 +534,7 @@ TEST(fvm_lowered, weighted_write_ion) { fvcell.initialize({0}, cable1d_recipe(c), cell_to_intdom, targets, probe_map); auto& state = *(fvcell.*private_state_ptr).get(); - auto& ion = state.ion_data.at(ionKind::ca); + auto& ion = state.ion_data.at("ca"s); ion.default_int_concentration = con_int; ion.default_ext_concentration = con_ext; ion.init_concentration(); @@ -553,7 +553,7 @@ TEST(fvm_lowered, weighted_write_ion) { ASSERT_TRUE(opt_cai_ptr); auto& test_ca_cai = *opt_cai_ptr.value(); - auto opt_ca_index_ptr = util::value_by_key((test_ca->*private_ion_index_table_ptr)(), ionKind::ca); + auto opt_ca_index_ptr = util::value_by_key((test_ca->*private_ion_index_table_ptr)(), "ca"s); ASSERT_TRUE(opt_ca_index_ptr); auto& test_ca_ca_index = *opt_ca_index_ptr.value();