diff --git a/src/nrnoc/eion.cpp b/src/nrnoc/eion.cpp index e8e7657b7e..6dece3efb8 100644 --- a/src/nrnoc/eion.cpp +++ b/src/nrnoc/eion.cpp @@ -15,9 +15,8 @@ #include #include -#include -#include -#include +#include +#include #undef hoc_retpushx @@ -394,17 +393,37 @@ double nrn_nernst_coef(int type) { /* It is generally an error for two models to WRITE the same concentration. -This functions checks that there's no write conflict; and warns if it detects -one. It also sets respective write flag in the style of the ion. +nrn_check_conc_write checks that there's no write conflict; and warns if it +detects one. It also sets respective write flag in the style of the ion. The argument `i` specifies which concentration is being written to. It's 0 for exterior; and 1 for interior. */ -void nrn_check_conc_write(Prop* p_ok, Prop* pion, int i) { - const int max_ions = 256; - static long size_; - static std::vector> chk_conc_, ion_bit_; +// Each mechanism type index that writes a concentration has a set of ion +// type indices it writes to. +// ionconctype is coded as iontype*2 + i where i=1 for interior +// So number of distinct ion mechanisms is essentially unlimited, max_int/2. +static std::map> mechtype2ionconctype; + +static void add_mechtype2ionconctype(int mechtype, int iontype, int i) { + auto& set_of_ionconctypes = mechtype2ionconctype[mechtype]; + set_of_ionconctypes.insert(2 * iontype + i); // unique though inserting for each instance. +} + +static bool mech_uses_ionconctype(int mechtype, int iontype, int i) { + if (auto search = mechtype2ionconctype.find(mechtype); search != mechtype2ionconctype.end()) { + auto& set_of_ionconctypes = search->second; + return set_of_ionconctypes.count(2 * iontype + i) == 1; + } + return false; +} + +void nrn_check_conc_write(Prop* pmech, Prop* pion, int i) { + // Would be less redundant to generate the "database" at + // mechanism registration time. But NMODL presently gives us this info + // only at each mechanism instance allocation. + add_mechtype2ionconctype(pmech->_type, pion->_type, i); Prop* p; int flag, j, k; @@ -414,51 +433,32 @@ void nrn_check_conc_write(Prop* p_ok, Prop* pion, int i) { flag = 0400; } - /* Create a vector holding std::bitset to track which ions - are being written to the membrane */ - if (n_memb_func > size_) { - chk_conc_.resize(2 * n_memb_func); - ion_bit_.resize(n_memb_func); - - for (j = size_; j < n_memb_func; ++j) { - chk_conc_[2 * j].reset(); - chk_conc_[2 * j + 1].reset(); - ion_bit_[j].reset(); - } - - size_ = n_memb_func; - } - for (k = 0, j = 0; j < n_memb_func; ++j) { - if (nrn_is_ion(j)) { - assert(k < max_ions); - ion_bit_[j].reset(); - ion_bit_[j].set(k); - ++k; - } - } - - chk_conc_[2 * p_ok->_type + i] |= ion_bit_[pion->_type]; - if (pion->dparam[iontype_index_dparam].get() & flag) { - /* now comes the hard part. Is the possibility in fact actual.*/ + auto ii = pion->dparam[iontype_index_dparam].get(); + if (ii & flag) { + // Is the possibility in fact actual. Unfortunately, uninserting the + // mechanism that writes a concentration does not reset the flag bit. + // So search the node property list for another mechanism that also + // writes this ion concentration. (that is needed anyway to + // fill out the warning message.) + + // the pion in the node property list is before mechanisms that use the ion for (p = pion->next; p; p = p->next) { - if (p == p_ok) { + if (p == pmech) { continue; } - auto rst = chk_conc_[2 * p->_type + i] & ion_bit_[pion->_type]; - if (rst.any()) { + if (mech_uses_ionconctype(p->_type, pion->_type, i)) { char buf[300]; Sprintf(buf, "%.*s%c is being written at the same location by %s and %s", (int) strlen(memb_func[pion->_type].sym->name) - 4, memb_func[pion->_type].sym->name, ((i == 1) ? 'i' : 'o'), - memb_func[p_ok->_type].sym->name, + memb_func[pmech->_type].sym->name, memb_func[p->_type].sym->name); hoc_warning(buf, (char*) 0); } } } - auto ii = pion->dparam[iontype_index_dparam].get(); ii |= flag; pion->dparam[iontype_index_dparam] = ii; }