Nektar++
BidomainRoth.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: BidomainRoth.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: Bidomain cardiac electrophysiology model - Roth formulation.
32//
33///////////////////////////////////////////////////////////////////////////////
34
35#include <iostream>
36
39
40using namespace std;
41
42namespace Nektar
43{
44
45/**
46 * Registers the class with the Factory.
47 */
50 "BidomainRoth", BidomainRoth::create,
51 "Bidomain Roth model of cardiac electrophysiology.");
52
53/**
54 *
55 */
58 : UnsteadySystem(pSession, pGraph)
59{
60}
61
62/**
63 *
64 */
65void BidomainRoth::v_InitObject(bool DeclareField)
66{
67 UnsteadySystem::v_InitObject(DeclareField);
68
69 m_session->LoadParameter("Chi", m_chi);
70 m_session->LoadParameter("Cm", m_capMembrane);
71
72 std::string vCellModel;
73 m_session->LoadSolverInfo("CELLMODEL", vCellModel, "");
74
75 ASSERTL0(vCellModel != "", "Cell Model not specified.");
76
78 m_fields[0]);
79
80 m_intVariables.push_back(0);
81
82 // Load variable coefficients
83 StdRegions::VarCoeffType varCoeffEnum[6] = {
87 std::string varCoeffString[6] = {"xx", "xy", "yy", "xz", "yz", "zz"};
88 std::string aniso_var[3] = {"fx", "fy", "fz"};
89
90 const int nq = m_fields[0]->GetNpoints();
91
92 // Allocate storage for variable coeffs and initialize to 1.
93 for (int i = 0, k = 0; i < m_spacedim; ++i)
94 {
95 for (int j = 0; j < i + 1; ++j)
96 {
97 if (i == j)
98 {
99 m_vardiffi[varCoeffEnum[k]] = Array<OneD, NekDouble>(nq, 1.0);
100 m_vardiffe[varCoeffEnum[k]] = Array<OneD, NekDouble>(nq, 1.0);
101 m_vardiffie[varCoeffEnum[k]] = Array<OneD, NekDouble>(nq, 1.0);
102 }
103 else
104 {
105 m_vardiffi[varCoeffEnum[k]] = Array<OneD, NekDouble>(nq, 0.0);
106 m_vardiffe[varCoeffEnum[k]] = Array<OneD, NekDouble>(nq, 0.0);
107 m_vardiffie[varCoeffEnum[k]] = Array<OneD, NekDouble>(nq, 0.0);
108 }
109 ++k;
110 }
111 }
112
113 // Apply fibre map f \in [0,1], scale to conductivity range
114 // [o_min,o_max], specified by the session parameters o_min and o_max
115 if (m_session->DefinesFunction("ExtracellularAnisotropicConductivity"))
116 {
117 if (m_session->DefinesCmdLineArgument("verbose"))
118 {
119 cout << "Loading Extracellular Anisotropic Fibre map." << endl;
120 }
121
122 NekDouble o_min = m_session->GetParameter("o_min");
123 NekDouble o_max = m_session->GetParameter("o_max");
124 int k = 0;
125
128
129 /*
130 * Diffusivity matrix D is upper triangular and defined as
131 * d_00 d_01 d_02
132 * d_11 d_12
133 * d_22
134 *
135 * Given a principle fibre direction _f_ the diffusivity is given
136 * by
137 * d_ij = { D_2 + (D_1 - D_2) f_i f_j if i==j
138 * { (D_1 - D_2) f_i f_j if i!=j
139 *
140 * The vector _f_ is given in terms of the variables fx,fy,fz in the
141 * function AnisotropicConductivity. The values of D_1 and D_2 are
142 * the parameters o_max and o_min, respectively.
143 */
144
145 // Loop through columns of D
146 for (int j = 0; j < m_spacedim; ++j)
147 {
148 ASSERTL0(m_session->DefinesFunction(
149 "ExtracellularAnisotropicConductivity", aniso_var[j]),
150 "Function 'AnisotropicConductivity' not correctly "
151 "defined.");
152
153 GetFunction("ExtracellularAnisotropicConductivity")
154 ->Evaluate(aniso_var[j], vTemp_j);
155
156 // Loop through rows of D
157 for (int i = 0; i < j + 1; ++i)
158 {
159 ASSERTL0(
160 m_session->DefinesFunction(
161 "ExtracellularAnisotropicConductivity", aniso_var[i]),
162 "Function 'ExtracellularAnisotropicConductivity' not "
163 "correctly defined.");
164
165 GetFunction("ExtracellularAnisotropicConductivity")
166 ->Evaluate(aniso_var[i], vTemp_i);
168 m_vardiffe[varCoeffEnum[k]].GetValue();
169
170 Vmath::Vmul(nq, vTemp_i, 1, vTemp_j, 1, tmp, 1);
171
172 Vmath::Smul(nq, o_max - o_min, tmp, 1, tmp, 1);
173
174 if (i == j)
175 {
176 Vmath::Sadd(nq, o_min, tmp, 1, tmp, 1);
177 }
178
179 m_vardiffe[varCoeffEnum[k]] = tmp;
180 }
181 }
182 }
183
184 // Apply fibre map f \in [0,1], scale to conductivity range
185 // [o_min,o_max], specified by the session parameters o_min and o_max
186 if (m_session->DefinesFunction("IntracellularAnisotropicConductivity"))
187 {
188 if (m_session->DefinesCmdLineArgument("verbose"))
189 {
190 cout << "Loading Anisotropic Fibre map." << endl;
191 }
192
193 NekDouble o_min = m_session->GetParameter("o_min");
194 NekDouble o_max = m_session->GetParameter("o_max");
195 int k = 0;
196
199
200 /*
201 * Diffusivity matrix D is upper triangular and defined as
202 * d_00 d_01 d_02
203 * d_11 d_12
204 * d_22
205 *
206 * Given a principle fibre direction _f_ the diffusivity is given
207 * by
208 * d_ij = { D_2 + (D_1 - D_2) f_i f_j if i==j
209 * { (D_1 - D_2) f_i f_j if i!=j
210 *
211 * The vector _f_ is given in terms of the variables fx,fy,fz in the
212 * function AnisotropicConductivity. The values of D_1 and D_2 are
213 * the parameters o_max and o_min, respectively.
214 */
215
216 // Loop through columns of D
217 for (int j = 0; j < m_spacedim; ++j)
218 {
219 ASSERTL0(m_session->DefinesFunction(
220 "IntracellularAnisotropicConductivity", aniso_var[j]),
221 "Function 'IntracellularAnisotropicConductivity' not "
222 "correctly defined.");
223
224 GetFunction("IntracellularAnisotropicConductivity")
225 ->Evaluate(aniso_var[j], vTemp_j);
226
227 // Loop through rows of D
228 for (int i = 0; i < j + 1; ++i)
229 {
230 ASSERTL0(
231 m_session->DefinesFunction(
232 "IntracellularAnisotropicConductivity", aniso_var[i]),
233 "Function 'IntracellularAnisotropicConductivity' not "
234 "correctly defined.");
235 GetFunction("IntracellularAnisotropicConductivity")
236 ->Evaluate(aniso_var[i], vTemp_i);
237
239 m_vardiffi[varCoeffEnum[k]].GetValue();
241 m_vardiffe[varCoeffEnum[k]].GetValue();
242 Vmath::Vmul(nq, vTemp_i, 1, vTemp_j, 1, tmp, 1);
243
244 Vmath::Smul(nq, o_max - o_min, tmp, 1, tmp, 1);
245
246 if (i == j)
247 {
248 Vmath::Sadd(nq, o_min, tmp, 1, tmp, 1);
249 }
250
251 Vmath::Vadd(nq, tmp2, 1, tmp, 1, tmp2, 1);
252
253 m_vardiffi[varCoeffEnum[k]] = tmp;
254 m_vardiffe[varCoeffEnum[k]] = tmp2;
255
256 ++k;
257 }
258 }
259 }
260
261 // Write out conductivity values
262 for (int j = 0, k = 0; j < m_spacedim; ++j)
263 {
264 // Loop through rows of D
265 for (int i = 0; i < j + 1; ++i)
266 {
267 // Transform variable coefficient and write out to file.
268 m_fields[0]->FwdTransLocalElmt(
269 m_vardiffi[varCoeffEnum[k]].GetValue(),
270 m_fields[0]->UpdateCoeffs());
271 std::stringstream filenamei;
272 filenamei << "IConductivity_" << varCoeffString[k] << ".fld";
273 WriteFld(filenamei.str());
274
275 // Transform variable coefficient and write out to file.
276 m_fields[0]->FwdTransLocalElmt(
277 m_vardiffe[varCoeffEnum[k]].GetValue(),
278 m_fields[0]->UpdateCoeffs());
279 std::stringstream filenamee;
280 filenamee << "EConductivity_" << varCoeffString[k] << ".fld";
281 WriteFld(filenamee.str());
282
283 ++k;
284 }
285 }
286
287 // Search through the loaded filters and pass the cell model to any
288 // CheckpointCellModel filters loaded.
289 for (auto &x : m_filters)
290 {
291 if (x.first == "CheckpointCellModel")
292 {
293 std::shared_ptr<FilterCheckpointCellModel> c =
294 std::dynamic_pointer_cast<FilterCheckpointCellModel>(x.second);
295 c->SetCellModel(m_cell);
296 }
297 }
298 // Load stimuli
300
302 {
304 }
307}
308
309/**
310 *
311 */
313{
314}
315
316/**
317 * @param inarray Input array.
318 * @param outarray Output array.
319 * @param time Current simulation time.
320 * @param lambda Timestep.
321 */
323 const Array<OneD, const Array<OneD, NekDouble>> &inarray,
325 [[maybe_unused]] const NekDouble time, const NekDouble lambda)
326{
327 int nq = m_fields[0]->GetNpoints();
328
329 StdRegions::ConstFactorMap factorsHelmholtz;
330 // lambda = \Delta t
331 factorsHelmholtz[StdRegions::eFactorLambda] =
332 1.0 / lambda * m_chi * m_capMembrane;
333
334 // ------------------------------
335 // Solve Helmholtz problem for Vm
336 // ------------------------------
337 // Multiply 1.0/timestep
338 // Vmath::Vadd(nq, inarray[0], 1, ggrad, 1, m_fields[0]->UpdatePhys(), 1);
339 Vmath::Smul(nq, -factorsHelmholtz[StdRegions::eFactorLambda], inarray[0], 1,
340 m_fields[0]->UpdatePhys(), 1);
341
342 // Solve a system of equations with Helmholtz solver and transform
343 // back into physical space.
344 m_fields[0]->HelmSolve(m_fields[0]->GetPhys(), m_fields[0]->UpdateCoeffs(),
345 factorsHelmholtz, m_vardiffe);
346
347 m_fields[0]->BwdTrans(m_fields[0]->GetCoeffs(), m_fields[0]->UpdatePhys());
348 m_fields[0]->SetPhysState(true);
349
350 // Copy the solution vector (required as m_fields must be set).
351 outarray[0] = m_fields[0]->GetPhys();
352}
353
354/**
355 *
356 */
358 const Array<OneD, const Array<OneD, NekDouble>> &inarray,
359 Array<OneD, Array<OneD, NekDouble>> &outarray, const NekDouble time)
360{
361 int nq = m_fields[0]->GetNpoints();
362
363 // Compute I_ion
364 m_cell->TimeIntegrate(inarray, outarray, time);
365
366 // Compute I_stim
367 for (unsigned int i = 0; i < m_stimulus.size(); ++i)
368 {
369 m_stimulus[i]->Update(outarray, time);
370 }
371
372 Array<OneD, NekDouble> ggrad0(nq), ggrad1(nq), ggrad2(nq), ggrad(nq);
373 StdRegions::ConstFactorMap factorsPoisson;
374 factorsPoisson[StdRegions::eFactorLambda] = 0.0;
375
376 // ----------------------------
377 // Compute \nabla g_i \nabla Vm
378 // ----------------------------
379 m_fields[0]->PhysDeriv(inarray[0], ggrad0, ggrad1, ggrad2);
380 m_fields[0]->PhysDeriv(0, ggrad0, ggrad0);
381 m_fields[0]->PhysDeriv(1, ggrad1, ggrad1);
382 m_fields[0]->PhysDeriv(2, ggrad2, ggrad2);
383 if (m_session->DefinesFunction("IntracellularAnisotropicConductivity") &&
384 m_session->DefinesFunction("ExtracellularAnisotropicConductivity"))
385 {
386 Vmath::Vmul(nq, &m_vardiffi[StdRegions::eVarCoeffD00][0], 1, &ggrad0[0],
387 1, &ggrad0[0], 1);
388 Vmath::Vmul(nq, &m_vardiffi[StdRegions::eVarCoeffD11][0], 1, &ggrad1[0],
389 1, &ggrad1[0], 1);
390 Vmath::Vmul(nq, &m_vardiffi[StdRegions::eVarCoeffD22][0], 1, &ggrad2[0],
391 1, &ggrad2[0], 1);
392 }
393 // Add partial derivatives together
394 Vmath::Vadd(nq, ggrad0, 1, ggrad1, 1, ggrad, 1);
395 Vmath::Vadd(nq, ggrad2, 1, ggrad, 1, ggrad, 1);
396
397 Vmath::Smul(nq, -1.0, ggrad, 1, m_fields[1]->UpdatePhys(), 1);
398
399 // ----------------------------
400 // Solve Poisson problem for Ve
401 // ----------------------------
402 m_fields[1]->HelmSolve(m_fields[1]->GetPhys(), m_fields[1]->UpdateCoeffs(),
403 factorsPoisson, m_vardiffie);
404 m_fields[1]->BwdTrans(m_fields[1]->GetCoeffs(), m_fields[1]->UpdatePhys());
405 m_fields[1]->SetPhysState(true);
406
407 // ------------------------------
408 // Compute Laplacian of Ve (forcing term)
409 // ------------------------------
410 m_fields[1]->PhysDeriv(m_fields[1]->GetPhys(), ggrad0, ggrad1, ggrad2);
411 m_fields[1]->PhysDeriv(0, ggrad0, ggrad0);
412 m_fields[1]->PhysDeriv(1, ggrad1, ggrad1);
413 m_fields[1]->PhysDeriv(2, ggrad2, ggrad2);
414 if (m_session->DefinesFunction("IntracellularAnisotropicConductivity") &&
415 m_session->DefinesFunction("ExtracellularAnisotropicConductivity"))
416 {
417 Vmath::Vmul(nq, &m_vardiffi[StdRegions::eVarCoeffD00][0], 1, &ggrad0[0],
418 1, &ggrad0[0], 1);
419 Vmath::Vmul(nq, &m_vardiffi[StdRegions::eVarCoeffD11][0], 1, &ggrad1[0],
420 1, &ggrad1[0], 1);
421 Vmath::Vmul(nq, &m_vardiffi[StdRegions::eVarCoeffD22][0], 1, &ggrad2[0],
422 1, &ggrad2[0], 1);
423 }
424 // Add partial derivatives together
425 Vmath::Vadd(nq, ggrad0, 1, ggrad1, 1, ggrad, 1);
426 Vmath::Vadd(nq, ggrad2, 1, ggrad, 1, ggrad, 1);
427
428 Vmath::Vadd(nq, ggrad, 1, outarray[0], 1, outarray[0], 1);
429}
430
431/**
432 *
433 */
435 bool dumpInitialConditions,
436 const int domain)
437{
438 EquationSystem::v_SetInitialConditions(initialtime, dumpInitialConditions,
439 domain);
440 m_cell->Initialise();
441}
442
443/**
444 *
445 */
447{
449 m_cell->GenerateSummary(s);
450}
451
452} // namespace Nektar
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:208
void v_GenerateSummary(SummaryList &s) override
Prints a summary of the model parameters.
void DoOdeRhs(const Array< OneD, const Array< OneD, NekDouble > > &inarray, Array< OneD, Array< OneD, NekDouble > > &outarray, const NekDouble time)
Computes the reaction terms and .
StdRegions::VarCoeffMap m_vardiffi
Definition: BidomainRoth.h:100
std::vector< StimulusSharedPtr > m_stimulus
Definition: BidomainRoth.h:98
void v_InitObject(bool DeclareField=true) override
Init object for UnsteadySystem class.
static std::string className
Name of class.
Definition: BidomainRoth.h:63
static SolverUtils::EquationSystemSharedPtr create(const LibUtilities::SessionReaderSharedPtr &pSession, const SpatialDomains::MeshGraphSharedPtr &pGraph)
Creates an instance of this class.
Definition: BidomainRoth.h:52
BidomainRoth(const LibUtilities::SessionReaderSharedPtr &pSession, const SpatialDomains::MeshGraphSharedPtr &pGraph)
Constructor.
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.
CellModelSharedPtr m_cell
Cell model.
Definition: BidomainRoth.h:96
void v_SetInitialConditions(NekDouble initialtime, bool dumpInitialConditions, const int domain) override
Sets a custom initial condition.
StdRegions::VarCoeffMap m_vardiffie
Definition: BidomainRoth.h:102
~BidomainRoth() override
Desctructor.
StdRegions::VarCoeffMap m_vardiffe
Definition: BidomainRoth.h:101
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)
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()
std::shared_ptr< MeshGraph > MeshGraphSharedPtr
Definition: MeshGraph.h:174
std::map< ConstFactorType, NekDouble > ConstFactorMap
Definition: StdRegions.hpp:402
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 Vadd(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Add vector z = x+y.
Definition: Vmath.hpp:180
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