Nektar++
Monodomain.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: Monodomain.cpp
4//
5// For more information, please see: http://www.nektar.info
6//
7// The MIT License
8//
9// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10// Department of Aeronautics, Imperial College London (UK), and Scientific
11// Computing and Imaging Institute, University of Utah (USA).
12//
13// Permission is hereby granted, free of charge, to any person obtaining a
14// copy of this software and associated documentation files (the "Software"),
15// to deal in the Software without restriction, including without limitation
16// the rights to use, copy, modify, merge, publish, distribute, sublicense,
17// and/or sell copies of the Software, and to permit persons to whom the
18// Software is furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included
21// in all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29// DEALINGS IN THE SOFTWARE.
30//
31// Description: Monodomain cardiac electrophysiology homogenised model.
32//
33///////////////////////////////////////////////////////////////////////////////
34
35#include <iostream>
36
40
41using namespace std;
42
43namespace Nektar
44{
45/**
46 * @class Monodomain
47 *
48 * Base model of cardiac electrophysiology of the form
49 * \f{align*}{
50 * \frac{\partial u}{\partial t} = \nabla^2 u + J_{ion},
51 * \f}
52 * where the reaction term, \f$J_{ion}\f$ is defined by a specific cell
53 * model.
54 *
55 * This implementation, at present, treats the reaction terms explicitly
56 * and the diffusive element implicitly.
57 */
58
59/**
60 * Registers the class with the Factory.
61 */
64 "Monodomain", Monodomain::create,
65 "Monodomain model of cardiac electrophysiology.");
66
67/**
68 *
69 */
72 : UnsteadySystem(pSession, pGraph)
73{
74}
75
76/**
77 *
78 */
79void Monodomain::v_InitObject(bool DeclareField)
80{
81 UnsteadySystem::v_InitObject(DeclareField);
82
83 m_session->LoadParameter("Chi", m_chi);
84 m_session->LoadParameter("Cm", m_capMembrane);
85
86 std::string vCellModel;
87 m_session->LoadSolverInfo("CELLMODEL", vCellModel, "");
88
89 ASSERTL0(vCellModel != "", "Cell Model not specified.");
90
92 m_fields[0]);
93
94 m_intVariables.push_back(0);
95
96 // Load variable coefficients
97 StdRegions::VarCoeffType varCoeffEnum[6] = {
101 std::string varCoeffString[6] = {"xx", "xy", "yy", "xz", "yz", "zz"};
102 std::string aniso_var[3] = {"fx", "fy", "fz"};
103
104 const int nq = m_fields[0]->GetNpoints();
105 const int nVarDiffCmpts = m_spacedim * (m_spacedim + 1) / 2;
106
107 // Allocate storage for variable coeffs and initialize to 1.
108 for (int i = 0, k = 0; i < m_spacedim; ++i)
109 {
110 for (int j = 0; j < i + 1; ++j)
111 {
112 if (i == j)
113 {
114 m_vardiff[varCoeffEnum[k]] = Array<OneD, NekDouble>(nq, 1.0);
115 }
116 else
117 {
118 m_vardiff[varCoeffEnum[k]] = Array<OneD, NekDouble>(nq, 0.0);
119 }
120 ++k;
121 }
122 }
123
124 // Apply fibre map f \in [0,1], scale to conductivity range
125 // [o_min,o_max], specified by the session parameters o_min and o_max
126 if (m_session->DefinesFunction("AnisotropicConductivity"))
127 {
128 if (m_session->DefinesCmdLineArgument("verbose"))
129 {
130 cout << "Loading Anisotropic Fibre map." << endl;
131 }
132
133 NekDouble o_min = m_session->GetParameter("o_min");
134 NekDouble o_max = m_session->GetParameter("o_max");
135 int k = 0;
136
139
140 /*
141 * Diffusivity matrix D is upper triangular and defined as
142 * d_00 d_01 d_02
143 * d_11 d_12
144 * d_22
145 *
146 * Given a principle fibre direction _f_ the diffusivity is given
147 * by
148 * d_ij = { D_2 + (D_1 - D_2) f_i f_j if i==j
149 * { (D_1 - D_2) f_i f_j if i!=j
150 *
151 * The vector _f_ is given in terms of the variables fx,fy,fz in the
152 * function AnisotropicConductivity. The values of D_1 and D_2 are
153 * the parameters o_max and o_min, respectively.
154 */
155
156 // Loop through columns of D
157 for (int j = 0; j < m_spacedim; ++j)
158 {
159 ASSERTL0(m_session->DefinesFunction("AnisotropicConductivity",
160 aniso_var[j]),
161 "Function 'AnisotropicConductivity' not correctly "
162 "defined.");
163 GetFunction("AnisotropicConductivity")
164 ->Evaluate(aniso_var[j], vTemp_j);
165
166 // Loop through rows of D
167 for (int i = 0; i < j + 1; ++i)
168 {
169 ASSERTL0(m_session->DefinesFunction("AnisotropicConductivity",
170 aniso_var[i]),
171 "Function 'AnisotropicConductivity' not correctly "
172 "defined.");
173 GetFunction("AnisotropicConductivity")
174 ->Evaluate(aniso_var[i], vTemp_i);
175
176 Array<OneD, NekDouble> tmp(vTemp_i.size());
177
178 Vmath::Vmul(nq, vTemp_i, 1, vTemp_j, 1, tmp, 1);
179 Vmath::Smul(nq, o_max - o_min, tmp, 1, tmp, 1);
180
181 if (i == j)
182 {
183 Vmath::Sadd(nq, o_min, tmp, 1, tmp, 1);
184 }
185
186 m_vardiff[varCoeffEnum[k]] = tmp;
187 ++k;
188 }
189 }
190 }
191 else
192 {
193 // Otherwise apply isotropic conductivity value (o_max) to
194 // diagonal components of tensor
195 NekDouble o_max = m_session->GetParameter("o_max");
196 for (int i = 0; i < nVarDiffCmpts; ++i)
197 {
198 Array<OneD, NekDouble> tmp(m_vardiff[varCoeffEnum[i]].GetValue());
199 Vmath::Smul(nq, o_max, tmp, 1, tmp, 1);
200 m_vardiff[varCoeffEnum[i]] = tmp;
201 }
202 }
203
204 // Scale by scar map (range 0->1) derived from intensity map
205 // (range d_min -> d_max)
206 if (m_session->DefinesFunction("IsotropicConductivity"))
207 {
208 if (m_session->DefinesCmdLineArgument("verbose"))
209 {
210 cout << "Loading Isotropic Conductivity map." << endl;
211 }
212
213 const std::string varName = "intensity";
215 GetFunction("IsotropicConductivity")->Evaluate(varName, vTemp);
216
217 // If the d_min and d_max parameters are defined, then we need to
218 // rescale the isotropic conductivity to convert from the source
219 // domain (e.g. late-gad intensity) to conductivity
220 if (m_session->DefinesParameter("d_min") ||
221 m_session->DefinesParameter("d_max"))
222 {
223 const NekDouble f_min = m_session->GetParameter("d_min");
224 const NekDouble f_max = m_session->GetParameter("d_max");
225 const NekDouble scar_min = 0.0;
226 const NekDouble scar_max = 1.0;
227
228 // Threshold based on d_min, d_max
229 for (int j = 0; j < nq; ++j)
230 {
231 vTemp[j] = (vTemp[j] < f_min ? f_min : vTemp[j]);
232 vTemp[j] = (vTemp[j] > f_max ? f_max : vTemp[j]);
233 }
234
235 // Rescale to s \in [0,1] (0 maps to d_max, 1 maps to d_min)
236 Vmath::Sadd(nq, -f_min, vTemp, 1, vTemp, 1);
237 Vmath::Smul(nq, -1.0 / (f_max - f_min), vTemp, 1, vTemp, 1);
238 Vmath::Sadd(nq, 1.0, vTemp, 1, vTemp, 1);
239 Vmath::Smul(nq, scar_max - scar_min, vTemp, 1, vTemp, 1);
240 Vmath::Sadd(nq, scar_min, vTemp, 1, vTemp, 1);
241 }
242
243 // Scale anisotropic conductivity values
244 for (int i = 0; i < nVarDiffCmpts; ++i)
245 {
246 Array<OneD, NekDouble> tmp = m_vardiff[varCoeffEnum[i]].GetValue();
247 Vmath::Vmul(nq, vTemp, 1, tmp, 1, tmp, 1);
248 m_vardiff[varCoeffEnum[i]] = tmp;
249 }
250 }
251
252 // Write out conductivity values
253 for (int j = 0, k = 0; j < m_spacedim; ++j)
254 {
255 // Loop through rows of D
256 for (int i = 0; i < j + 1; ++i)
257 {
258 // Transform variable coefficient and write out to file.
259 m_fields[0]->FwdTransLocalElmt(
260 m_vardiff[varCoeffEnum[k]].GetValue(),
261 m_fields[0]->UpdateCoeffs());
262 std::stringstream filename;
263 filename << "Conductivity_" << varCoeffString[k] << ".fld";
264 WriteFld(filename.str());
265
266 ++k;
267 }
268 }
269
270 // Search through the loaded filters and pass the cell model to any
271 // CheckpointCellModel filters loaded.
272 for (auto &x : m_filters)
273 {
274 if (x.first == "CheckpointCellModel")
275 {
276 std::shared_ptr<FilterCheckpointCellModel> c =
277 std::dynamic_pointer_cast<FilterCheckpointCellModel>(x.second);
278 c->SetCellModel(m_cell);
279 }
280 if (x.first == "CellHistoryPoints")
281 {
282 std::shared_ptr<FilterCellHistoryPoints> c =
283 std::dynamic_pointer_cast<FilterCellHistoryPoints>(x.second);
284 c->SetCellModel(m_cell);
285 }
286 }
287
288 // Load stimuli
290
292 {
294 }
297}
298
299/**
300 *
301 */
303{
304}
305
306/**
307 * @param inarray Input array.
308 * @param outarray Output array.
309 * @param time Current simulation time.
310 * @param lambda Timestep.
311 */
313 const Array<OneD, const Array<OneD, NekDouble>> &inarray,
315 [[maybe_unused]] const NekDouble time, const NekDouble lambda)
316{
317 int nvariables = inarray.size();
318 int nq = m_fields[0]->GetNpoints();
320 // lambda = \Delta t
322
323 // We solve ( \nabla^2 - HHlambda ) Y[i] = rhs [i]
324 // inarray = input: \hat{rhs} -> output: \hat{Y}
325 // outarray = output: nabla^2 \hat{Y}
326 // where \hat = modal coeffs
327 for (int i = 0; i < nvariables; ++i)
328 {
329 // Multiply 1.0/timestep
330 Vmath::Smul(nq, -factors[StdRegions::eFactorLambda], inarray[i], 1,
331 m_fields[i]->UpdatePhys(), 1);
332
333 // Solve a system of equations with Helmholtz solver and transform
334 // back into physical space.
335 m_fields[i]->HelmSolve(m_fields[i]->GetPhys(),
336 m_fields[i]->UpdateCoeffs(), factors, m_vardiff);
337
338 m_fields[i]->BwdTrans(m_fields[i]->GetCoeffs(),
339 m_fields[i]->UpdatePhys());
340 m_fields[i]->SetPhysState(true);
341
342 // Copy the solution vector (required as m_fields must be set).
343 outarray[i] = m_fields[i]->GetPhys();
344 }
345}
346
347/**
348 *
349 */
351 const Array<OneD, const Array<OneD, NekDouble>> &inarray,
352 Array<OneD, Array<OneD, NekDouble>> &outarray, const NekDouble time)
353{
354 // Compute I_ion
355 m_cell->TimeIntegrate(inarray, outarray, time);
356
357 // Compute I_stim
358 for (unsigned int i = 0; i < m_stimulus.size(); ++i)
359 {
360 m_stimulus[i]->Update(outarray, time);
361 }
362}
363
364/**
365 *
366 */
368 bool dumpInitialConditions,
369 const int domain)
370{
371 EquationSystem::v_SetInitialConditions(initialtime, dumpInitialConditions,
372 domain);
373 m_cell->Initialise();
374}
375
376/**
377 *
378 */
380{
382 if (m_session->DefinesFunction("d00") &&
383 m_session->GetFunctionType("d00", "intensity") ==
385 {
387 s, "Diffusivity-x",
388 m_session->GetFunction("d00", "intensity")->GetExpression());
389 }
390 if (m_session->DefinesFunction("d11") &&
391 m_session->GetFunctionType("d11", "intensity") ==
393 {
395 s, "Diffusivity-y",
396 m_session->GetFunction("d11", "intensity")->GetExpression());
397 }
398 if (m_session->DefinesFunction("d22") &&
399 m_session->GetFunctionType("d22", "intensity") ==
401 {
403 s, "Diffusivity-z",
404 m_session->GetFunction("d22", "intensity")->GetExpression());
405 }
406 m_cell->GenerateSummary(s);
407}
408} // namespace Nektar
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:208
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
tBaseSharedPtr CreateInstance(tKey idKey, tParam... args)
Create an instance of the class referred to by idKey.
void DefineProjection(FuncPointerT func, ObjectPointerT obj)
void DefineOdeRhs(FuncPointerT func, ObjectPointerT obj)
void DefineImplicitSolve(FuncPointerT func, ObjectPointerT obj)
void DoImplicitSolve(const Array< OneD, const Array< OneD, NekDouble > > &inarray, Array< OneD, Array< OneD, NekDouble > > &outarray, NekDouble time, NekDouble lambda)
Solve for the diffusion term.
Definition: Monodomain.cpp:312
void v_SetInitialConditions(NekDouble initialtime, bool dumpInitialConditions, const int domain) override
Sets a custom initial condition.
Definition: Monodomain.cpp:367
void DoOdeRhs(const Array< OneD, const Array< OneD, NekDouble > > &inarray, Array< OneD, Array< OneD, NekDouble > > &outarray, const NekDouble time)
Computes the reaction terms and .
Definition: Monodomain.cpp:350
Monodomain(const LibUtilities::SessionReaderSharedPtr &pSession, const SpatialDomains::MeshGraphSharedPtr &pGraph)
Constructor.
Definition: Monodomain.cpp:70
~Monodomain() override
Desctructor.
Definition: Monodomain.cpp:302
CellModelSharedPtr m_cell
Cell model.
Definition: Monodomain.h:98
StdRegions::VarCoeffMap m_vardiff
Variable diffusivity.
Definition: Monodomain.h:103
static EquationSystemSharedPtr create(const LibUtilities::SessionReaderSharedPtr &pSession, const SpatialDomains::MeshGraphSharedPtr &pGraph)
Creates an instance of this class.
Definition: Monodomain.h:54
NekDouble m_capMembrane
Definition: Monodomain.h:106
void v_InitObject(bool DeclareField=true) override
Init object for UnsteadySystem class.
Definition: Monodomain.cpp:79
std::vector< StimulusSharedPtr > m_stimulus
Definition: Monodomain.h:100
static std::string className
Name of class.
Definition: Monodomain.h:65
void v_GenerateSummary(SummaryList &s) override
Prints a summary of the model parameters.
Definition: Monodomain.cpp:379
int m_spacedim
Spatial dimension (>= expansion dim).
virtual SOLVER_UTILS_EXPORT void v_SetInitialConditions(NekDouble initialtime=0.0, bool dumpInitialConditions=true, const int domain=0)
Array< OneD, MultiRegions::ExpListSharedPtr > m_fields
Array holding all dependent variables.
SOLVER_UTILS_EXPORT void WriteFld(const std::string &outname)
Write field data to the given filename.
LibUtilities::SessionReaderSharedPtr m_session
The session reader.
SOLVER_UTILS_EXPORT SessionFunctionSharedPtr GetFunction(std::string name, const MultiRegions::ExpListSharedPtr &field=MultiRegions::NullExpListSharedPtr, bool cache=false)
Get a SessionFunction by name.
Base class for unsteady solvers.
LibUtilities::TimeIntegrationSchemeOperators m_ode
The time integration scheme operators to use.
std::vector< std::pair< std::string, FilterSharedPtr > > m_filters
SOLVER_UTILS_EXPORT void DoDummyProjection(const Array< OneD, const Array< OneD, NekDouble > > &inarray, Array< OneD, Array< OneD, NekDouble > > &outarray, const NekDouble time)
Perform dummy projection.
bool m_explicitDiffusion
Indicates if explicit or implicit treatment of diffusion is used.
SOLVER_UTILS_EXPORT void v_GenerateSummary(SummaryList &s) override
Print a summary of time stepping parameters.
SOLVER_UTILS_EXPORT void v_InitObject(bool DeclareField=true) override
Init object for UnsteadySystem class.
static std::vector< StimulusSharedPtr > LoadStimuli(const LibUtilities::SessionReaderSharedPtr &pSession, const MultiRegions::ExpListSharedPtr &pField)
Definition: Stimulus.cpp:89
std::shared_ptr< SessionReader > SessionReaderSharedPtr
std::vector< std::pair< std::string, std::string > > SummaryList
Definition: Misc.h:46
EquationSystemFactory & GetEquationSystemFactory()
void AddSummaryItem(SummaryList &l, const std::string &name, const std::string &value)
Adds a summary item to the summary info list.
Definition: Misc.cpp:47
std::shared_ptr< MeshGraph > MeshGraphSharedPtr
Definition: MeshGraph.h:174
std::map< ConstFactorType, NekDouble > ConstFactorMap
Definition: StdRegions.hpp:430
StdRegions::ConstFactorMap factors
CellModelFactory & GetCellModelFactory()
Definition: CellModel.cpp:46
double NekDouble
void Vmul(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Multiply vector z = x*y.
Definition: Vmath.hpp:72
void Smul(int n, const T alpha, const T *x, const int incx, T *y, const int incy)
Scalar multiply y = alpha*x.
Definition: Vmath.hpp:100
void Sadd(int n, const T alpha, const T *x, const int incx, T *y, const int incy)
Add vector y = alpha + x.
Definition: Vmath.hpp:194
STL namespace.