svMultiPhysics
Loading...
Searching...
No Matches
IonicModel.h
1// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the
2// University of California, and others. SPDX-License-Identifier: BSD-3-Clause
3
4#ifndef IONIC_MODEL_H
5#define IONIC_MODEL_H
6
7#include "Array.h"
8#include "Parameters.h"
9#include "Vector.h"
10
12
13#include <string>
14#include <utility>
15#include <vector>
16
17#include "CmMod.h"
18
19#include "factory.h"
20
21// Forward declarations.
22class outputType;
23
24/**
25 * @brief Enumeration of time integration types for ODEs.
26 */
27enum class TimeIntegrationType {
28 NA = 200, ///< Undefined integration method.
29 FE = 201, ///< Forward Euler.
30 RK4 = 202, ///< 4th order explicit Runge-Kutta
31 CN2 = 203 ///< Crank-Nicolson.
32};
33
34/// Map from a string representation of the integration type to the actual type.
35extern const std::map<std::string, TimeIntegrationType> cep_time_int_to_type;
36
37/// Write a TimeIntegrationType to an output stream.
38static std::ostream &operator<<(std::ostream &strm, TimeIntegrationType type) {
39 const std::map<TimeIntegrationType, std::string> names = {
40 {TimeIntegrationType::NA, "NA"},
41 {TimeIntegrationType::FE, "FE"},
42 {TimeIntegrationType::RK4, "RK4"},
43 {TimeIntegrationType::CN2, "CN2"},
44 };
45
46 return strm << names.at(type);
47}
48
49/// @brief Time integration scheme and related parameters
50class odeType {
51public:
52 odeType() {};
53
54 /// @brief Time integration method type
55 TimeIntegrationType tIntType = TimeIntegrationType::NA;
56
57 /// @brief Max. iterations for Newton-Raphson method
58 int maxItr = 5;
59
60 /// @brief Absolute tolerance
61 double absTol = 1.E-8;
62
63 /// @brief Relative tolerance
64 double relTol = 1.E-4;
65};
66
67/**
68 * @brief Abstract ionic model class.
69 *
70 * ### Mathematical model
71 *
72 * This class represents an abstract ionic model, i.e. an ODE system in the form
73 * @f[ \begin{aligned}
74 * \frac{\partial v}{\partial t} + I_\text{ion}(v, \mathbf{w}) &=
75 * I_\text{ext}(t) \\
76 * \frac{\partial \mathbf{w}}{\partial t} &= \mathbf{f}(v, \mathbf{w})
77 * \end{aligned} @f]
78 * where @f$v@f$ is the transmembrane potential, @f$\mathbf{w}@f$ is a vector of
79 * ionic model variables (which may be gating variables or ionic
80 * concentrations), @f$I_\text{ion}@f$ is the ionic current, and
81 * @f$I_\text{ext}@f$ is an externally applied current.
82 *
83 * Individual models differ in the number of state variables @f$\mathbf{w}@f$
84 * and in the expressions of @f$I_\text{ion}@f$ and @f$\mathbf{F}@f$. These are
85 * specified by classes derived from this.
86 *
87 * **References**:
88 * - [Colli Franzone, Pavarino, Scacchi
89 * (2014)](https://doi.org/10.1007/978-3-319-04801-7)
90 * - [Goktepe, Kuhl (2009)](https://doi.org/10.1007/s00466-009-0434-z)
91 *
92 * ### Numerical methods
93 *
94 * The vector of state variables @f$\mathbf{w}@f$ is partitioned into two
95 * groups, the state variables @f$\mathbf{x}@f$ (including the transmembrane
96 * potential @f$v@f$) and the gating variables @f$\mathbf{x}_g@f$. The ODE
97 * system is advanced through a Rush-Larsen scheme, in which the gating
98 * variables are updated through an analytical expression and the state
99 * variables through a time-stepping scheme.
100 *
101 * This class currently supports forward Euler (FE), fourth-order explicit
102 * Runge-Kutta (RK) and Crank-Nicolson (CN) for advancing the state variables.
103 * Refer to the documentation of the functions integ_fe, integ_rk and integ_cn2
104 * for details on the individual methods. Beware that the CN method is only
105 * supported for ionic models that implement the getj method, and an exception
106 * is raised otherwise.
107 *
108 * ### Implementing concrete ionic models
109 *
110 * To implement a new ionic model, the following steps need to be taken:
111 *
112 * 1. Create a class derived from @ref IonicModel.
113 * 2. Override the methods @ref init, @ref update_g, @ref getf for that ionic
114 * model. If implicit solvers are desired, also override @ref getj.
115 * 3. Create a class derived from @ref IonicModelParameters to manage the
116 * parameters specific to the new ionic model.
117 * 4. Override the methods @ref get_parameters, @ref read_parameters and @ref
118 * distribute_parameters to manage the parameters of the new ionic model.
119 * 5. Override the @ref get_calcium_index method to return the index of the
120 * calcium proxy variable.
121 * 6. Register the new class into the ionic model factory by using the macro
122 * @ref REGISTER_IONIC_MODEL. The macro should be called in a `.cpp`
123 * file, not in a header file.
124 * 7. Edit the files `CepMod.h` and `CepMod.cpp` to add the label for the new
125 * ionic model type.
126 */
128public:
129 /// Alias for initial states vector. Each initial state is a pair of a label
130 /// for that state variable and its initial value.
131 /// @todo[michelebucelli] This would work better with a struct, due to the
132 /// fields having meaningful names instead of first and second.
133 using InitialStates = std::vector<std::pair<std::string, double>>;
134
135 /// Constructor.
136 IonicModel(const InitialStates &initial_X_, const InitialStates &initial_Xg_,
137 const double Vrest_)
138 : initial_X(initial_X_), initial_Xg(initial_Xg_), Vrest(Vrest_),
139 Vscale(1.0), Tscale(1.0), Voffset(0.0) {}
140
141 /// Constructor with scaling factors.
142 IonicModel(const InitialStates &initial_X_, const InitialStates &initial_Xg_,
143 const double Vrest_, const double Vscale_, const double Tscale_,
144 const double Voffset_)
145 : initial_X(initial_X_), initial_Xg(initial_Xg_), Vrest(Vrest_),
146 Vscale(Vscale_), Tscale(Tscale_), Voffset(Voffset_) {}
147
148 /// Virtual destructor.
149 virtual ~IonicModel() = default;
150
151 /**
152 * @brief Construct an instance of model parameters for this model.
153 */
154 virtual std::unique_ptr<IonicModelParameters> get_parameters() const {
155 return nullptr;
156 };
157
158 /**
159 * @brief Read model parameters from a parameter object.
160 *
161 * By default, this method only takes care of the parameters related to
162 * initial states. If a derived ionic model has other parameters, then this
163 * method should be overridden to read those as well.
164 */
165 virtual void read_parameters(const IonicModelParameters &params);
166
167 /**
168 * @brief Distribute model parameters to all parallel processes.
169 *
170 * By default, this method only takes care of the parameters related to
171 * initial states. If a derived ionic model has other parameters, then this
172 * method should be overridden to distribute those as well.
173 */
174 virtual void distribute_parameters(const CmMod &cm_mod, const cmType &cm);
175
176 /**
177 * @brief Setup model initial conditions.
178 *
179 * @param[out] X Vector of state variables to be initialized.
180 * @param[out] Xg Vector of gating variables to be initialized.
181 */
182 void init(Vector<double> &X, Vector<double> &Xg) const;
183
184 /**
185 * @brief Integrate over one time step.
186 *
187 * @param[in] ode_solver_params ODE solver parameters structure, including
188 * the solver method and the stopping criterion information.
189 * @param[in] zone_id Identifier for the transmural zone (epicardium,
190 * endocardium, myocardium).
191 * @param[in] t Current time.
192 * @param[in] dt Integration time step.
193 * @param[in] Istim Amplitude of the stimulus current at the current time.
194 * @param[in] Ksac Amplitude of the stretch-activated current.
195 * @param[in,out] X Vector of state variables to be updated.
196 * @param[in,out] Xg Vector of gating variables to be updated.
197 */
198 void integ(const odeType &ode_solver_params, const int zone_id,
199 const double t, const double dt, const double Istim,
200 const double Ksac, Vector<double> &X, Vector<double> &Xg) const;
201
202 /**
203 * @brief Get the number of state variables.
204 */
205 unsigned int nX() const { return initial_X.size(); }
206
207 /**
208 * @brief Get the number of gating variables.
209 */
210 unsigned int nG() const { return initial_Xg.size(); }
211
212 /**
213 * @brief Get the index of the intracellular calcium concentration in the
214 * state vector.
215 *
216 * This is the index of the variable to be used for electromechanics coupling.
217 */
218 virtual unsigned int get_calcium_index() const = 0;
219
220 /**
221 * @brief Get a list of state variables to export to VTU.
222 *
223 * By default, this function returns the calcium index as the only exported
224 * state variable. Derived classes can override this to export additional
225 * states if needed. Beware that, for phenomenological models, the calcium
226 * variable might actually be a proxy for calcium, rather than the actual
227 * concentration (and in particular it might be non-dimensional or have
228 * different units than a molar concentration).
229 *
230 * @return A vector of pairs {variable_name, state_vector_index}. The vector
231 * need not include the transmembrane potential V, which is exported by other
232 * means.
233 */
234 virtual std::vector<std::pair<std::string, int>>
236 return {{"Calcium", get_calcium_index()}};
237 }
238
239 /**
240 * @brief Get output variable information for output registration.
241 */
242 std::vector<outputType> get_registered_outputs() const;
243
244protected:
245 /**
246 * @name Integration methods.
247 * @{
248 */
249
250 /**
251 * @brief Integrate the model with the forward Euler method.
252 *
253 * @f[ \begin{aligned}
254 * \mathbf{x}^{n+1} &=
255 * \mathbf{x}^n + \Delta t \mathbf{f}(\mathbf{x}^n, \mathbf{x}_g^n) \\
256 * \mathbf{x}_g^{n+1} &=
257 * \texttt{update_g}(\Delta t, \mathbf{x}^n, \mathbf{x}_g^n)
258 * \end{aligned} @f]
259 *
260 * @param[in] zone_id Identifier for the transmural zone (epicardium,
261 * endocardium, myocardium).
262 * @param[in,out] X Vector of state variables to be updated.
263 * @param[in,out] Xg Vector of gating variables to be updated.
264 * @param[in] Ts Current time.
265 * @param[in] Ti Time step.
266 * @param[in] Istim Applied current.
267 * @param[in] Ksac Stretch-activated current coefficient.
268 */
269 void integ_fe(const unsigned int zone_id, Vector<double> &X,
270 Vector<double> &Xg, const double Ts, const double Ti,
271 const double Istim, const double Ksac) const;
272
273 /**
274 * @brief Integrate the model with the fourth-order explicit Runge-Kutta
275 * method.
276 *
277 * @f[ \begin{aligned}
278 * \mathbf{x}^{(1)} &= \mathbf{x}^n \\
279 * \mathbf{f}^{(1)} &= \mathbf{f}(\mathbf{x}^{(1)}, \mathbf{x}_g^n) \\
280 * \mathbf{x}_g^{(1)} &=
281 * \texttt{update_g}(\Delta t/2, \mathbf{x}^{(1)}, \mathbf{x}_g^n) \\
282 * \\
283 * \mathbf{x}^{(2)} &= \mathbf{x}^n + \frac{\Delta t}{2}\mathbf{f}^{(1)} \\
284 * \mathbf{f}^{(2)} &= \mathbf{f}(\mathbf{x}^{(2)}, \mathbf{x}_g^{(1)}) \\
285 * \\
286 * \mathbf{x}^{(3)} &= \mathbf{x}^n + \frac{\Delta t}{2}\mathbf{f}^{(2)} \\
287 * \mathbf{f}^{(3)} &= \mathbf{f}(\mathbf{x}^{(3)}, \mathbf{x}_g^{(1)}) \\
288 * \\
289 * \mathbf{x}_g^{(4)} &=
290 * \texttt{update_g}(\Delta t, \mathbf{x}^{(1)}, \mathbf{x}_g^{(1)}) \\
291 * \mathbf{x}^{(4)} &= \mathbf{x} + \Delta t \mathbf{f}^{(3)} \\
292 * \mathbf{f}^{(4)} &= \mathbf{f}(\mathbf{x}^{(4)}, \mathbf{x}_g^{(4)}) \\
293 * \\
294 * \mathbf{x}^{n+1} &= \mathbf{x} + \frac{\Delta t}{6} \left(
295 * \mathbf{f}^{(1)} + 2\mathbf{f}^{(2)} +
296 * 2\mathbf{f}^{(3)} + \mathbf{f}^{(4)} \right) \\
297 * \mathbf{x}_g^{n+1} &= \mathbf{x}_g^{(4)}
298 * \end{aligned} @f]
299 *
300 * @param[in] zone_id Identifier for the transmural zone (epicardium,
301 * endocardium, myocardium).
302 * @param[in,out] X Vector of state variables to be updated.
303 * @param[in,out] Xg Vector of gating variables to be updated.
304 * @param[in] Ts Current time.
305 * @param[in] Ti Time step.
306 * @param[in] Istim Applied current.
307 * @param[in] Ksac Stretch-activated current coefficient.
308 */
309 void integ_rk(const unsigned int zone_id, Vector<double> &X,
310 Vector<double> &Xg, const double Ts, const double Ti,
311 const double Istim, const double Ksac) const;
312
313 /**
314 * @brief Integrate the model with the Crank-Nicolson method.
315 *
316 * The state variables @f$\mathbf{x}@f$ are updated by solving the following
317 * non-linear problem:
318 * @f[
319 * \frac{\mathbf{x}^{n+1} - \mathbf{x}^n}{\Delta t} = \frac{1}{2} \left(
320 * \mathbf{f}(\mathbf{x}^n, \mathbf{x}_g^n) +
321 * \mathbf{f}(\mathbf{x}^{n+1}, \mathbf{x}_g^n) \right)
322 * @f]
323 * The problem is solved through Newton's method. Subsequently, the gating
324 * variables are updated:
325 * @f[
326 * \mathbf{x}_g^{n+1} =
327 * \texttt{update_g}(\Delta t, \mathbf{x}^{n+1}, \mathbf{x}_g^n)
328 * @f]
329 *
330 * Since this method involves solving a nonlinear system of equations, it is
331 * only available for those derived classes that implement the Jacobian matrix
332 * of the system through getj. An exception will be raised otherwise.
333 *
334 * @param[in] zone_id Identifier for the transmural zone (epicardium,
335 * endocardium, myocardium).
336 * @param[in,out] X Vector of state variables to be updated.
337 * @param[in,out] Xg Vector of gating variables to be updated.
338 * @param[in] Ts Current time.
339 * @param[in] Ti Time step.
340 * @param[in] Istim Applied current.
341 * @param[in] Ksac Stretch-activated current coefficient.
342 * @param[in] max_iter Maximum number of Newton iterations. Beware that no
343 * error is raised if the maximum number of iterations is reached, so that
344 * the solution might be affected by the truncation of the iterations.
345 * @param[in] rtol Relative tolerance for the Newton method.
346 * @param[in] atol Absolute tolerance for the Newton method.
347 */
348 void integ_cn2(const unsigned int zone_id, Vector<double> &X,
349 Vector<double> &Xg, const double Ts, const double Ti,
350 const double Istim, const double Ksac,
351 const unsigned int max_iter, const double rtol,
352 const double atol) const;
353
354 /**
355 * @}
356 */
357
358 /**
359 * @brief Update variables with analytical solution.
360 *
361 * @param[in] zone_id Identifier for the transmural zone (epicardium,
362 * endocardium, myocardium).
363 * @param[in] dt Time step.
364 * @param[in] X Vector of state variables.
365 * @param[out] Xg Vector of gating variables.
366 */
367 virtual void update_g(const unsigned int zone_id, const double dt,
368 const Vector<double> &X, Vector<double> &Xg) const = 0;
369
370 /**
371 * @brief Model right hand side.
372 *
373 * Defines the right-hand side function for the potential and ionic equations.
374 * Must be ovverridden in derived classes.
375 *
376 * @param[in] zone_id Identifier for the transmural zone (epicardium,
377 * endocardium, myocardium).
378 * @param[in] X Vector of state variables.
379 * @param[in] Xg Vector of gating variables.
380 * @param[in] I_stim Applied current.
381 * @param[in] I_sac Stretch-activated current.
382 *
383 * @return A vector containing the right-hand side of the model equations.
384 */
385 virtual Vector<double> getf(const unsigned int zone_id,
386 const Vector<double> &X, const Vector<double> &Xg,
387 const double I_stim,
388 const double I_sac) const = 0;
389
390 /**
391 * @brief Model jacobian.
392 *
393 * Defines the jacobian matrix of the model equations, that is the matirx of
394 * derivatives of the function evaulated by getf.
395 *
396 * @param[in] zone_id Identifier for the transmural zone (epicardium,
397 * endocardium, myocardium).
398 * @param[in] X Vector of state variables.
399 * @param[in] Xg Vector of gating variables.
400 * @param[in] Ksac Stretch-activated current coefficient.
401 *
402 * @return A matrix containing the jacobian of the model equations.
403 */
404 virtual Array<double> getj(const unsigned int zone_id,
405 const Vector<double> &X, const Vector<double> &Xg,
406 const double Ksac) const {
407 svmp::raise<svmp::FE::NotImplementedException>(
408 "getj method not implemented for this ionic model.");
409
410 // Dummy return statement to avoid compiler warnings.
411 Array<double> dummy(X.size(), X.size());
412 return dummy;
413 }
414
415 /// Initial states.
417
418 /// Initial gating variables.
420
421 /// Resting transmembrane potential. It is used to define the
422 /// stretch-activated current.
423 const double Vrest;
424
425 /**
426 * @name Scaling factors.
427 *
428 * Individual ionic models may need to rescale the time or voltage variable,
429 * e.g. to bring them into dimensionless form. These are the factors used for
430 * that purpose. They are assigned in the constructor of this class.
431 *
432 * @{
433 */
434
435 /// Voltage scaling [mV].
436 const double Vscale;
437
438 /// Time scaling [ms].
439 const double Tscale;
440
441 /// Voltage offset parameter [mV].
442 const double Voffset;
443
444 /**
445 * @}
446 */
447};
448
449/**
450 * @brief Alias for the ionic model factory.
451 *
452 * See the documentation for @ref Factory for more details on how this works.
453 */
455
456/**
457 * @brief Macro to register a ionic model in the factory.
458 */
459#define REGISTER_IONIC_MODEL(name, type) \
460 REGISTER_IN_FACTORY(IonicModel, type, name)
461
462#endif
Exception hierarchy for error handling in the FE library.
The CmMod class duplicates the data structures in the Fortran CMMOD module defined in COMU....
Definition CmMod.h:36
Abstract self-registering factory.
Definition factory.h:30
Abstract ionic model class.
Definition IonicModel.h:127
void integ_rk(const unsigned int zone_id, Vector< double > &X, Vector< double > &Xg, const double Ts, const double Ti, const double Istim, const double Ksac) const
Integrate the model with the fourth-order explicit Runge-Kutta method.
Definition IonicModel.cpp:185
IonicModel(const InitialStates &initial_X_, const InitialStates &initial_Xg_, const double Vrest_)
Constructor.
Definition IonicModel.h:136
InitialStates initial_Xg
Initial gating variables.
Definition IonicModel.h:419
const double Vscale
Voltage scaling [mV].
Definition IonicModel.h:436
void integ_fe(const unsigned int zone_id, Vector< double > &X, Vector< double > &Xg, const double Ts, const double Ti, const double Istim, const double Ksac) const
Integrate the model with the forward Euler method.
Definition IonicModel.cpp:156
virtual unsigned int get_calcium_index() const =0
Get the index of the intracellular calcium concentration in the state vector.
void init(Vector< double > &X, Vector< double > &Xg) const
Setup model initial conditions.
Definition IonicModel.cpp:41
unsigned int nX() const
Get the number of state variables.
Definition IonicModel.h:205
std::vector< std::pair< std::string, double > > InitialStates
Definition IonicModel.h:133
virtual void read_parameters(const IonicModelParameters &params)
Read model parameters from a parameter object.
Definition IonicModel.cpp:23
virtual Array< double > getj(const unsigned int zone_id, const Vector< double > &X, const Vector< double > &Xg, const double Ksac) const
Model jacobian.
Definition IonicModel.h:404
const double Vrest
Definition IonicModel.h:423
virtual void update_g(const unsigned int zone_id, const double dt, const Vector< double > &X, Vector< double > &Xg) const =0
Update variables with analytical solution.
const double Voffset
Voltage offset parameter [mV].
Definition IonicModel.h:442
virtual ~IonicModel()=default
Virtual destructor.
IonicModel(const InitialStates &initial_X_, const InitialStates &initial_Xg_, const double Vrest_, const double Vscale_, const double Tscale_, const double Voffset_)
Constructor with scaling factors.
Definition IonicModel.h:142
virtual Vector< double > getf(const unsigned int zone_id, const Vector< double > &X, const Vector< double > &Xg, const double I_stim, const double I_sac) const =0
Model right hand side.
void integ(const odeType &ode_solver_params, const int zone_id, const double t, const double dt, const double Istim, const double Ksac, Vector< double > &X, Vector< double > &Xg) const
Integrate over one time step.
Definition IonicModel.cpp:59
unsigned int nG() const
Get the number of gating variables.
Definition IonicModel.h:210
virtual void distribute_parameters(const CmMod &cm_mod, const cmType &cm)
Distribute model parameters to all parallel processes.
Definition IonicModel.cpp:33
virtual std::vector< std::pair< std::string, int > > get_output_variables() const
Get a list of state variables to export to VTU.
Definition IonicModel.h:235
const double Tscale
Time scaling [ms].
Definition IonicModel.h:439
std::vector< outputType > get_registered_outputs() const
Get output variable information for output registration.
Definition IonicModel.cpp:240
virtual std::unique_ptr< IonicModelParameters > get_parameters() const
Construct an instance of model parameters for this model.
Definition IonicModel.h:154
InitialStates initial_X
Initial states.
Definition IonicModel.h:416
void integ_cn2(const unsigned int zone_id, Vector< double > &X, Vector< double > &Xg, const double Ts, const double Ti, const double Istim, const double Ksac, const unsigned int max_iter, const double rtol, const double atol) const
Integrate the model with the Crank-Nicolson method.
Definition IonicModel.cpp:84
Parameters for a generic ionic model.
Definition Parameters.h:1344
The Vector template class is used for storing int and double data.
Definition Vector.h:26
The cmType class stores data and defines methods used for mpi communication.
Definition CmMod.h:56
Time integration scheme and related parameters.
Definition IonicModel.h:50
double absTol
Absolute tolerance.
Definition IonicModel.h:61
double relTol
Relative tolerance.
Definition IonicModel.h:64
TimeIntegrationType tIntType
Time integration method type.
Definition IonicModel.h:55
int maxItr
Max. iterations for Newton-Raphson method.
Definition IonicModel.h:58
Declared type for outputed variables.
Definition ComMod.h:588