Nektar++
CellModel.cpp
Go to the documentation of this file.
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 // File: CellModel.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: Cell model base class.
32 //
33 ///////////////////////////////////////////////////////////////////////////////
34 
36 
38 
40 //#include <LibUtilities/LinearAlgebra/Blas.hpp>
41 
42 using namespace std;
43 
44 namespace Nektar
45 {
47 {
48  static CellModelFactory instance;
49  return instance;
50 }
51 
52 /**
53  * @class CellModel
54  *
55  * The CellModel class and derived classes implement a range of cell model
56  * ODE systems. A cell model comprises a system of ion concentration
57  * variables and zero or more gating variables. Gating variables are
58  * time-integrated using the Rush-Larsen method and for each variable y,
59  * the corresponding y_inf and tau_y value is computed by Update(). The tau
60  * values are stored in separate storage to inarray/outarray, #m_gates_tau.
61  */
62 
63 /**
64  * Cell model base class constructor.
65  */
66 CellModel::CellModel(const LibUtilities::SessionReaderSharedPtr &pSession,
67  const MultiRegions::ExpListSharedPtr &pField)
68 {
69  m_session = pSession;
70  m_field = pField;
71  m_lastTime = 0.0;
72  m_substeps = pSession->GetParameter("Substeps");
73  m_nvar = 0;
74  m_useNodal = false;
75 
76  // Number of points in nodal space is the number of coefficients
77  // in modified basis
78  std::set<enum LibUtilities::ShapeType> s;
79  for (size_t i = 0; i < m_field->GetNumElmts(); ++i)
80  {
81  s.insert(m_field->GetExp(i)->DetShapeType());
82  }
83 
84  // Use nodal projection if only triangles
85  if (s.size() == 1 && (s.count(LibUtilities::eTriangle) == 1 ||
86  s.count(LibUtilities::eTetrahedron) == 1))
87  {
88  // This is disabled for now as it causes problems at high order.
89  // m_useNodal = true;
90  }
91 
92  // ---------------------------
93  // Move to nodal points
94  if (m_useNodal)
95  {
96  m_nq = pField->GetNcoeffs();
97  int order = m_field->GetExp(0)->GetBasis(0)->GetNumModes();
98 
99  // Set up a nodal tri
107  LibUtilities::eGaussRadauMAlpha1Beta0));
111  LibUtilities::eGaussRadauMAlpha2Beta0));
112 
113  m_nodalTri =
116  m_nodalTet =
119  }
120  else
121  {
122  m_nq = pField->GetTotPoints();
123  }
124 }
125 
126 /**
127  * Initialise the cell model. Allocate workspace and variable storage.
128  */
129 void CellModel::Initialise()
130 {
131  ASSERTL1(m_nvar > 0, "Cell model must have at least 1 variable.");
132 
133  m_cellSol = Array<OneD, Array<OneD, NekDouble>>(m_nvar);
134  m_wsp = Array<OneD, Array<OneD, NekDouble>>(m_nvar);
135  for (size_t i = 0; i < m_nvar; ++i)
136  {
137  m_cellSol[i] = Array<OneD, NekDouble>(m_nq);
138  m_wsp[i] = Array<OneD, NekDouble>(m_nq);
139  }
140  m_gates_tau = Array<OneD, Array<OneD, NekDouble>>(m_gates.size());
141  for (size_t i = 0; i < m_gates.size(); ++i)
142  {
143  m_gates_tau[i] = Array<OneD, NekDouble>(m_nq);
144  }
145 
146  if (m_session->DefinesFunction("CellModelInitialConditions"))
147  {
148  LoadCellModel();
149  }
150  else
151  {
152  v_SetInitialConditions();
153  }
154 }
155 
156 /**
157  * Integrates the cell model for one PDE time-step. Cell model is
158  * sub-stepped.
159  *
160  * Ion concentrations and membrane potential are integrated using forward
161  * Euler, while gating variables are integrated using the Rush-Larsen
162  * scheme.
163  */
164 void CellModel::TimeIntegrate(
165  const Array<OneD, const Array<OneD, NekDouble>> &inarray,
166  Array<OneD, Array<OneD, NekDouble>> &outarray, const NekDouble time)
167 {
168  int phys_offset = 0;
169  int coef_offset = 0;
170  size_t nvar = inarray.size();
172 
173  // ---------------------------
174  // Check nodal temp array set up
175  if (m_useNodal)
176  {
177  if (!m_nodalTmp.size())
178  {
179  m_nodalTmp = Array<OneD, Array<OneD, NekDouble>>(nvar);
180  for (size_t k = 0; k < nvar; ++k)
181  {
182  m_nodalTmp[k] = Array<OneD, NekDouble>(m_nq);
183  }
184  }
185 
186  // Move to nodal points
187  Array<OneD, NekDouble> tmpCoeffs(
188  max(m_nodalTri->GetNcoeffs(), m_nodalTet->GetNcoeffs()));
189 
190  for (size_t k = 0; k < nvar; ++k)
191  {
192  for (size_t i = 0; i < m_field->GetNumElmts(); ++i)
193  {
194  phys_offset = m_field->GetPhys_Offset(i);
195  coef_offset = m_field->GetCoeff_Offset(i);
196  if (m_field->GetExp(0)->DetShapeType() ==
198  {
199  m_field->GetExp(0)->FwdTrans(inarray[k] + phys_offset,
200  tmpCoeffs);
201  m_nodalTri->ModalToNodal(tmpCoeffs,
202  tmp = m_nodalTmp[k] + coef_offset);
203  }
204  else
205  {
206  m_field->GetExp(0)->FwdTrans(inarray[k] + phys_offset,
207  tmpCoeffs);
208  m_nodalTet->ModalToNodal(tmpCoeffs,
209  tmp = m_nodalTmp[k] + coef_offset);
210  }
211  }
212  }
213  // Copy new transmembrane potential into cell model
214  Vmath::Vcopy(m_nq, m_nodalTmp[0], 1, m_cellSol[0], 1);
215  }
216  else
217  {
218  // Copy new transmembrane potential into cell model
219  Vmath::Vcopy(m_nq, inarray[0], 1, m_cellSol[0], 1);
220  }
221  // -------------------------
222 
223  NekDouble delta_t = (time - m_lastTime) / m_substeps;
224 
225  // Perform substepping
226  for (size_t i = 0; i < m_substeps - 1; ++i)
227  {
228  Update(m_cellSol, m_wsp, time);
229  // Voltage
230  Vmath::Svtvp(m_nq, delta_t, m_wsp[0], 1, m_cellSol[0], 1, m_cellSol[0],
231  1);
232  // Ion concentrations
233  for (size_t j = 0; j < m_concentrations.size(); ++j)
234  {
235  Vmath::Svtvp(m_nq, delta_t, m_wsp[m_concentrations[j]], 1,
236  m_cellSol[m_concentrations[j]], 1,
237  m_cellSol[m_concentrations[j]], 1);
238  }
239  // Gating variables: Rush-Larsen scheme
240  for (size_t j = 0; j < m_gates.size(); ++j)
241  {
242  Vmath::Sdiv(m_nq, -delta_t, m_gates_tau[j], 1, m_gates_tau[j], 1);
243  Vmath::Vexp(m_nq, m_gates_tau[j], 1, m_gates_tau[j], 1);
244  Vmath::Vsub(m_nq, m_cellSol[m_gates[j]], 1, m_wsp[m_gates[j]], 1,
245  m_cellSol[m_gates[j]], 1);
246  Vmath::Vvtvp(m_nq, m_cellSol[m_gates[j]], 1, m_gates_tau[j], 1,
247  m_wsp[m_gates[j]], 1, m_cellSol[m_gates[j]], 1);
248  }
249  }
250 
251  // Perform final cell model step
252  Update(m_cellSol, m_wsp, time);
253 
254  // Output dV/dt from last step but integrate remaining cell model vars
255  // Transform cell model I_total from nodal to modal space
256  if (m_useNodal)
257  {
258  Array<OneD, NekDouble> tmpCoeffs(
259  max(m_nodalTri->GetNcoeffs(), m_nodalTet->GetNcoeffs()));
260 
261  for (size_t k = 0; k < nvar; ++k)
262  {
263  for (size_t i = 0; i < m_field->GetNumElmts(); ++i)
264  {
265  int phys_offset = m_field->GetPhys_Offset(i);
266  int coef_offset = m_field->GetCoeff_Offset(i);
267  if (m_field->GetExp(0)->DetShapeType() ==
269  {
270  m_nodalTri->NodalToModal(m_wsp[k] + coef_offset, tmpCoeffs);
271  m_field->GetExp(0)->BwdTrans(tmpCoeffs, tmp = outarray[k] +
272  phys_offset);
273  }
274  else
275  {
276  m_nodalTet->NodalToModal(m_wsp[k] + coef_offset, tmpCoeffs);
277  m_field->GetExp(0)->BwdTrans(tmpCoeffs, tmp = outarray[k] +
278  phys_offset);
279  }
280  }
281  }
282  }
283  else
284  {
285  Vmath::Vcopy(m_nq, m_wsp[0], 1, outarray[0], 1);
286  }
287 
288  // Ion concentrations
289  for (size_t j = 0; j < m_concentrations.size(); ++j)
290  {
291  Vmath::Svtvp(m_nq, delta_t, m_wsp[m_concentrations[j]], 1,
292  m_cellSol[m_concentrations[j]], 1,
293  m_cellSol[m_concentrations[j]], 1);
294  }
295 
296  // Gating variables: Rush-Larsen scheme
297  for (size_t j = 0; j < m_gates.size(); ++j)
298  {
299  Vmath::Sdiv(m_nq, -delta_t, m_gates_tau[j], 1, m_gates_tau[j], 1);
300  Vmath::Vexp(m_nq, m_gates_tau[j], 1, m_gates_tau[j], 1);
301  Vmath::Vsub(m_nq, m_cellSol[m_gates[j]], 1, m_wsp[m_gates[j]], 1,
302  m_cellSol[m_gates[j]], 1);
303  Vmath::Vvtvp(m_nq, m_cellSol[m_gates[j]], 1, m_gates_tau[j], 1,
304  m_wsp[m_gates[j]], 1, m_cellSol[m_gates[j]], 1);
305  }
306 
307  m_lastTime = time;
308 }
309 
310 Array<OneD, NekDouble> CellModel::GetCellSolutionCoeffs(size_t idx)
311 {
312  ASSERTL0(idx < m_nvar, "Index out of range for cell model.");
313 
314  Array<OneD, NekDouble> outarray(m_field->GetNcoeffs());
316 
317  if (m_useNodal)
318  {
319  for (size_t i = 0; i < m_field->GetNumElmts(); ++i)
320  {
321  int coef_offset = m_field->GetCoeff_Offset(i);
322  if (m_field->GetExp(0)->DetShapeType() == LibUtilities::eTriangle)
323  {
324  m_nodalTri->NodalToModal(m_cellSol[idx] + coef_offset,
325  tmp = outarray + coef_offset);
326  }
327  else
328  {
329  m_nodalTet->NodalToModal(m_cellSol[idx] + coef_offset,
330  tmp = outarray + coef_offset);
331  }
332  }
333  }
334  else
335  {
336  m_field->FwdTransLocalElmt(m_cellSol[idx], outarray);
337  }
338 
339  return outarray;
340 }
341 
342 Array<OneD, NekDouble> CellModel::GetCellSolution(size_t idx)
343 {
344  return m_cellSol[idx];
345 }
346 
347 void CellModel::LoadCellModel()
348 {
349  const bool root = (m_session->GetComm()->GetRank() == 0);
350  const std::string fncName = "CellModelInitialConditions";
351  const size_t nvar = m_cellSol[0].size();
352  std::string varName;
353  Array<OneD, NekDouble> coeffs(m_field->GetNcoeffs());
355  size_t j = 0;
356 
357  SpatialDomains::MeshGraphSharedPtr vGraph = m_field->GetGraph();
358 
359  if (root)
360  {
361  cout << "Cell model initial conditions: " << endl;
362  }
363 
364  // First determine all the files we need to load
365  std::set<std::string> filelist;
366  for (j = 1; j < nvar; ++j)
367  {
368  // Get the name of the jth variable
369  varName = GetCellVarName(j);
370 
371  if (m_session->GetFunctionType(fncName, varName) ==
373  {
374  filelist.insert(m_session->GetFunctionFilename(fncName, varName));
375  }
376  }
377 
378  // Read files
379  typedef std::vector<LibUtilities::FieldDefinitionsSharedPtr> FDef;
380  typedef std::vector<std::vector<NekDouble>> FData;
381  std::map<std::string, FDef> FieldDef;
382  std::map<std::string, FData> FieldData;
383  LibUtilities::FieldMetaDataMap fieldMetaDataMap;
384 
385  for (auto &setIt : filelist)
386  {
387  if (root)
388  {
389  cout << " - Reading file: " << setIt << endl;
390  }
391  FieldDef[setIt] = FDef(0);
392  FieldData[setIt] = FData(0);
394  LibUtilities::FieldIO::CreateForFile(m_session, setIt);
395  fld->Import(setIt, FieldDef[setIt], FieldData[setIt], fieldMetaDataMap);
396  }
397 
398  // Get time of checkpoint from file if available
399  auto iter = fieldMetaDataMap.find("Time");
400  if (iter != fieldMetaDataMap.end())
401  {
402  m_lastTime = boost::lexical_cast<NekDouble>(iter->second);
403  }
404 
405  // Load each cell model variable
406  // j=0 and j=1 are for transmembrane or intra/extra-cellular volt.
407  Vmath::Zero(m_nq, m_cellSol[0], 1);
408  for (j = 1; j < m_cellSol.size(); ++j)
409  {
410  // Get the name of the jth variable
411  varName = GetCellVarName(j);
412 
413  // Check if this variable is defined in a file or analytically
414  if (m_session->GetFunctionType(fncName, varName) ==
416  {
417  const std::string file =
418  m_session->GetFunctionFilename(fncName, varName);
419 
420  if (root)
421  {
422  cout << " - Field " << varName << ": from file " << file
423  << endl;
424  }
425 
426  // Extract the data into the modal coefficients
427  for (size_t i = 0; i < FieldDef[file].size(); ++i)
428  {
429  m_field->ExtractDataToCoeffs(
430  FieldDef[file][i], FieldData[file][i], varName, coeffs);
431  }
432 
433  // If using nodal cell model then we do a modal->nodal transform
434  // otherwise we do a backward transform onto physical points.
435  if (m_useNodal)
436  {
437  for (size_t i = 0; i < m_field->GetNumElmts(); ++i)
438  {
439  int coef_offset = m_field->GetCoeff_Offset(i);
440  if (m_field->GetExp(0)->DetShapeType() ==
442  {
443  m_nodalTri->ModalToNodal(coeffs + coef_offset,
444  tmp = m_cellSol[j] +
445  coef_offset);
446  }
447  else
448  {
449  m_nodalTet->ModalToNodal(coeffs + coef_offset,
450  tmp = m_cellSol[j] +
451  coef_offset);
452  }
453  }
454  }
455  else
456  {
457  m_field->BwdTrans(coeffs, m_cellSol[j]);
458  }
459  }
460  else if (m_session->GetFunctionType(fncName, varName) ==
462  {
464  m_session->GetFunction(fncName, varName);
465 
466  if (root)
467  {
468  cout << " - Field " << varName << ": " << equ->GetExpression()
469  << endl;
470  }
471 
472  const size_t nphys = m_field->GetNpoints();
473  Array<OneD, NekDouble> x0(nphys);
474  Array<OneD, NekDouble> x1(nphys);
475  Array<OneD, NekDouble> x2(nphys);
476  m_field->GetCoords(x0, x1, x2);
477 
478  if (m_useNodal)
479  {
480  Array<OneD, NekDouble> phys(nphys);
481  Array<OneD, NekDouble> tmpCoeffs(
482  max(m_nodalTri->GetNcoeffs(), m_nodalTet->GetNcoeffs()));
483 
484  equ->Evaluate(x0, x1, x2, phys);
485  for (size_t i = 0; i < m_field->GetNumElmts(); ++i)
486  {
487  int phys_offset = m_field->GetPhys_Offset(i);
488  int coef_offset = m_field->GetCoeff_Offset(i);
489  if (m_field->GetExp(0)->DetShapeType() ==
491  {
492  m_field->GetExp(0)->FwdTrans(phys + phys_offset,
493  tmpCoeffs);
494  m_nodalTri->ModalToNodal(tmpCoeffs, tmp = m_cellSol[j] +
495  coef_offset);
496  }
497  else
498  {
499  m_field->GetExp(0)->FwdTrans(phys + phys_offset,
500  tmpCoeffs);
501  m_nodalTet->ModalToNodal(tmpCoeffs, tmp = m_cellSol[j] +
502  coef_offset);
503  }
504  }
505  }
506  else
507  {
508  equ->Evaluate(x0, x1, x2, m_cellSol[j]);
509  }
510  }
511  }
512 }
513 } // namespace Nektar
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:215
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode....
Definition: ErrorUtil.hpp:249
Describes the specification for a Basis.
Definition: Basis.h:50
Provides a generic Factory class.
Definition: NekFactory.hpp:105
Defines a specification for a set of points.
Definition: Points.h:59
General purpose memory allocation routines with the ability to allocate from thread specific memory p...
std::shared_ptr< FieldIO > FieldIOSharedPtr
Definition: FieldIO.h:327
std::map< std::string, std::string > FieldMetaDataMap
Definition: FieldIO.h:52
std::shared_ptr< SessionReader > SessionReaderSharedPtr
std::shared_ptr< Equation > EquationSharedPtr
Definition: Equation.h:129
@ eNodalTriEvenlySpaced
2D Evenly-spaced points on a Triangle
Definition: PointsType.h:85
@ eGaussLobattoLegendre
1D Gauss-Lobatto-Legendre quadrature points
Definition: PointsType.h:53
@ eNodalTetEvenlySpaced
3D Evenly-spaced points on a Tetrahedron
Definition: PointsType.h:86
@ eModified_B
Principle Modified Functions .
Definition: BasisType.h:51
@ eModified_C
Principle Modified Functions .
Definition: BasisType.h:52
@ eModified_A
Principle Modified Functions .
Definition: BasisType.h:50
std::shared_ptr< ExpList > ExpListSharedPtr
Shared pointer to an ExpList object.
std::shared_ptr< MeshGraph > MeshGraphSharedPtr
Definition: MeshGraph.h:172
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:2
CellModelFactory & GetCellModelFactory()
Definition: CellModel.cpp:46
double NekDouble
void Vexp(int n, const T *x, const int incx, T *y, const int incy)
Definition: Vmath.hpp:125
void Svtvp(int n, const T alpha, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
svtvp (scalar times vector plus vector): z = alpha*x + y
Definition: Vmath.cpp:622
void Vvtvp(int n, const T *w, const int incw, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
vvtvp (vector times vector plus vector): z = w*x + y
Definition: Vmath.cpp:574
void Sdiv(int n, const T alpha, const T *x, const int incx, T *y, const int incy)
Scalar multiply y = alpha/y.
Definition: Vmath.cpp:324
void Zero(int n, T *x, const int incx)
Zero vector.
Definition: Vmath.cpp:492
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition: Vmath.cpp:1255
void Vsub(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Subtract vector z = x-y.
Definition: Vmath.cpp:419