diff --git a/arbor/backends/gpu/shared_state.cpp b/arbor/backends/gpu/shared_state.cpp index bed738bc86b5d74e45105a932147621477d79ff7..b01cd663082d365c9e33afa82051ee06ce1d9262 100644 --- a/arbor/backends/gpu/shared_state.cpp +++ b/arbor/backends/gpu/shared_state.cpp @@ -57,11 +57,14 @@ ion_state::ion_state( const fvm_ion_config& ion_data, unsigned // alignment/padding ignored. ): + write_eX_(ion_data.revpot_written), + write_Xo_(ion_data.econc_written), + write_Xi_(ion_data.iconc_written), node_index_(make_const_view(ion_data.cv)), iX_(ion_data.cv.size(), NAN), - eX_(ion_data.cv.size(), NAN), - Xi_(ion_data.cv.size(), NAN), - Xo_(ion_data.cv.size(), NAN), + eX_(ion_data.init_revpot.begin(), ion_data.init_revpot.end()), + Xi_(ion_data.init_iconc.begin(), ion_data.init_iconc.end()), + Xo_(ion_data.init_econc.begin(), ion_data.init_econc.end()), init_Xi_(make_const_view(ion_data.init_iconc)), init_Xo_(make_const_view(ion_data.init_econc)), reset_Xi_(make_const_view(ion_data.reset_iconc)), @@ -75,8 +78,8 @@ ion_state::ion_state( } void ion_state::init_concentration() { - memory::copy(init_Xi_, Xi_); - memory::copy(init_Xo_, Xo_); + if (write_Xi_) memory::copy(init_Xi_, Xi_); + if (write_Xo_) memory::copy(init_Xo_, Xo_); } void ion_state::zero_current() { @@ -85,9 +88,9 @@ void ion_state::zero_current() { void ion_state::reset() { zero_current(); - memory::copy(reset_Xi_, Xi_); - memory::copy(reset_Xo_, Xo_); - memory::copy(init_eX_, eX_); + if (write_Xi_) memory::copy(reset_Xi_, Xi_); + if (write_Xo_) memory::copy(reset_Xo_, Xo_); + if (write_eX_) memory::copy(init_eX_, eX_); } // istim_state methods: diff --git a/arbor/backends/gpu/shared_state.hpp b/arbor/backends/gpu/shared_state.hpp index e0cacfc78d95acf1bf62d81d9d9cc01cf16d07a0..85f1ea0c451331d59338bebf0747567c0cc8a91c 100644 --- a/arbor/backends/gpu/shared_state.hpp +++ b/arbor/backends/gpu/shared_state.hpp @@ -27,8 +27,11 @@ namespace gpu { * Xi_ cai internal calcium concentration * Xo_ cao external calcium concentration */ + struct ARB_ARBOR_API ion_state { + bool write_eX_; // is eX written? + bool write_Xo_; // is Xo written? + bool write_Xi_; // is Xi written? -struct ARB_ARBOR_API ion_state { iarray node_index_; // Instance to CV map. array iX_; // (A/m²) current density array eX_; // (mV) reversal potential diff --git a/arbor/backends/multicore/shared_state.cpp b/arbor/backends/multicore/shared_state.cpp index 538f7718d56db69592d5dc0bd76b86b42a7f12e6..f6c0b205714fa4cfacd7cabe575bd7373d04cac2 100644 --- a/arbor/backends/multicore/shared_state.cpp +++ b/arbor/backends/multicore/shared_state.cpp @@ -59,11 +59,14 @@ ion_state::ion_state( unsigned align ): alignment(min_alignment(align)), + write_eX_(ion_data.revpot_written), + write_Xo_(ion_data.econc_written), + write_Xi_(ion_data.iconc_written), node_index_(ion_data.cv.begin(), ion_data.cv.end(), pad(alignment)), iX_(ion_data.cv.size(), NAN, pad(alignment)), eX_(ion_data.init_revpot.begin(), ion_data.init_revpot.end(), pad(alignment)), - Xi_(ion_data.cv.size(), NAN, pad(alignment)), - Xo_(ion_data.cv.size(), NAN, pad(alignment)), + Xi_(ion_data.init_iconc.begin(), ion_data.init_iconc.end(), pad(alignment)), + Xo_(ion_data.init_econc.begin(), ion_data.init_econc.end(), pad(alignment)), init_Xi_(ion_data.init_iconc.begin(), ion_data.init_iconc.end(), pad(alignment)), init_Xo_(ion_data.init_econc.begin(), ion_data.init_econc.end(), pad(alignment)), reset_Xi_(ion_data.reset_iconc.begin(), ion_data.reset_iconc.end(), pad(alignment)), @@ -78,8 +81,8 @@ ion_state::ion_state( } void ion_state::init_concentration() { - std::copy(init_Xi_.begin(), init_Xi_.end(), Xi_.begin()); - std::copy(init_Xo_.begin(), init_Xo_.end(), Xo_.begin()); + if (write_Xi_) std::copy(init_Xi_.begin(), init_Xi_.end(), Xi_.begin()); + if (write_Xo_) std::copy(init_Xo_.begin(), init_Xo_.end(), Xo_.begin()); } void ion_state::zero_current() { @@ -88,9 +91,9 @@ void ion_state::zero_current() { void ion_state::reset() { zero_current(); - std::copy(reset_Xi_.begin(), reset_Xi_.end(), Xi_.begin()); - std::copy(reset_Xo_.begin(), reset_Xo_.end(), Xo_.begin()); - std::copy(init_eX_.begin(), init_eX_.end(), eX_.begin()); + if (write_Xi_) std::copy(reset_Xi_.begin(), reset_Xi_.end(), Xi_.begin()); + if (write_Xo_) std::copy(reset_Xo_.begin(), reset_Xo_.end(), Xo_.begin()); + if (write_eX_) std::copy(init_eX_.begin(), init_eX_.end(), eX_.begin()); } // istim_state methods: @@ -239,8 +242,7 @@ shared_state::shared_state( void shared_state::add_ion( const std::string& ion_name, int charge, - const fvm_ion_config& ion_info) -{ + const fvm_ion_config& ion_info) { ion_data.emplace(std::piecewise_construct, std::forward_as_tuple(ion_name), std::forward_as_tuple(charge, ion_info, alignment)); diff --git a/arbor/backends/multicore/shared_state.hpp b/arbor/backends/multicore/shared_state.hpp index ab9fa7f5342bd463b5ec015dc695b73f641f4aab..8aa8d4155141efb78fae60a14d660eddc6565cd5 100644 --- a/arbor/backends/multicore/shared_state.hpp +++ b/arbor/backends/multicore/shared_state.hpp @@ -38,10 +38,13 @@ namespace multicore { * Xi_ cai internal calcium concentration * Xo_ cao external calcium concentration */ - struct ARB_ARBOR_API ion_state { unsigned alignment = 1; // Alignment and padding multiple. + bool write_eX_; // is eX written? + bool write_Xo_; // is Xo written? + bool write_Xi_; // is Xi written? + iarray node_index_; // Instance to CV map. array iX_; // (A/m²) current density array eX_; // (mV) reversal potential diff --git a/arbor/fvm_layout.cpp b/arbor/fvm_layout.cpp index f231defdabf62d9678dba21a9730f34e566fc3f8..7ede9f7c8fe816da055c78e240b1a8c5c4761e29 100644 --- a/arbor/fvm_layout.cpp +++ b/arbor/fvm_layout.cpp @@ -595,9 +595,8 @@ fvm_mechanism_data& append(fvm_mechanism_data& left, const fvm_mechanism_data& r fvm_size_type target_offset = left.n_target; - for (const auto& kv: right.ions) { - fvm_ion_config& L = left.ions[kv.first]; - const fvm_ion_config& R = kv.second; + for (const auto& [k, R]: right.ions) { + fvm_ion_config& L = left.ions[k]; append(L.cv, R.cv); append(L.init_iconc, R.init_iconc); @@ -605,6 +604,9 @@ fvm_mechanism_data& append(fvm_mechanism_data& left, const fvm_mechanism_data& r append(L.reset_iconc, R.reset_iconc); append(L.reset_econc, R.reset_econc); append(L.init_revpot, R.init_revpot); + L.econc_written |= R.econc_written; + L.iconc_written |= R.iconc_written; + L.revpot_written |= R.revpot_written; } for (const auto& kv: right.mechanisms) { @@ -744,6 +746,9 @@ fvm_mechanism_data fvm_build_mechanism_data( const auto& global_dflt = gprop.default_parameters; const auto& dflt = cell.default_parameters(); + std::unordered_set<std::string> write_xi; + std::unordered_set<std::string> write_xo; + fvm_mechanism_data M; // Verify mechanism ion usage, parameter values. @@ -867,18 +872,20 @@ fvm_mechanism_data fvm_build_mechanism_data( } } - for (const auto& iondep: info.ions) { - if (iondep.second.write_concentration_int) { + for (const auto& [ion, dep]: info.ions) { + if (dep.write_concentration_int) { + write_xi.insert(ion); for (auto c: support) { - bool ok = init_iconc_mask[iondep.first].insert(c.first, 0.); + bool ok = init_iconc_mask[ion].insert(c.first, 0.); if (!ok) { throw cable_cell_error("overlapping ion concentration writing mechanism "+name); } } } - if (iondep.second.write_concentration_ext) { + if (dep.write_concentration_ext) { + write_xo.insert(ion); for (auto c: support) { - bool ok = init_econc_mask[iondep.first].insert(c.first, 0.); + bool ok = init_econc_mask[ion].insert(c.first, 0.); if (!ok) { throw cable_cell_error("overlapping ion concentration writing mechanism "+name); } @@ -993,7 +1000,7 @@ fvm_mechanism_data fvm_build_mechanism_data( return a.target_index<b.target_index; }); - bool coalesce = catalogue[name].linear && gprop.coalesce_synapses; + bool coalesce = info.linear && gprop.coalesce_synapses; fvm_mechanism_config config; config.kind = arb_mechanism_kind_point; @@ -1029,6 +1036,15 @@ fvm_mechanism_data fvm_build_mechanism_data( // If synapse uses an ion, add to ion support. update_ion_support(info, config.cv); + for (const auto& [ion, dep]: info.ions) { + if (dep.write_concentration_int) { + write_xi.insert(ion); + } + if (dep.write_concentration_ext) { + write_xo.insert(ion); + } + } + M.n_target += config.target.size(); if (!config.cv.empty()) M.mechanisms[name] = std::move(config); } @@ -1091,6 +1107,16 @@ fvm_mechanism_data fvm_build_mechanism_data( } lid_junction_desc.insert({pm.lid, std::move(per_lid)}); } + + for (const auto& [ion, dep]: info.ions) { + if (dep.write_concentration_int) { + write_xi.insert(ion); + } + if (dep.write_concentration_ext) { + write_xo.insert(ion); + } + } + junction_configs[name] = std::move(config); } @@ -1169,11 +1195,9 @@ fvm_mechanism_data fvm_build_mechanism_data( auto initial_econc_map = cell.region_assignments().get<init_ext_concentration>(); auto initial_rvpot_map = cell.region_assignments().get<init_reversal_potential>(); - for (const auto& ion_cvs: ion_support) { - const std::string& ion = ion_cvs.first; - + for (const auto& [ion, cvs]: ion_support) { fvm_ion_config config; - config.cv = ion_cvs.second; + config.cv = cvs; auto n_cv = config.cv.size(); config.init_iconc.resize(n_cv); @@ -1229,6 +1253,8 @@ fvm_mechanism_data fvm_build_mechanism_data( config.init_econc[i] *= oo_cv_area; } + config.econc_written = write_xo.count(ion); + config.iconc_written = write_xi.count(ion); if (!config.cv.empty()) M.ions[ion] = std::move(config); } @@ -1269,6 +1295,8 @@ fvm_mechanism_data fvm_build_mechanism_data( throw cable_cell_error("revpot mechanism for ion "+ion+" does not write this reversal potential"); } + M.ions[ion].revpot_written = true; + // Only instantiate if the ion is used. if (M.ions.count(ion)) { // Revpot mechanism already configured? Add cvs for this ion too. diff --git a/arbor/fvm_layout.hpp b/arbor/fvm_layout.hpp index 1ebe0827aa4dd06d0e5cfb043055b1a7075abbd4..b81debcd8111d2e0f8822ae554ec8fcaa0689aa1 100644 --- a/arbor/fvm_layout.hpp +++ b/arbor/fvm_layout.hpp @@ -224,6 +224,11 @@ struct fvm_ion_config { using value_type = fvm_value_type; using index_type = fvm_index_type; + // Keep track whether eX, Xi, Xo are actually to be reset. + bool revpot_written = false; + bool iconc_written = false; + bool econc_written = false; + // Ordered CV indices where ion must be present. std::vector<index_type> cv; diff --git a/arbor/fvm_lowered_cell_impl.hpp b/arbor/fvm_lowered_cell_impl.hpp index a548e4af16e75201b66676f07798a6cc8c51472b..ddfe104e4add2789aa2d7a80ddf331bcb2e128b8 100644 --- a/arbor/fvm_lowered_cell_impl.hpp +++ b/arbor/fvm_lowered_cell_impl.hpp @@ -503,14 +503,12 @@ fvm_initialization_data fvm_lowered_cell_impl<Backend>::initialize( // Instantiate mechanisms, ions, and stimuli. - for (auto& i: mech_data.ions) { - const std::string& ion_name = i.first; - - if (auto charge = value_by_key(global_props.ion_species, ion_name)) { - state_->add_ion(ion_name, *charge, i.second); + for (const auto& [ion, data]: mech_data.ions) { + if (auto charge = value_by_key(global_props.ion_species, ion)) { + state_->add_ion(ion, *charge, data); } else { - throw cable_cell_error("unrecognized ion '"+ion_name+"' in mechanism"); + throw cable_cell_error("unrecognized ion '"+ion+"' in mechanism"); } } diff --git a/arbor/version.cpp b/arbor/version.cpp index 88b693891f53ae9cf31b4f03edc2a8dd959c5ba2..242a6fdde77fa978b434f1665e3026bdb8c6376d 100644 --- a/arbor/version.cpp +++ b/arbor/version.cpp @@ -1,4 +1,5 @@ #include <arbor/version.hpp> +#include <arbor/export.hpp> namespace arb { ARB_ARBOR_API const char* source_id = ARB_SOURCE_ID; diff --git a/test/unit/test_fvm_lowered.cpp b/test/unit/test_fvm_lowered.cpp index e1599b5d7f719f568a97c7cfdbdb4f691b28dd08..b07208fdcf85c9445239d685027621936b4274e4 100644 --- a/test/unit/test_fvm_lowered.cpp +++ b/test/unit/test_fvm_lowered.cpp @@ -591,6 +591,9 @@ TEST(fvm_lowered, ionic_concentrations) { layout.cv.push_back(i); ion_config.cv.push_back(i); } + ion_config.econc_written = true; + ion_config.iconc_written = true; + ion_config.revpot_written = true; ion_config.init_revpot.assign(ncv, 0.); ion_config.init_econc.assign(ncv, 0.); ion_config.init_iconc.assign(ncv, 0.); diff --git a/test/unit/test_probe.cpp b/test/unit/test_probe.cpp index 4608bb1d4c938e634dcf47517e212f5ddba7b162..bc9f41c374678bd4f2251c3072d58402fdfd38ed 100644 --- a/test/unit/test_probe.cpp +++ b/test/unit/test_probe.cpp @@ -549,7 +549,19 @@ void run_ion_density_probe_test(const context& ctx) { rec.add_probe(0, 0, cable_probe_ion_ext_concentration_cell{"ca"}); fvm_cell lcell(*ctx); + auto fvm_info = lcell.initialize({0}, rec); + // We skipped FVM layout here, so we need to set these manually + auto& state = backend_access<Backend>::state(lcell); + state.ion_data["ca"].write_Xi_ = true; + state.ion_data["ca"].write_Xo_ = true; + state.ion_data["ca"].init_concentration(); + state.ion_data["na"].write_Xi_ = true; + state.ion_data["na"].write_Xo_ = true; + state.ion_data["na"].init_concentration(); + // Now, re-init cell + lcell.reset(); + const auto& probe_map = fvm_info.probe_map; // Should be no sodium ion instantiated on CV 0, so probe (0, 6) should