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.
Definition: NekFactory.hpp:197
tBaseSharedPtr CreateInstance(tKey idKey, tParam... args)
Create an instance of the class referred to by idKey.
Definition: NekFactory.hpp:143
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:402
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