Nektar++
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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 // License for the specific language governing rights and limitations under
14 // Permission is hereby granted, free of charge, to any person obtaining a
15 // copy of this software and associated documentation files (the "Software"),
16 // to deal in the Software without restriction, including without limitation
17 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 // and/or sell copies of the Software, and to permit persons to whom the
19 // Software is furnished to do so, subject to the following conditions:
20 //
21 // The above copyright notice and this permission notice shall be included
22 // in all copies or substantial portions of the Software.
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 // DEALINGS IN THE SOFTWARE.
31 //
32 // Description: Monodomain cardiac electrophysiology homogenised model.
33 //
34 ///////////////////////////////////////////////////////////////////////////////
35 
36 #include <iostream>
37 
40 
41 namespace Nektar
42 {
43  /**
44  * @class Monodomain
45  *
46  * Base model of cardiac electrophysiology of the form
47  * \f{align*}{
48  * \frac{\partial u}{\partial t} = \nabla^2 u + J_{ion},
49  * \f}
50  * where the reaction term, \f$J_{ion}\f$ is defined by a specific cell
51  * model.
52  *
53  * This implementation, at present, treats the reaction terms explicitly
54  * and the diffusive element implicitly.
55  */
56 
57  /**
58  * Registers the class with the Factory.
59  */
62  "Monodomain",
64  "Monodomain model of cardiac electrophysiology.");
65 
66 
67  /**
68  *
69  */
72  : UnsteadySystem(pSession)
73  {
74  }
75 
76 
77  /**
78  *
79  */
81  {
83 
84  m_session->LoadParameter("Chi", m_chi);
85  m_session->LoadParameter("Cm", m_capMembrane);
86 
87  std::string vCellModel;
88  m_session->LoadSolverInfo("CELLMODEL", vCellModel, "");
89 
90  ASSERTL0(vCellModel != "", "Cell Model not specified.");
91 
93  vCellModel, m_session, m_fields[0]);
94 
95  m_intVariables.push_back(0);
96 
97  // Load variable coefficients
98  StdRegions::VarCoeffType varCoeffEnum[6] = {
105  };
106  std::string varCoeffString[6] = {"xx","xy","yy","xz","yz","zz"};
107  std::string aniso_var[3] = {"fx", "fy", "fz"};
108 
109  const int nq = m_fields[0]->GetNpoints();
110  const int nVarDiffCmpts = m_spacedim * (m_spacedim + 1) / 2;
111 
112  // Allocate storage for variable coeffs and initialize to 1.
113  for (int i = 0, k = 0; i < m_spacedim; ++i)
114  {
115  for (int j = 0; j < i+1; ++j)
116  {
117  if (i == j)
118  {
119  m_vardiff[varCoeffEnum[k]] = Array<OneD, NekDouble>(nq, 1.0);
120  }
121  else
122  {
123  m_vardiff[varCoeffEnum[k]] = Array<OneD, NekDouble>(nq, 0.0);
124  }
125  ++k;
126  }
127  }
128 
129  // Apply fibre map f \in [0,1], scale to conductivity range
130  // [o_min,o_max], specified by the session parameters o_min and o_max
131  if (m_session->DefinesFunction("AnisotropicConductivity"))
132  {
133  if (m_session->DefinesCmdLineArgument("verbose"))
134  {
135  cout << "Loading Anisotropic Fibre map." << endl;
136  }
137 
138  NekDouble o_min = m_session->GetParameter("o_min");
139  NekDouble o_max = m_session->GetParameter("o_max");
140  int k = 0;
141 
142  Array<OneD, NekDouble> vTemp_i;
143  Array<OneD, NekDouble> vTemp_j;
144 
145  /*
146  * Diffusivity matrix D is upper triangular and defined as
147  * d_00 d_01 d_02
148  * d_11 d_12
149  * d_22
150  *
151  * Given a principle fibre direction _f_ the diffusivity is given
152  * by
153  * d_ij = { D_2 + (D_1 - D_2) f_i f_j if i==j
154  * { (D_1 - D_2) f_i f_j if i!=j
155  *
156  * The vector _f_ is given in terms of the variables fx,fy,fz in the
157  * function AnisotropicConductivity. The values of D_1 and D_2 are
158  * the parameters o_max and o_min, respectively.
159  */
160 
161  // Loop through columns of D
162  for (int j = 0; j < m_spacedim; ++j)
163  {
164  ASSERTL0(m_session->DefinesFunction("AnisotropicConductivity",
165  aniso_var[j]),
166  "Function 'AnisotropicConductivity' not correctly "
167  "defined.");
168  EvaluateFunction(aniso_var[j], vTemp_j,
169  "AnisotropicConductivity");
170 
171  // Loop through rows of D
172  for (int i = 0; i < j + 1; ++i)
173  {
174  ASSERTL0(m_session->DefinesFunction(
175  "AnisotropicConductivity",aniso_var[i]),
176  "Function 'AnisotropicConductivity' not correctly "
177  "defined.");
178  EvaluateFunction(aniso_var[i], vTemp_i,
179  "AnisotropicConductivity");
180 
181  Vmath::Vmul(nq, vTemp_i, 1, vTemp_j, 1,
182  m_vardiff[varCoeffEnum[k]], 1);
183 
184  Vmath::Smul(nq, o_max-o_min,
185  m_vardiff[varCoeffEnum[k]], 1,
186  m_vardiff[varCoeffEnum[k]], 1);
187 
188  if (i == j)
189  {
190  Vmath::Sadd(nq, o_min,
191  m_vardiff[varCoeffEnum[k]], 1,
192  m_vardiff[varCoeffEnum[k]], 1);
193  }
194 
195  ++k;
196  }
197  }
198  }
199 
200  // Scale by scar map (range 0->1) derived from intensity map
201  // (range d_min -> d_max)
202  if (m_session->DefinesFunction("IsotropicConductivity"))
203  {
204  if (m_session->DefinesCmdLineArgument("verbose"))
205  {
206  cout << "Loading Isotropic Conductivity map." << endl;
207  }
208 
209  const std::string varName = "intensity";
210  Array<OneD, NekDouble> vTemp;
211  EvaluateFunction(varName, vTemp, "IsotropicConductivity");
212 
213  // If the d_min and d_max parameters are defined, then we need to
214  // rescale the isotropic conductivity to convert from the source
215  // domain (e.g. late-gad intensity) to conductivity
216  if ( m_session->DefinesParameter("d_min") ||
217  m_session->DefinesParameter("d_max") ) {
218  const NekDouble f_min = m_session->GetParameter("d_min");
219  const NekDouble f_max = m_session->GetParameter("d_max");
220  const NekDouble scar_min = 0.0;
221  const NekDouble scar_max = 1.0;
222 
223  // Threshold based on d_min, d_max
224  for (int j = 0; j < nq; ++j)
225  {
226  vTemp[j] = (vTemp[j] < f_min ? f_min : vTemp[j]);
227  vTemp[j] = (vTemp[j] > f_max ? f_max : vTemp[j]);
228  }
229 
230  // Rescale to s \in [0,1] (0 maps to d_max, 1 maps to d_min)
231  Vmath::Sadd(nq, -f_min, vTemp, 1, vTemp, 1);
232  Vmath::Smul(nq, -1.0/(f_max-f_min), vTemp, 1, vTemp, 1);
233  Vmath::Sadd(nq, 1.0, vTemp, 1, vTemp, 1);
234  Vmath::Smul(nq, scar_max - scar_min, vTemp, 1, vTemp, 1);
235  Vmath::Sadd(nq, scar_min, vTemp, 1, vTemp, 1);
236  }
237 
238  // Scale anisotropic conductivity values
239  for (int i = 0; i < nVarDiffCmpts; ++i)
240  {
241  Vmath::Vmul(nq, vTemp, 1,
242  m_vardiff[varCoeffEnum[i]], 1,
243  m_vardiff[varCoeffEnum[i]], 1);
244  }
245  }
246 
247 
248  // Write out conductivity values
249  for (int j = 0, k = 0; j < m_spacedim; ++j)
250  {
251  // Loop through rows of D
252  for (int i = 0; i < j + 1; ++i)
253  {
254  // Transform variable coefficient and write out to file.
255  m_fields[0]->FwdTrans_IterPerExp(m_vardiff[varCoeffEnum[k]],
256  m_fields[0]->UpdateCoeffs());
257  std::stringstream filename;
258  filename << "Conductivity_" << varCoeffString[k] << ".fld";
259  WriteFld(filename.str());
260 
261  ++k;
262  }
263  }
264 
265  // Search through the loaded filters and pass the cell model to any
266  // CheckpointCellModel filters loaded.
267  int k = 0;
268  const LibUtilities::FilterMap& f = m_session->GetFilters();
269  LibUtilities::FilterMap::const_iterator x;
270  for (x = f.begin(); x != f.end(); ++x, ++k)
271  {
272  if (x->first == "CheckpointCellModel")
273  {
274  boost::shared_ptr<FilterCheckpointCellModel> c
275  = boost::dynamic_pointer_cast<FilterCheckpointCellModel>(
276  m_filters[k]);
277  c->SetCellModel(m_cell);
278  }
279  }
280 
281  // Load stimuli
283 
284  if (!m_explicitDiffusion)
285  {
287  }
289  }
290 
291 
292  /**
293  *
294  */
296  {
297 
298  }
299 
300 
301  /**
302  * @param inarray Input array.
303  * @param outarray Output array.
304  * @param time Current simulation time.
305  * @param lambda Timestep.
306  */
308  const Array<OneD, const Array<OneD, NekDouble> >&inarray,
309  Array<OneD, Array<OneD, NekDouble> >&outarray,
310  const NekDouble time,
311  const NekDouble lambda)
312  {
313  int nvariables = inarray.num_elements();
314  int nq = m_fields[0]->GetNpoints();
316  // lambda = \Delta t
317  factors[StdRegions::eFactorLambda] = 1.0/lambda*m_chi*m_capMembrane;
318 
319  // We solve ( \nabla^2 - HHlambda ) Y[i] = rhs [i]
320  // inarray = input: \hat{rhs} -> output: \hat{Y}
321  // outarray = output: nabla^2 \hat{Y}
322  // where \hat = modal coeffs
323  for (int i = 0; i < nvariables; ++i)
324  {
325  // Multiply 1.0/timestep
326  Vmath::Smul(nq, -factors[StdRegions::eFactorLambda], inarray[i], 1,
327  m_fields[i]->UpdatePhys(), 1);
328 
329  // Solve a system of equations with Helmholtz solver and transform
330  // back into physical space.
331  m_fields[i]->HelmSolve(m_fields[i]->GetPhys(),
332  m_fields[i]->UpdateCoeffs(), NullFlagList,
333  factors, m_vardiff);
334 
335  m_fields[i]->BwdTrans( m_fields[i]->GetCoeffs(),
336  m_fields[i]->UpdatePhys());
337  m_fields[i]->SetPhysState(true);
338 
339  // Copy the solution vector (required as m_fields must be set).
340  outarray[i] = m_fields[i]->GetPhys();
341  }
342  }
343 
344 
345  /**
346  *
347  */
349  const Array<OneD, const Array<OneD, NekDouble> >&inarray,
350  Array<OneD, Array<OneD, NekDouble> >&outarray,
351  const NekDouble time)
352  {
353  // Compute I_ion
354  m_cell->TimeIntegrate(inarray, outarray, time);
355 
356  // Compute I_stim
357  for (unsigned int i = 0; i < m_stimulus.size(); ++i)
358  {
359  m_stimulus[i]->Update(outarray, time);
360  }
361  }
362 
363 
364  /**
365  *
366  */
368  bool dumpInitialConditions,
369  const int domain)
370  {
372  dumpInitialConditions,
373  domain);
374  m_cell->Initialise();
375  }
376 
377 
378  /**
379  *
380  */
382  {
384  if (m_session->DefinesFunction("d00") &&
385  m_session->GetFunctionType("d00", "intensity")
387  {
388  AddSummaryItem(s, "Diffusivity-x",
389  m_session->GetFunction("d00", "intensity")->GetExpression());
390  }
391  if (m_session->DefinesFunction("d11") &&
392  m_session->GetFunctionType("d11", "intensity")
394  {
395  AddSummaryItem(s, "Diffusivity-y",
396  m_session->GetFunction("d11", "intensity")->GetExpression());
397  }
398  if (m_session->DefinesFunction("d22") &&
399  m_session->GetFunctionType("d22", "intensity")
401  {
402  AddSummaryItem(s, "Diffusivity-z",
403  m_session->GetFunction("d22", "intensity")->GetExpression());
404  }
405  m_cell->GenerateSummary(s);
406  }
407 }