From e07f64fa4d2c7d2176b3f90c5eecb72bb3006c8d Mon Sep 17 00:00:00 2001 From: Sam Yates <halfflat@gmail.com> Date: Tue, 7 May 2019 16:28:14 +0930 Subject: [PATCH] Add conductivity to implicit voltage solve step for stability. (#735) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes implicit solve step from: > solve (c/δt + L) v' = c/δt v - J to > solve (c/δt + g + L)v' = (c/δt +g) v - J where _c_ is capacitance, _g_ is membrane conductance, _J_ is membrane current. * Compute conductivity contribution for mechanisms from symbolic d/dv of current contribution (extracted from linearity test.) * Add new modcc 'source kind' for conductivity; tie to `vec_g_`. * Add conductivity field to fvm shared state. * Include conductivity in matrix assemblies for solution. Fixes #633. --- arbor/backends/gpu/matrix_assemble.cu | 28 ++++++++++++------- arbor/backends/gpu/matrix_fine.cu | 11 +++++--- arbor/backends/gpu/matrix_fine.hpp | 1 + arbor/backends/gpu/matrix_state_fine.hpp | 10 ++++--- arbor/backends/gpu/matrix_state_flat.hpp | 5 ++-- .../backends/gpu/matrix_state_interleaved.hpp | 8 ++++-- arbor/backends/gpu/mechanism.cpp | 1 + arbor/backends/gpu/mechanism_ppack_base.hpp | 1 + arbor/backends/gpu/shared_state.cpp | 4 +++ arbor/backends/gpu/shared_state.hpp | 25 +++++++++-------- arbor/backends/multicore/matrix_state.hpp | 17 ++++++----- arbor/backends/multicore/mechanism.cpp | 1 + arbor/backends/multicore/mechanism.hpp | 1 + arbor/backends/multicore/shared_state.cpp | 4 +++ arbor/backends/multicore/shared_state.hpp | 15 +++++----- arbor/fvm_lowered_cell_impl.hpp | 2 +- arbor/matrix.hpp | 4 +-- modcc/identifier.hpp | 1 + modcc/module.cpp | 23 +++++++++++++-- modcc/printer/printerutil.cpp | 3 ++ test/unit/test_matrix.cpp | 21 ++++++++------ test/unit/test_matrix.cu | 15 ++++++---- test/unit/test_matrix_cpuvsgpu.cpp | 6 ++-- 23 files changed, 135 insertions(+), 72 deletions(-) diff --git a/arbor/backends/gpu/matrix_assemble.cu b/arbor/backends/gpu/matrix_assemble.cu index bcc589d2..4b998413 100644 --- a/arbor/backends/gpu/matrix_assemble.cu +++ b/arbor/backends/gpu/matrix_assemble.cu @@ -21,8 +21,9 @@ void assemble_matrix_flat( const T* invariant_d, const T* voltage, const T* current, + const T* conductivity, const T* cv_capacitance, - const T* area, + const T* cv_area, const I* cv_to_cell, const T* dt_intdom, const I* cell_to_intdom, @@ -42,11 +43,12 @@ void assemble_matrix_flat( // The 1e-3 is a constant of proportionality required to ensure that the // conductance (gi) values have units μS (micro-Siemens). // See the model documentation in docs/model for more information. - T factor = 1e-3/dt; + T oodt_factor = 1e-3/dt; // [1/μs] + T area_factor = 1e-3*cv_area[tid]; // [1e-9·m²] - auto gi = factor * cv_capacitance[tid]; + auto gi = oodt_factor * cv_capacitance[tid] + area_factor*conductivity[tid]; // [μS] d[tid] = gi + invariant_d[tid]; - rhs[tid] = gi*voltage[tid] - T(1e-3)*area[tid]*current[tid]; + rhs[tid] = gi*voltage[tid] - area_factor*current[tid]; } else { d[tid] = 0; @@ -69,6 +71,7 @@ void assemble_matrix_interleaved( const T* invariant_d, const T* voltage, const T* current, + const T* conductivity, const T* cv_capacitance, const T* area, const I* sizes, @@ -82,6 +85,7 @@ void assemble_matrix_interleaved( "number of threads must equal number of values to process per block"); __shared__ T buffer_v[Threads]; __shared__ T buffer_i[Threads]; + __shared__ T buffer_g[Threads]; const unsigned tid = threadIdx.x + blockIdx.x*blockDim.x; const unsigned lid = threadIdx.x; @@ -103,7 +107,7 @@ void assemble_matrix_interleaved( const unsigned max_size = sizes[0]; - T factor = 0; + T oodt_factor = 0; T dt = 0; const unsigned permuted_cid = blk_id*BlockWidth + blk_lane; @@ -115,23 +119,25 @@ void assemble_matrix_interleaved( // conductance (gi) values have units μS (micro-Siemens). // See the model documentation in docs/model for more information. - factor = dt>0? 1e-3/dt: 0; + oodt_factor = dt>0? T(1e-3)/dt: 0; } for (unsigned j=0u; j<max_size; j+=LoadWidth) { if (do_load && load_pos<end) { buffer_v[lid] = voltage[load_pos]; buffer_i[lid] = current[load_pos]; + buffer_g[lid] = conductivity[load_pos]; } __syncthreads(); if (j+blk_row<padded_size) { - const auto gi = factor * cv_capacitance[store_pos]; + T area_factor = T(1e-3)*area[store_pos]; + const auto gi = oodt_factor*cv_capacitance[store_pos] + area_factor*buffer_g[blk_pos]; if (dt>0) { d[store_pos] = (gi + invariant_d[store_pos]); - rhs[store_pos] = (gi*buffer_v[blk_pos] - T(1e-3)*area[store_pos]*buffer_i[blk_pos]); + rhs[store_pos] = (gi*buffer_v[blk_pos] - area_factor*buffer_i[blk_pos]); } else { d[store_pos] = 0; @@ -154,6 +160,7 @@ void assemble_matrix_flat( const fvm_value_type* invariant_d, const fvm_value_type* voltage, const fvm_value_type* current, + const fvm_value_type* conductivity, const fvm_value_type* cv_capacitance, const fvm_value_type* area, const fvm_index_type* cv_to_cell, @@ -167,7 +174,7 @@ void assemble_matrix_flat( kernels::assemble_matrix_flat <fvm_value_type, fvm_index_type> <<<grid_dim, block_dim>>> - (d, rhs, invariant_d, voltage, current, cv_capacitance, + (d, rhs, invariant_d, voltage, current, conductivity, cv_capacitance, area, cv_to_cell, dt_intdom, cell_to_intdom, n); } @@ -178,6 +185,7 @@ void assemble_matrix_interleaved( const fvm_value_type* invariant_d, const fvm_value_type* voltage, const fvm_value_type* current, + const fvm_value_type* conductivity, const fvm_value_type* cv_capacitance, const fvm_value_type* area, const fvm_index_type* sizes, @@ -197,7 +205,7 @@ void assemble_matrix_interleaved( kernels::assemble_matrix_interleaved <fvm_value_type, fvm_index_type, bd, lw, block_dim> <<<grid_dim, block_dim>>> - (d, rhs, invariant_d, voltage, current, cv_capacitance, area, + (d, rhs, invariant_d, voltage, current, conductivity, cv_capacitance, area, sizes, starts, matrix_to_cell, dt_intdom, cell_to_intdom, padded_size, num_mtx); } diff --git a/arbor/backends/gpu/matrix_fine.cu b/arbor/backends/gpu/matrix_fine.cu index f3ad5372..6fe430df 100644 --- a/arbor/backends/gpu/matrix_fine.cu +++ b/arbor/backends/gpu/matrix_fine.cu @@ -50,6 +50,7 @@ void assemble_matrix_fine( const T* invariant_d, const T* voltage, const T* current, + const T* conductivity, const T* cv_capacitance, const T* area, const I* cv_to_cell, @@ -68,12 +69,13 @@ void assemble_matrix_fine( // The 1e-3 is a constant of proportionality required to ensure that the // conductance (gi) values have units μS (micro-Siemens). // See the model documentation in docs/model for more information. - T factor = 1e-3/dt; + T oodt_factor = T(1e-3)/dt; + T area_factor = T(1e-3)*area[tid]; - const auto gi = factor * cv_capacitance[tid]; + const auto gi = oodt_factor*cv_capacitance[tid] + area_factor*conductivity[tid]; const auto pid = perm[tid]; d[pid] = gi + invariant_d[tid]; - rhs[pid] = gi*voltage[tid] - T(1e-3)*area[tid]*current[tid]; + rhs[pid] = gi*voltage[tid] - area_factor*current[tid]; } else { const auto pid = perm[tid]; @@ -262,6 +264,7 @@ void assemble_matrix_fine( const fvm_value_type* invariant_d, const fvm_value_type* voltage, const fvm_value_type* current, + const fvm_value_type* conductivity, const fvm_value_type* cv_capacitance, const fvm_value_type* area, const fvm_index_type* cv_to_cell, @@ -274,7 +277,7 @@ void assemble_matrix_fine( const unsigned num_blocks = impl::block_count(n, block_dim); kernels::assemble_matrix_fine<<<num_blocks, block_dim>>>( - d, rhs, invariant_d, voltage, current, cv_capacitance, area, + d, rhs, invariant_d, voltage, current, conductivity, cv_capacitance, area, cv_to_cell, dt_intdom, cell_to_intdom, perm, n); } diff --git a/arbor/backends/gpu/matrix_fine.hpp b/arbor/backends/gpu/matrix_fine.hpp index 896bb28b..1a15bd7f 100644 --- a/arbor/backends/gpu/matrix_fine.hpp +++ b/arbor/backends/gpu/matrix_fine.hpp @@ -55,6 +55,7 @@ void assemble_matrix_fine( const fvm_value_type* invariant_d, const fvm_value_type* voltage, const fvm_value_type* current, + const fvm_value_type* conductivity, const fvm_value_type* cv_capacitance, const fvm_value_type* area, const fvm_index_type* cv_to_cell, diff --git a/arbor/backends/gpu/matrix_state_fine.hpp b/arbor/backends/gpu/matrix_state_fine.hpp index f5c44cbe..87ebf2ae 100644 --- a/arbor/backends/gpu/matrix_state_fine.hpp +++ b/arbor/backends/gpu/matrix_state_fine.hpp @@ -437,17 +437,19 @@ public: } // Assemble the matrix - // Afterwards the diagonal and RHS will have been set given dt, voltage and current - // dt_intdom [ms] (per cell) + // Afterwards the diagonal and RHS will have been set given dt, voltage, current, and conductivity. + // dt_intdom [ms] (per integration domain) // voltage [mV] - // current [nA] - void assemble(const_view dt_intdom, const_view voltage, const_view current) { + // current density [A/m²] + // conductivity [kS/m²] + void assemble(const_view dt_intdom, const_view voltage, const_view current, const_view conductivity) { assemble_matrix_fine( d.data(), rhs.data(), invariant_d.data(), voltage.data(), current.data(), + conductivity.data(), cv_capacitance.data(), cv_area.data(), cv_to_cell.data(), diff --git a/arbor/backends/gpu/matrix_state_flat.hpp b/arbor/backends/gpu/matrix_state_flat.hpp index 6f4cbd01..df1c8c84 100644 --- a/arbor/backends/gpu/matrix_state_flat.hpp +++ b/arbor/backends/gpu/matrix_state_flat.hpp @@ -27,6 +27,7 @@ void assemble_matrix_flat( const fvm_value_type* invariant_d, const fvm_value_type* voltage, const fvm_value_type* current, + const fvm_value_type* conductivity, const fvm_value_type* cv_capacitance, const fvm_value_type* cv_area, const fvm_index_type* cv_to_cell, @@ -123,11 +124,11 @@ struct matrix_state_flat { // dt_intdom [ms] (per integration domain) // voltage [mV] // current [nA] - void assemble(const_view dt_intdom, const_view voltage, const_view current) { + void assemble(const_view dt_intdom, const_view voltage, const_view current, const_view conductance) { // perform assembly on the gpu assemble_matrix_flat( d.data(), rhs.data(), invariant_d.data(), voltage.data(), - current.data(), cv_capacitance.data(), cv_area.data(), + current.data(), conductance.data(), cv_capacitance.data(), cv_area.data(), cv_to_cell.data(), dt_intdom.data(), cell_to_intdom.data(), size()); } diff --git a/arbor/backends/gpu/matrix_state_interleaved.hpp b/arbor/backends/gpu/matrix_state_interleaved.hpp index 864b77eb..89e63cb7 100644 --- a/arbor/backends/gpu/matrix_state_interleaved.hpp +++ b/arbor/backends/gpu/matrix_state_interleaved.hpp @@ -23,6 +23,7 @@ void assemble_matrix_interleaved( const fvm_value_type* invariant_d, const fvm_value_type* voltage, const fvm_value_type* current, + const fvm_value_type* conductivity, const fvm_value_type* cv_capacitance, const fvm_value_type* area, const fvm_index_type* sizes, @@ -266,11 +267,12 @@ struct matrix_state_interleaved { // Afterwards the diagonal and RHS will have been set given dt, voltage and current. // dt_intdom [ms] (per integration domain) // voltage [mV] (per compartment) - // current density [A.m^-2] (per compartment) - void assemble(const_view dt_intdom, const_view voltage, const_view current) { + // current density [A/m²] (per compartment) + // conductivity [kS/m²] (per compartment) + void assemble(const_view dt_intdom, const_view voltage, const_view current, const_view conductivity) { assemble_matrix_interleaved (d.data(), rhs.data(), invariant_d.data(), - voltage.data(), current.data(), cv_capacitance.data(), cv_area.data(), + voltage.data(), current.data(), conductivity.data(), cv_capacitance.data(), cv_area.data(), matrix_sizes.data(), matrix_index.data(), matrix_to_cell_index.data(), dt_intdom.data(), cell_to_intdom.data(), padded_matrix_size(), num_matrices()); diff --git a/arbor/backends/gpu/mechanism.cpp b/arbor/backends/gpu/mechanism.cpp index d73e46e9..f1c74225 100644 --- a/arbor/backends/gpu/mechanism.cpp +++ b/arbor/backends/gpu/mechanism.cpp @@ -76,6 +76,7 @@ void mechanism::instantiate(unsigned id, pp->vec_v_ = shared.voltage.data(); pp->vec_i_ = shared.current_density.data(); + pp->vec_g_ = shared.conductivity.data(); pp->temperature_degC_ = shared.temperature_degC.data(); diff --git a/arbor/backends/gpu/mechanism_ppack_base.hpp b/arbor/backends/gpu/mechanism_ppack_base.hpp index 16518c09..457f3eeb 100644 --- a/arbor/backends/gpu/mechanism_ppack_base.hpp +++ b/arbor/backends/gpu/mechanism_ppack_base.hpp @@ -35,6 +35,7 @@ struct mechanism_ppack_base { const value_type* vec_dt_; const value_type* vec_v_; value_type* vec_i_; + value_type* vec_g_; const value_type* temperature_degC_; const index_type* node_index_; diff --git a/arbor/backends/gpu/shared_state.cpp b/arbor/backends/gpu/shared_state.cpp index fe499023..1f8c7331 100644 --- a/arbor/backends/gpu/shared_state.cpp +++ b/arbor/backends/gpu/shared_state.cpp @@ -126,6 +126,7 @@ shared_state::shared_state( dt_cv(n_cv), voltage(n_cv), current_density(n_cv), + conductivity(n_cv), temperature_degC(1), deliverable_events(n_intdom) {} @@ -144,6 +145,7 @@ void shared_state::add_ion( void shared_state::reset(fvm_value_type initial_voltage, fvm_value_type temperature_K) { memory::fill(voltage, initial_voltage); memory::fill(current_density, 0); + memory::fill(conductivity, 0); memory::fill(time, 0); memory::fill(time_to, 0); memory::fill(temperature_degC, temperature_K - 273.15); @@ -155,6 +157,7 @@ void shared_state::reset(fvm_value_type initial_voltage, fvm_value_type temperat void shared_state::zero_currents() { memory::fill(current_density, 0); + memory::fill(conductivity, 0); for (auto& i: ion_data) { i.second.zero_current(); } @@ -205,6 +208,7 @@ std::ostream& operator<<(std::ostream& o, shared_state& s) { o << " dt_cv " << s.dt_cv << "\n"; o << " voltage " << s.voltage << "\n"; o << " current " << s.current_density << "\n"; + o << " conductivity " << s.conductivity << "\n"; for (auto& ki: s.ion_data) { auto kn = to_string(ki.first); auto& i = const_cast<ion_state&>(ki.second); diff --git a/arbor/backends/gpu/shared_state.hpp b/arbor/backends/gpu/shared_state.hpp index 778d82b2..fe662720 100644 --- a/arbor/backends/gpu/shared_state.hpp +++ b/arbor/backends/gpu/shared_state.hpp @@ -66,18 +66,19 @@ struct ion_state { struct shared_state { fvm_size_type n_intdom = 0; // Number of distinct integration domains. - fvm_size_type n_cv = 0; // Total number of CVs. - fvm_size_type n_gj = 0; // Total number of GJs. - - iarray cv_to_intdom; // Maps CV index to intdom index. - gjarray gap_junctions; // Stores gap_junction info. - array time; // Maps intdom index to integration start time [ms]. - array time_to; // Maps intdom index to integration stop time [ms]. - array dt_intdom; // Maps intdom index to (stop time) - (start time) [ms]. - array dt_cv; // Maps CV index to dt [ms]. - array voltage; // Maps CV index to membrane voltage [mV]. - array current_density; // Maps CV index to current density [A/m²]. - array temperature_degC; // Global temperature [°C] (length 1 array). + fvm_size_type n_cv = 0; // Total number of CVs. + fvm_size_type n_gj = 0; // Total number of GJs. + + iarray cv_to_intdom; // Maps CV index to intdom index. + gjarray gap_junctions; // Stores gap_junction info. + array time; // Maps intdom index to integration start time [ms]. + array time_to; // Maps intdom index to integration stop time [ms]. + array dt_intdom; // Maps intdom index to (stop time) - (start time) [ms]. + array dt_cv; // Maps CV index to dt [ms]. + array voltage; // Maps CV index to membrane voltage [mV]. + array current_density; // Maps CV index to current density [A/m²]. + 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; diff --git a/arbor/backends/multicore/matrix_state.hpp b/arbor/backends/multicore/matrix_state.hpp index 9e54b8eb..7cba8e96 100644 --- a/arbor/backends/multicore/matrix_state.hpp +++ b/arbor/backends/multicore/matrix_state.hpp @@ -74,10 +74,11 @@ public: // Assemble the matrix // Afterwards the diagonal and RHS will have been set given dt, voltage and current. - // dt_intdom [ms] (per integration domain) - // voltage [mV] (per compartment) - // current density [A.m^-2] (per compartment) - void assemble(const_view dt_intdom, const_view voltage, const_view current) { + // dt_intdom [ms] (per integration domain) + // voltage [mV] (per control volume) + // current density [A.m^-2] (per control volume) + // conductivity [kS.m^-2] (per control volume) + void assemble(const_view dt_intdom, const_view voltage, const_view current, const_view conductivity) { auto cell_cv_part = util::partition_view(cell_cv_divs); const index_type ncells = cell_cv_part.size(); @@ -86,13 +87,15 @@ public: auto dt = dt_intdom[cell_to_intdom[m]]; if (dt>0) { - value_type factor = 1e-3/dt; + value_type oodt_factor = 1e-3/dt; // [1/µs] for (auto i: util::make_span(cell_cv_part[m])) { - auto gi = factor*cv_capacitance[i]; + auto area_factor = 1e-3*cv_area[i]; // [1e-9·m²] + + auto gi = oodt_factor*cv_capacitance[i] + area_factor*conductivity[i]; // [μS] d[i] = gi + invariant_d[i]; // convert current to units nA - rhs[i] = gi*voltage[i] - 1e-3*cv_area[i]*current[i]; + rhs[i] = gi*voltage[i] - area_factor*current[i]; } } else { diff --git a/arbor/backends/multicore/mechanism.cpp b/arbor/backends/multicore/mechanism.cpp index 97aa457e..fde15d3e 100644 --- a/arbor/backends/multicore/mechanism.cpp +++ b/arbor/backends/multicore/mechanism.cpp @@ -78,6 +78,7 @@ void mechanism::instantiate(unsigned id, backend::shared_state& shared, const la vec_v_ = shared.voltage.data(); vec_i_ = shared.current_density.data(); + vec_g_ = shared.conductivity.data(); temperature_degC_ = &shared.temperature_degC; diff --git a/arbor/backends/multicore/mechanism.hpp b/arbor/backends/multicore/mechanism.hpp index 21493e94..f3819ab1 100644 --- a/arbor/backends/multicore/mechanism.hpp +++ b/arbor/backends/multicore/mechanism.hpp @@ -79,6 +79,7 @@ protected: const value_type* vec_dt_; // CV to integration time step. const value_type* vec_v_; // CV to cell membrane voltage. value_type* vec_i_; // CV to cell membrane current density. + value_type* vec_g_; // CV to cell membrane conductivity. const value_type* temperature_degC_; // Pointer to global temperature scalar. deliverable_event_stream* event_stream_ptr_; diff --git a/arbor/backends/multicore/shared_state.cpp b/arbor/backends/multicore/shared_state.cpp index 6313d429..6af8064d 100644 --- a/arbor/backends/multicore/shared_state.cpp +++ b/arbor/backends/multicore/shared_state.cpp @@ -133,6 +133,7 @@ shared_state::shared_state( dt_cv(n_cv, pad(alignment)), voltage(n_cv, pad(alignment)), current_density(n_cv, pad(alignment)), + conductivity(n_cv, pad(alignment)), temperature_degC(NAN), deliverable_events(n_intdom) { @@ -161,6 +162,7 @@ void shared_state::add_ion( void shared_state::reset(fvm_value_type initial_voltage, fvm_value_type temperature_K) { util::fill(voltage, initial_voltage); util::fill(current_density, 0); + util::fill(conductivity, 0); util::fill(time, 0); util::fill(time_to, 0); temperature_degC = temperature_K - 273.15; @@ -172,6 +174,7 @@ void shared_state::reset(fvm_value_type initial_voltage, fvm_value_type temperat void shared_state::zero_currents() { util::fill(current_density, 0); + util::fill(conductivity, 0); for (auto& i: ion_data) { i.second.zero_current(); } @@ -262,6 +265,7 @@ std::ostream& operator<<(std::ostream& out, const shared_state& s) { out << "dt_cv " << csv(s.dt_cv) << "\n"; 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); auto& i = const_cast<ion_state&>(ki.second); diff --git a/arbor/backends/multicore/shared_state.hpp b/arbor/backends/multicore/shared_state.hpp index fc2c0e7f..be3b5725 100644 --- a/arbor/backends/multicore/shared_state.hpp +++ b/arbor/backends/multicore/shared_state.hpp @@ -88,14 +88,15 @@ struct shared_state { fvm_size_type n_cv = 0; // Total number of CVs. fvm_size_type n_gj = 0; // Total number of GJs. - iarray cv_to_intdom; // Maps CV index to integration domain index. + iarray cv_to_intdom; // Maps CV index to integration domain index. gjarray gap_junctions; // Stores gap_junction info. - array time; // Maps intdom index to integration start time [ms]. - array time_to; // Maps intdom index to integration stop time [ms]. - array dt_intdom; // Maps index to (stop time) - (start time) [ms]. - array dt_cv; // Maps CV index to dt [ms]. - array voltage; // Maps CV index to membrane voltage [mV]. - array current_density; // Maps CV index to current density [A/m²]. + array time; // Maps intdom index to integration start time [ms]. + array time_to; // Maps intdom index to integration stop time [ms]. + array dt_intdom; // Maps index to (stop time) - (start time) [ms]. + array dt_cv; // Maps CV index to dt [ms]. + array voltage; // Maps CV index to membrane voltage [mV]. + array current_density; // Maps CV index to membrane current density contributions [A/m²]. + 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; diff --git a/arbor/fvm_lowered_cell_impl.hpp b/arbor/fvm_lowered_cell_impl.hpp index 1bd3889d..c3e1d8da 100644 --- a/arbor/fvm_lowered_cell_impl.hpp +++ b/arbor/fvm_lowered_cell_impl.hpp @@ -237,7 +237,7 @@ fvm_integration_result fvm_lowered_cell_impl<Backend>::integrate( // Integrate voltage by matrix solve. PE(advance_integrate_matrix_build); - matrix_.assemble(state_->dt_intdom, state_->voltage, state_->current_density); + matrix_.assemble(state_->dt_intdom, state_->voltage, state_->current_density, state_->conductivity); PL(); PE(advance_integrate_matrix_solve); matrix_.solve(); diff --git a/arbor/matrix.hpp b/arbor/matrix.hpp index 94958c00..8609cb7c 100644 --- a/arbor/matrix.hpp +++ b/arbor/matrix.hpp @@ -67,8 +67,8 @@ public: } /// Assemble the matrix for given dt - void assemble(const array& dt_cell, const array& voltage, const array& current) { - state_.assemble(dt_cell, voltage, current); + void assemble(const array& dt_cell, const array& voltage, const array& current, const array& conductivity) { + state_.assemble(dt_cell, voltage, current, conductivity); } /// Get a view of the solution diff --git a/modcc/identifier.hpp b/modcc/identifier.hpp index 3ddd3c56..891e6f49 100644 --- a/modcc/identifier.hpp +++ b/modcc/identifier.hpp @@ -46,6 +46,7 @@ enum class ionKind { enum class sourceKind { voltage, current, + conductivity, dt, ion_current, ion_revpot, diff --git a/modcc/module.cpp b/modcc/module.cpp index 93e5ac7c..c78d9c72 100644 --- a/modcc/module.cpp +++ b/modcc/module.cpp @@ -45,16 +45,25 @@ public: virtual void finalize() override { if (has_current_update_) { - // Initialize current_ as first statement. + // Initialize current_ and conductivity_ as first statements. + statements_.push_front(make_expression<AssignmentExpression>(loc_, + id("conductivity_"), + make_expression<NumberExpression>(loc_, 0.0))); statements_.push_front(make_expression<AssignmentExpression>(loc_, id("current_"), make_expression<NumberExpression>(loc_, 0.0))); + // Scale current and conductivity contributions by weight. statements_.push_back(make_expression<AssignmentExpression>(loc_, id("current_"), make_expression<MulBinaryExpression>(loc_, id("weight_"), id("current_")))); + statements_.push_back(make_expression<AssignmentExpression>(loc_, + id("conductivity_"), + make_expression<MulBinaryExpression>(loc_, + id("weight_"), + id("conductivity_")))); for (auto& v: ion_current_vars_) { statements_.push_back(make_expression<AssignmentExpression>(loc_, @@ -79,7 +88,8 @@ public: } has_current_update_ = true; - if (!linear_test(e->rhs(), {"v"}).is_linear) { + linear_test_result L = linear_test(e->rhs(), {"v"}); + if (!L.is_linear) { error({"current update expressions must be linear in v: "+e->rhs()->to_string(), e->location()}); return; @@ -90,6 +100,13 @@ public: make_expression<AddBinaryExpression>(loc, id("current_", loc), e->lhs()->clone()))); + if (L.coef.count("v")) { + statements_.push_back(make_expression<AssignmentExpression>(loc, + id("conductivity_", loc), + make_expression<AddBinaryExpression>(loc, + id("conductivity_", loc), + L.coef.at("v")->clone()))); + } } } } @@ -492,6 +509,8 @@ void Module::add_variables_to_symbols() { create_indexed_variable("current_", "vec_i", sourceKind::current, tok::plus, accessKind::write, ionKind::none, Location()); + create_indexed_variable("conductivity_", "vec_g", sourceKind::conductivity, tok::plus, + accessKind::write, ionKind::none, Location()); create_indexed_variable("v", "vec_v", sourceKind::voltage, tok::eq, accessKind::read, ionKind::none, Location()); create_indexed_variable("dt", "vec_dt", sourceKind::dt, tok::eq, diff --git a/modcc/printer/printerutil.cpp b/modcc/printer/printerutil.cpp index 09bc4c73..f5f95cf5 100644 --- a/modcc/printer/printerutil.cpp +++ b/modcc/printer/printerutil.cpp @@ -129,6 +129,9 @@ indexed_variable_info decode_indexed_variable(IndexedVariable* sym) { case sourceKind::current: data_var="vec_i_"; break; + case sourceKind::conductivity: + data_var="vec_g_"; + break; case sourceKind::dt: data_var="vec_dt_"; break; diff --git a/test/unit/test_matrix.cpp b/test/unit/test_matrix.cpp index eb72dacf..6ead914f 100644 --- a/test/unit/test_matrix.cpp +++ b/test/unit/test_matrix.cpp @@ -144,18 +144,21 @@ TEST(matrix, zero_diagonal_assembled) // Intial voltage of zero; currents alone determine rhs. array v(7, 0.0); vvec area(7, 1.0); - array i = {-3000, -5000, -7000, -6000, -9000, -16000, -32000}; + + // (Scaled) membrane conductances contribute to diagonal. + array mg = { 1000, 2000, 3000, 4000, 5000, 6000, 7000 }; + array i = {-7000, -15000, -25000, -34000, -49000, -70000, -102000}; // Expected matrix and rhs: // u = [ 0 -1 -1 0 -1 0 -2] - // d = [ 2 3 2 2 2 4 5] - // rhs = [ 3 5 7 2 4 16 32] + // d = [ 3 5 5 6 7 10 12] + // rhs = [ 7 15 25 34 49 70 102 ] // // Expected solution: - // x = [ 4 5 6 7 8 9 10] + // x = [ 4 5 6 7 8 9 10 ] matrix_type m(p, c, Cm, g, area, s); - m.assemble(dt, v, i); + m.assemble(dt, v, i, mg); m.solve(); vvec x; @@ -168,13 +171,13 @@ TEST(matrix, zero_diagonal_assembled) // should then return voltage values for that submatrix. dt[1] = 0; - v[3] = 20; - v[4] = 30; - m.assemble(dt, v, i); + v[3] = -20; + v[4] = -30; + m.assemble(dt, v, i, mg); m.solve(); assign(x, m.solution()); - expected = {4, 5, 6, 20, 30, 9, 10}; + expected = {4, 5, 6, -20, -30, 9, 10}; EXPECT_TRUE(testing::seq_almost_eq<double>(expected, x)); } diff --git a/test/unit/test_matrix.cu b/test/unit/test_matrix.cu index 8d01632d..706f3a34 100644 --- a/test/unit/test_matrix.cu +++ b/test/unit/test_matrix.cu @@ -270,8 +270,8 @@ TEST(matrix, backends) auto group_size = cell_cv_divs.back(); - // Build the capacitance and conductance vectors and - // populate with nonzero random values + // Build the capacitance, (axial) conductance, voltage, current density, + // and membrane conductance vectors. Populate them with nonzero random values. auto gen = std::mt19937(); gen.seed(100); auto dist = std::uniform_real_distribution<T>(1, 200); @@ -280,12 +280,14 @@ TEST(matrix, backends) std::vector<T> g(group_size); std::vector<T> v(group_size); std::vector<T> i(group_size); + std::vector<T> mg(group_size); std::vector<T> area(group_size, 1e3); std::generate(Cm.begin(), Cm.end(), [&](){return dist(gen);}); std::generate(g.begin(), g.end(), [&](){return dist(gen);}); std::generate(v.begin(), v.end(), [&](){return dist(gen);}); std::generate(i.begin(), i.end(), [&](){return dist(gen);}); + std::generate(mg.begin(), mg.end(), [&](){return dist(gen);}); // Make the reference matrix and the gpu matrix auto flat = state_flat(p, cell_cv_divs, Cm, g, area, cell_to_intdom); // flat @@ -298,14 +300,15 @@ TEST(matrix, backends) auto dt_dist = std::uniform_real_distribution<T>(0.01, 0.02); std::generate(dt.begin(), dt.end(), [&](){return dt_dist(gen);}); - // Voltage and current values. + // Voltage, current, and membrane conductance values. auto gpu_dt = on_gpu(dt); auto gpu_v = on_gpu(v); auto gpu_i = on_gpu(i); + auto gpu_mg = on_gpu(mg); - flat.assemble(gpu_dt, gpu_v, gpu_i); - intl.assemble(gpu_dt, gpu_v, gpu_i); - fine.assemble(gpu_dt, gpu_v, gpu_i); + flat.assemble(gpu_dt, gpu_v, gpu_i, gpu_mg); + intl.assemble(gpu_dt, gpu_v, gpu_i, gpu_mg); + fine.assemble(gpu_dt, gpu_v, gpu_i, gpu_mg); flat.solve(); intl.solve(); diff --git a/test/unit/test_matrix_cpuvsgpu.cpp b/test/unit/test_matrix_cpuvsgpu.cpp index 9d90e062..f89bb900 100644 --- a/test/unit/test_matrix_cpuvsgpu.cpp +++ b/test/unit/test_matrix_cpuvsgpu.cpp @@ -123,10 +123,10 @@ TEST(matrix, assemble) auto dt_dist = std::uniform_real_distribution<T>(0.1, 0.2); std::generate(dt.begin(), dt.end(), [&](){return dt_dist(gen);}); - // Voltage and current values - m_mc.assemble(host_array(dt.begin(), dt.end()), host_array(group_size, -64), host_array(group_size, 10)); + // Voltage, current, and conductance values + m_mc.assemble(host_array(dt.begin(), dt.end()), host_array(group_size, -64), host_array(group_size, 10), host_array(group_size, 3)); m_mc.solve(); - m_gpu.assemble(on_gpu(dt), gpu_array(group_size, -64), gpu_array(group_size, 10)); + m_gpu.assemble(on_gpu(dt), gpu_array(group_size, -64), gpu_array(group_size, 10), gpu_array(group_size, 3)); m_gpu.solve(); // Compare the GPU and CPU results. -- GitLab