LifeV
lifev/electrophysiology/testsuite/test_benchmark/main.cpp
Go to the documentation of this file.
1 //@HEADER
2 /*
3 *******************************************************************************
4 
5  Copyright (C) 2004, 2005, 2007 EPFL, Politecnico di Milano, INRIA
6  Copyright (C) 2010 EPFL, Politecnico di Milano, Emory University
7 
8  This file is part of LifeV.
9 
10  LifeV is free software; you can redistribute it and/or modify
11  it under the terms of the GNU Lesser General Public License as published by
12  the Free Software Foundation, either version 3 of the License, or
13  (at your option) any later version.
14 
15  LifeV is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  Lesser General Public License for more details.
19 
20  You should have received a copy of the GNU Lesser General Public License
21  along with LifeV. If not, see <http://www.gnu.org/licenses/>.
22 
23 *******************************************************************************
24 */
25 //@HEADER
26 
27 /*!
28  @file
29  @brief Electrophysiology benchmark proposed in Niederer et al. 2011
30 
31  This test is the basic test to start using the electrophysiology module.
32  We consider a parallelepiped excited in a small region in one corner.
33  The excitation wave propagates and excites the whole domain.
34  We check the time it takes for the wave to activate the whole domain.
35  We provide two coarse meshes with this test.
36  Be aware that with such mesh sizes we are far from convergence.
37 
38  The parameter list is an xml file. To work properly it should be called
39  MonodomainSolverParamList.xml
40  as the provided one, or change the name of the file (see below in the code.)
41 
42  Run it using e.g.:
43  mpirun -n 2 Electrophysiology_benchmark -o OutputFolder
44 
45 
46  WARNING: The provided mesh is coarse!!! You should not expect nice results
47  using such mesh. A finer mesh can be obtained just using gmsh and using
48  the refine by splitting function. The required mesh would be too large
49  and we decided to provide only a coarse one.
50 
51  NOTICE: The value of the membrane capacitance has a big influence on the
52  propagation. For example the TenTusscher model has Cm = 2 and final activation
53  time (in this benchmark) about twice the one computed with Luo-Rudy I or the Minimal
54  Model. Imposing Cm = 1, then, these ionic models give similar results in terms of
55  activation times.
56  Be aware of what you are doing! Don't use the default parameters as black boxes!!!
57 
58  @date 01-2013
59  @author Simone Rossi <simone.rossi@epfl.ch>
60 
61  @contributor
62  @mantainer Simone Rossi <simone.rossi@epfl.ch>
63  */
64 
65 // ---------------------------------------------------------------
66 // In order to solve the monodomain model we need
67 // to include the ETAMonodmomainSolver
68 // ---------------------------------------------------------------
69 
70 #include <lifev/electrophysiology/solver/ElectroETAMonodomainSolver.hpp>
71 
72 // ---------------------------------------------------------------
73 // We created a separate file where we collect all the functions
74 // needed to run the benchmark. In particular this utility file
75 // contains the following functions:
76 // - chooseIonicModel,
77 // to be chosen among:
78 // - AlievPanfilov
79 // - LuoRudyI
80 // - TenTusscher06
81 // - HodgkinHuxley
82 // - NoblePurkinje
83 // - MinimalModel
84 // - Fox (tested with timestep 0.0025 ms, RushLarsen method not implemented)
85 //
86 // - pacingProtocolMM,
87 // to pace with the minimal model and AlievPanfilov
88 // - pacingProtocolHH,
89 // to pace with the Hodgkin-Huxley model
90 // - pacingProtocol,
91 // to pace with the other models
92 // - setStimulus,
93 // to actually set the above pacings depending on the chosen ionic model
94 // ---------------------------------------------------------------
95 
96 #include <lifev/electrophysiology/testsuite/test_benchmark/benchmarkUtility.hpp>
97 
98 // ---------------------------------------------------------------
99 // This include is necessary in order to save the output of the
100 // simulation on a specified folder. To specify the output folder
101 // you should call "-o Folder", e.g.:
102 //
103 // mpirun -n 2 Electrophysiology_benchmark -o OutputFolder
104 // ---------------------------------------------------------------
105 
106 #include <sys/stat.h>
107 
108 // ---------------------------------------------------------------
109 // As usual, we work in the LifeV namespace.
110 // ---------------------------------------------------------------
111 
112 using namespace LifeV;
113 
114 // ---------------------------------------------------------------
115 // The test is checked only with the AlievPanfilov and it compares
116 // the final activation time with a precomputed one.
117 // This final time was computed using gcc on Ubuntu/Linux
118 // On Mac Os X 10.9 Mavercik with clang 503.0.38 the test has final time 40.
119 // ---------------------------------------------------------------
120 
121 #define finalActivationTime 53.1
122 
123 // ---------------------------------------------------------------
124 // We start the programm by the definition of the communicator.
125 // We assume you will be using mpi even for a single process.
126 // ---------------------------------------------------------------
127 
128 Int main ( Int argc, char** argv )
129 {
130 
131  //! Initializing Epetra communicator
132  MPI_Init (&argc, &argv);
133  std::shared_ptr<Epetra_Comm> Comm ( new Epetra_MpiComm (MPI_COMM_WORLD) );
134  if ( Comm->MyPID() == 0 )
135  {
136  std::cout << "% using MPI" << std::endl;
137  }
138 
139  // ---------------------------------------------------------------
140  // We create the output folder where we save the solution.
141  // The folder can be specified appending "-o FolderName" when
142  // in the command line when executing the test. If nothing is
143  // specified we create by default a folder called "Ouput".
144  // ---------------------------------------------------------------
145 
146  GetPot commandLine ( argc, argv );
147  std::string problemFolder = commandLine.follow ( "Output", 2, "-o", "--output" );
148  // Create the problem folder
149  if ( problemFolder.compare ("./") )
150  {
151  problemFolder += "/";
152 
153  if ( Comm->MyPID() == 0 )
154  {
155  mkdir ( problemFolder.c_str(), 0777 );
156  }
157  }
158 
159  // ---------------------------------------------------------------
160  // For convenience we create some typedefs for the mesh,
161  // the vectors, the matrices, the functions (used to impose the
162  // external stimulus), the ionic model and the monodomain solver.
163  // ---------------------------------------------------------------
164 
165  typedef RegionMesh<LinearTetra> mesh_Type;
166 
167  typedef VectorEpetra vector_Type;
168  typedef std::shared_ptr<vector_Type> vectorPtr_Type;
169 
170  typedef MatrixEpetra<Real> matrix_Type;
171  typedef std::shared_ptr<matrix_Type> matrixPtr_Type;
172 
173  typedef std::function < Real (const Real& /*t*/,
174  const Real & x,
175  const Real & y,
176  const Real& /*z*/,
177  const ID& /*i*/ ) > function_Type;
178 
179  typedef ElectroIonicModel ionicModel_Type;
180  typedef std::shared_ptr<ionicModel_Type> ionicModelPtr_Type;
181 
182  typedef ElectroETAMonodomainSolver< mesh_Type, ionicModel_Type > monodomainSolver_Type;
183  typedef std::shared_ptr< monodomainSolver_Type > monodomainSolverPtr_Type;
184 
185  // ---------------------------------------------------------------
186  // We set up a chronometer, to check how long it takes to setup
187  // the monodomain problem with different methods. The chronometer
188  // will be check the time only on the processor 0.
189  // ---------------------------------------------------------------
190 
191  LifeChrono chronoinitialsettings;
192 
193  if ( Comm->MyPID() == 0 )
194  {
195  chronoinitialsettings.start();
196  }
197 
198  // ---------------------------------------------------------------
199  // We read the xml parameter list file using Teuchos.
200  // ---------------------------------------------------------------
201 
202  if ( Comm->MyPID() == 0 )
203  {
204  std::cout << "Importing parameters list...";
205  }
206  Teuchos::ParameterList monodomainList = * ( Teuchos::getParametersFromXmlFile ( "MonodomainSolverParamList.xml" ) );
207  if ( Comm->MyPID() == 0 )
208  {
209  std::cout << " Done!" << std::endl;
210  }
211 
212  // ---------------------------------------------------------------
213  // From the parameter list we read the ionic model. We use the
214  // function defined in the benchmarkUtility.hpp file to choose
215  // between the models.
216  // - AlievPanfilov
217  // - LuoRudyI
218  // - TenTusscher06
219  // - HodgkinHuxley
220  // - NoblePurkinje
221  // - MinimalModel
222  // - Fox (tested with timestep 0.0025 ms)
223  // These are not the only model available. All the ionic models can
224  // be found in the folder solver/IonicModels/ of this module.
225  // The function chooseIonicModel returns the value for which we
226  // consider tissue activation. For example in adimensional models,
227  // such as the Aliev-Panfilov or the MinimalModel, this value
228  // is set to 0.95. The same values is also used for the other
229  // cardiac models. For the Hodgkin-Huxley model instead we set
230  // this threshold value equal to ten.
231  // ---------------------------------------------------------------
232 
233  std::string ionic_model ( monodomainList.get ("ionic_model", "minimalModel") );
234  if ( Comm->MyPID() == 0 )
235  {
236  std::cout << "\nIonic_Model:" << ionic_model;
237  }
238  ionicModelPtr_Type model;
239  Real activationThreshold = BenchmarkUtility::chooseIonicModel (model, ionic_model, *Comm );
240 
241 
242  // ---------------------------------------------------------------
243  // We read from the parameter list the name of the mesh file
244  // and the path to the mesh. Please use absolute paths.
245  // Make sure you specify the mesh name in the xml file, otherwise
246  // the test will fail.
247  // ---------------------------------------------------------------
248 
249  std::string meshName = monodomainList.get ("mesh_name", "");
250  std::string meshPath = monodomainList.get ("mesh_path", "./");
251 
252  // ---------------------------------------------------------------
253  // We create a GetPot datafile. Although the module do not require
254  // the use of datafile, the object is required in order to
255  // setup the preconditioners.
256  // ---------------------------------------------------------------
257 
258  GetPot dataFile (argc, argv);
259 
260  // ---------------------------------------------------------------
261  // The standard way to create the monodomain solver is to call the
262  // constructur with the following arguments:
263  // - std::string meshName (the name of the mesh file)
264  // - std::string meshPath (the path to the mesh)
265  // - GetPot dataFile (as explained above, used only to setup the preconditioners)
266  // - ionicModelPtr_Type (a shared_ptr to the ionic model)
267  // The monodomain solver class is templetized over the IonicModel class
268  // as it does not make sense to run a monodomain simulation without
269  // an ionic model.
270  // For the bistable equation consider using the ADRassembler available in LifeV.
271  // ---------------------------------------------------------------
272 
273  if ( Comm->MyPID() == 0 )
274  {
275  std::cout << "\nBuilding Monodomain Solver ... \n";
276  }
277 
278  monodomainSolverPtr_Type solver ( new monodomainSolver_Type ( meshName, meshPath, dataFile , model ) );
279  if ( Comm->MyPID() == 0 )
280  {
281  std::cout << "\t... solver created!";
282  }
283 
284  // ---------------------------------------------------------------
285  // We initialize the potential and the applied current to zero.
286  // Each ionic model has its own resting conditions.
287  // The method setInitialConditions, initialize all the variables
288  // in the ionic model with this default values.
289  // If different initial conditions are needed it is possible
290  // to use setters and getters.
291  // ---------------------------------------------------------------
292 
293  if ( Comm->MyPID() == 0 )
294  {
295  std::cout << "\nInitializing potential and gating variables ... " ;
296  }
297  solver -> setInitialConditions();
298  if ( Comm->MyPID() == 0 )
299  {
300  std::cout << "Done!" ;
301  }
302 
303  // ---------------------------------------------------------------
304  // In order to set the parameters of the modomain solver
305  // we call the method setParameters which take as argument
306  // the Teuchos parameter list we have imported at the beginning
307  // This method sets:
308  // - the time step (default: 0.01 ms)
309  // - the initial and ending time (default: 0 and 100 ms)
310  // - the surface to volume ratio (default: 2400.0)
311  // - the conductivity coefficients (default: 0.001 isotropic)
312  // - the order of the elements (default: P1. Not tested with higher order elements)
313  // - the lumping of the mass matrix (default: false)
314  // Take care that the default values do not make sense in particular
315  // if the surface to volume ratio is of the order of 1e3 then the conductivity should
316  // be around 1e-1 - 1e0.
317  // ---------------------------------------------------------------
318 
319  solver -> setParameters ( monodomainList );
320 
321  // ---------------------------------------------------------------
322  // Cardiac tissue is typically model as transversely isotropic.
323  // We need to define the preferred direction (or fiber direction).
324  // In this simple case, we specify a uniform fiber field described
325  // by the vector (0, 0, 1). The setupFiber method setup
326  // an EpetraVector with the given direction. In general
327  // the fiber field must be computed using the rule-based algorithm
328  // available in the module.
329  // ---------------------------------------------------------------
330 
331  if ( Comm->MyPID() == 0 )
332  {
333  std::cout << "\nSetting fibers ... " ;
334  }
335 
336  VectorSmall<3> fibers;
337  fibers[0] = 0.0;
338  fibers[1] = 0.0;
339  fibers[2] = 1.0;
340  solver -> setupFibers ( fibers );
341 
342  if ( Comm->MyPID() == 0 )
343  {
344  std::cout << "Done!" ;
345  }
346 
347  // ---------------------------------------------------------------
348  // Let's choose the method to solve the monodomain
349  // model:
350  // - 1st order operator splitting (2nd order available but still experimental)
351  // - L-ICI method (or Full lumping, all mass matrices are lumped - behavior equivalent to operator splitting)
352  // - ICI method (or Half-Lumping, only the mass matrix relative to the time dependent term is lumped)
353  // - SVI method (the most computationally expensive method)
354  // ---------------------------------------------------------------
355 
356  std::string solutionMethod = monodomainList.get ("solutionMethod", "splitting");
357 
358  if ( Comm->MyPID() == 0 )
359  {
360  std::cout << "\nSolving the monodomain using " << solutionMethod;
361  }
362 
363  // ---------------------------------------------------------------
364  // The monodomain is typically solved using an IMEX method, where
365  // the linear part is treated implicitely and the nonlinear
366  // explicitely. Therefore the operator is constant and has
367  // two contributions: a mass matrix and a stiffness matrix.
368  // Here we builde the two matrices and we add them together in
369  // a global matrix.
370  //
371  // If we choose to lump the mass matrix (recommended), the lumping
372  // is achieved by using nodal integration.
373  // If we want to solve the system using the so called ICI method
374  // ( sometimes called half-lumping method ) we need both the full
375  // mass matrix and the lumped one.
376  // So if we want to lump the mass matrix we first create an auxiliary
377  // full mass matrix that we will use to solve with the ICI method.
378  // ---------------------------------------------------------------
379 
380  if ( Comm->MyPID() == 0 )
381  {
382  std::cout << "\nSetup operators: \n" ;
383  }
384 
385  // Read if you are going to use a lumped mass matrix;
386  // This is set by the xml file when you create the monodomain
387  bool lumpedMass = solver -> lumpedMassMatrix();
388 
389  //safe check! You should not do this error.
390  // If you are using L-ICI, you should set setLumperMassMatrix to false
391  // If you do not set anything in the xml is set to false by default
392  if (solutionMethod == "L-ICI" && !lumpedMass)
393  {
394  std::cout << "===============================================\n";
395  std::cout << "You are using L-ICI without lumping! You can't!\n";
396  std::cout << "This time I'm fixing it for you ... be careful.\n";
397  std::cout << "===============================================\n";
398  solver -> setLumpedMassMatrix (true);
399  }
400 
401  //We create a pointer to store a full mass matrix
402  matrixPtr_Type fullMass;
403 
404  //if we are using ICI then we need to compute the fullMass matrix even
405  // if we are using lumping
406  if ( lumpedMass && solutionMethod == "ICI")
407  {
408  solver -> setLumpedMassMatrix (false);
409  solver -> setupMassMatrix();
410  fullMass.reset (new matrix_Type ( * (solver -> massMatrixPtr() ) ) );
411  solver -> setFullMassMatrixPtr (fullMass);
412  solver -> setLumpedMassMatrix (lumpedMass);
413  }
414 
415  //Build the solver mass matrix
416  solver -> setupMassMatrix();
417 
418  //safety check!!! In this tesst ICI is solved using the solveOneICIStepWithFullMass()
419  //Therefore we need to set the fullMass pointer. If we are using lumping that we
420  // already set it before, but if you are not lumping (not recommended choice)
421  // then the full mass matrix is equal to the solver mass matrix.
422  if ( !lumpedMass && solutionMethod == "ICI")
423  {
424  solver -> setFullMassMatrixPtr (solver -> massMatrixPtr() );
425  }
426 
427  //Building the stiffness matrix and the global matrix (stifness and mass)
428  solver -> setupStiffnessMatrix ();
429  solver -> setupGlobalMatrix();
430 
431  if ( Comm->MyPID() == 0 )
432  {
433  std::cout << "Done!" ;
434  }
435 
436  // ---------------------------------------------------------------
437  // The exporter is used to save the solution of the simulation.
438  // It's an external object and not part of the solver. Therefore
439  // we need to create it ourself. On the other hand the monodomain
440  // solver can set it up in order to save all the solution solved
441  // by the solver, which may vary depending on the ionic model.
442  // We pass the exporter, the name of the file where we want to
443  // save the solution, and the folder where we want it to be saved.
444  // We export the initial conditions at time 0.
445  // We always use HDF5 ouput.
446  // ---------------------------------------------------------------
447 
448  if ( Comm->MyPID() == 0 )
449  {
450  std::cout << "\nSetting up the exporter ... " ;
451  }
452  ExporterHDF5< RegionMesh <LinearTetra> > exporter;
453  solver -> setupExporter ( exporter, monodomainList.get ("OutputFile", "Solution") , problemFolder);
454  if ( Comm->MyPID() == 0 )
455  {
456  std::cout << " exporting initial solution ... " ;
457  }
458  solver -> exportSolution ( exporter, 0);
459 
460  // ---------------------------------------------------------------
461  // We want to save the activation times in the domains.
462  // Therefore, we create a vector which is initialized with the value -1.
463  // At every timestep, we will check if the nodes in the mesh have
464  // been activated, that is we check if the value of the potential
465  // is bigger than a given threshold (which was defined at the beninning
466  // when choosing the ionic model).
467  // Moreover, we want to export the activation time. We therefore create
468  // another HDF5 exporter to save the activation times on a separate file.
469  // ---------------------------------------------------------------
470 
471  vectorPtr_Type activationTimeVector ( new vector_Type ( solver -> potentialPtr() -> map() ) );
472  *activationTimeVector = -1.0;
473 
474  ExporterHDF5< RegionMesh <LinearTetra> > activationTimeExporter;
475  activationTimeExporter.setMeshProcId (solver -> localMeshPtr(), solver -> commPtr() ->MyPID() );
476  activationTimeExporter.addVariable (ExporterData<mesh_Type>::ScalarField, "Activation Time",
477  solver -> feSpacePtr(), activationTimeVector, UInt (0) );
478  activationTimeExporter.setPrefix ("ActivationTime");
479  activationTimeExporter.setPostDir (problemFolder);
480 
481  // ---------------------------------------------------------------
482  // We are ready to solve the monodomain model. We will not save the
483  // solution at every timestep. We put in the xml file the timestep
484  // between each save. By default, we save evry 1ms.
485  // We will keep track of the time used for solving the system and therefore
486  // we initialize some variables to record these values.
487  // ---------------------------------------------------------------
488 
489  Real dt (solver -> timeStep() );
490  Int iter = monodomainList.get ("saveStep", 1.0) / dt;
491  Int subiter = monodomainList.get ("subiter", 10);
492  Int k (0);
493 
494  Real timeReac = 0.0;
495  Real timeDiff = 0.0;
496  Real timeReacDiff = 0.0;
497  LifeChrono chrono;
498 
499  // ---------------------------------------------------------------
500  // The external stimulus is given as a boost function.
501  // We use the function in the benchamarkUtility.hpp to set up
502  // the boost function depending on the chosen ionic model.
503  // Then, we call the setAppliedCurrentFromFunction method of the
504  // monodomain solver wich sets the external stimulus in the solver.
505  // The value 0.0 is the time.
506  // ---------------------------------------------------------------
507 
508  if ( Comm->MyPID() == 0 )
509  {
510  std::cout << "\nSetting up the external stimulus ... " ;
511  }
512  function_Type stimulus;
513  BenchmarkUtility::setStimulus (stimulus, ionic_model);
514  if ( Comm->MyPID() == 0 )
515  {
516  std::cout << "Done!" ;
517  }
518 
519 
520  // ---------------------------------------------------------------
521  // We are done with the setup of the solver. We show how long it
522  // took.
523  // ---------------------------------------------------------------
524 
525  if ( Comm->MyPID() == 0 )
526  {
527  std::cout << "\nMonodomain solver setup done in " << chronoinitialsettings.diff() << " s.";
528  }
529 
530  // ---------------------------------------------------------------
531  // We perform the for loop over time and we start solving the
532  // monodomain model.
533  // ---------------------------------------------------------------
534 
535  if ( Comm->MyPID() == 0 )
536  {
537  std::cout << "\nstart solving: " ;
538  }
539 
540  for ( Real t = solver -> initialTime(); t < solver -> endTime(); )
541  {
542 
543  // ---------------------------------------------------------------
544  // The first step is to update the applied current with respect
545  // to time. We call again the setAppliedCurrentFromFunction
546  // method passing the time t.
547  // ---------------------------------------------------------------
548 
549  solver -> setAppliedCurrentFromFunction ( stimulus, t );
550  // ---------------------------------------------------------------
551  // Next we consider case by case the different solution methods:
552  // ---------------------------------------------------------------
553 
554  // ---------------------------------------------------------------
555  // Solving with first order operator splitting:
556  // ---------------------------------------------------------------
557 
558  if ( solutionMethod == "splitting" )
559  {
560 
561  // ---------------------------------------------------------------
562  // We first solve the system of ODEs in the ionic model.
563  // For some models it is possible to solve them with the
564  // Rush-Larsen method, otherwise we use simple forward Euler.
565  // The Rush-Larsen method is directly implemented in the ionic models,
566  // while the forward Euler scheme is implemented in the MonodomainSolver.
567  // We also save the time needed to compute the reaction step.
568  // ---------------------------------------------------------------
569 
570  chrono.reset();
571  chrono.start();
572  if (ionic_model != "MinimalModel" && ionic_model != "AlievPanfilov" && ionic_model != "Fox")
573  {
574  solver->solveOneReactionStepRL();
575  }
576  else
577  {
578  for (int j = 0; j < subiter; j++)
579  {
580  solver->solveOneReactionStepFE (subiter);
581  }
582  }
583  chrono.stop();
584 
585  timeReac += chrono.globalDiff ( *Comm );
586 
587  // ---------------------------------------------------------------
588  // We solve the diffusion step. First we need to update the rhs,
589  // and then we solve the linear system. Eventually we save the time
590  // needed to compute the diffusion step.
591  // ---------------------------------------------------------------
592 
593  (*solver->rhsPtrUnique() ) *= 0.0;
594  solver->updateRhs();
595 
596  chrono.reset();
597  chrono.start();
598  solver->solveOneDiffusionStepBE();
599  chrono.stop();
600  timeDiff += chrono.globalDiff ( *Comm );
601  }
602 
603  // ---------------------------------------------------------------
604  // Solving with Lumped Ionic Current Interpolation (L-ICI)
605  // ---------------------------------------------------------------
606 
607  else if ( solutionMethod == "L-ICI" )
608  {
609 
610  // ---------------------------------------------------------------
611  // We first solve the system of ODEs in the ionic model.
612  // For some models it is possible to solve them with the
613  // Rush-Larsen method, otherwise we use simple forward Euler.
614  // The Rush-Larsen method is directly implemented in the ionic models,
615  // while the forward Euler scheme is implemented in the MonodomainSolver.
616  // We also save the time needed to compute the solution.
617  // ---------------------------------------------------------------
618 
619  chrono.reset();
620  chrono.start();
621  if (ionic_model != "MinimalModel" && ionic_model != "AlievPanfilov" && ionic_model != "Fox")
622  {
623  solver -> solveOneStepGatingVariablesRL();
624  }
625  else
626  {
627  solver -> solveOneStepGatingVariablesFE();
628  }
629 
630 
631  // ---------------------------------------------------------------
632  // The solution with L-ICI consist in calling directly the solve ICI
633  // method using a lumped mass matrix.
634  // ---------------------------------------------------------------
635 
636  solver -> solveOneICIStep();
637  chrono.stop();
638  timeReacDiff += chrono.globalDiff ( *Comm );
639  }
640  // ---------------------------------------------------------------
641  // Solving with Lumped Ionic Current Interpolation (L-ICI)
642  // ---------------------------------------------------------------
643 
644  else if ( solutionMethod == "ICI" )
645  {
646  // ---------------------------------------------------------------
647  // We first solve the system of ODEs in the ionic model.
648  // For some models it is possible to solve them with the
649  // Rush-Larsen method, otherwise we use simple forward Euler.
650  // The Rush-Larsen method is directly implemented in the ionic models,
651  // while the forward Euler scheme is implemented in the MonodomainSolver.
652  // We also save the time needed to compute the solution.
653  // ---------------------------------------------------------------
654 
655  chrono.reset();
656  chrono.start();
657  if (ionic_model != "MinimalModel" && ionic_model != "AlievPanfilov" && ionic_model != "Fox")
658  {
659  solver -> solveOneStepGatingVariablesRL();
660  }
661  else
662  {
663  solver -> solveOneStepGatingVariablesFE();
664  }
665 
666  // ---------------------------------------------------------------
667  // The solution with L-ICI consist in calling directly the solve ICI
668  // method using a lumped mass matrix for the time dependent terms
669  // while using the full mass matrix for the reaction part.
670  // Therefore if we have lumped the mass matrix, we pass the full mass
671  // matrix as argument in the solveOneICIStep method.
672  // ---------------------------------------------------------------
673 
674  solver -> solveOneICIStepWithFullMass();
675  chrono.stop();
676  timeReacDiff += chrono.globalDiff ( *Comm );
677  }
678 
679  // ---------------------------------------------------------------
680  // Solving with State Variable Interpolation (SVI)
681  // ---------------------------------------------------------------
682 
683  else if ( solutionMethod == "SVI" )
684  {
685 
686  // ---------------------------------------------------------------
687  // We first solve the system of ODEs in the ionic model.
688  // For some models it is possible to solve them with the
689  // Rush-Larsen method, otherwise we use simple forward Euler.
690  // The Rush-Larsen method is directly implemented in the ionic models,
691  // while the forward Euler scheme is implemented in the MonodomainSolver.
692  // We also save the time needed to compute the solution.
693  // ---------------------------------------------------------------
694 
695  chrono.reset();
696  chrono.start();
697  if (ionic_model != "MinimalModel" && ionic_model != "AlievPanfilov" && ionic_model != "Fox")
698  {
699  solver -> solveOneStepGatingVariablesRL();
700  }
701  else
702  {
703  solver -> solveOneStepGatingVariablesFE();
704  }
705 
706 
707  // ---------------------------------------------------------------
708  // We call the SolveOneSVIStep method of the solver.
709  // ---------------------------------------------------------------
710 
711  solver -> solveOneSVIStep();
712  chrono.stop();
713  timeReacDiff += chrono.globalDiff ( *Comm );
714  }
715 
716  // ---------------------------------------------------------------
717  // We update the iteration number k, and the time.
718  // ---------------------------------------------------------------
719 
720  k++;
721  t = t + dt;
722 
723  // ---------------------------------------------------------------
724  // We save the activation time in the vector (*activationTimeVector)
725  // ---------------------------------------------------------------
726 
727  solver -> registerActivationTime (*activationTimeVector, t, activationThreshold);
728 
729  // ---------------------------------------------------------------
730  // If it's time to save the solution we export using the exportSolution method
731  // ---------------------------------------------------------------
732 
733  if ( k % iter == 0 )
734  {
735  if ( Comm->MyPID() == 0 )
736  {
737  std::cout << "\nTime : " << t;
738  }
739  solver -> exportSolution (exporter, t);
740  }
741 
742  }
743 
744  // ---------------------------------------------------------------
745  // We close the solution exporter. Then we export the activation
746  // times and we close the relative exporter.
747  // ---------------------------------------------------------------
748 
749  exporter.closeFile();
750  activationTimeExporter.postProcess (0);
751  activationTimeExporter.closeFile();
752 
753  // ---------------------------------------------------------------
754  // We also export the fiber direction.
755  // ---------------------------------------------------------------
756 
757  if ( Comm->MyPID() == 0 )
758  {
759  std::cout << "\nExporting fibers ... ";
760  }
761  solver -> exportFiberDirection (problemFolder);
762 
763  // ---------------------------------------------------------------
764  // We destroy the solver
765  // ---------------------------------------------------------------
766 
767  solver.reset();
768 
769  // ---------------------------------------------------------------
770  // We show show long it took to solve the problem and we thank
771  // for using the monodomain solver
772  // ---------------------------------------------------------------
773 
774  if ( Comm->MyPID() == 0 )
775  {
776  chronoinitialsettings.stop();
777  std::cout << "\n\n\nTotal lapsed time : " << chronoinitialsettings.diff() << std::endl;
778  if ( solutionMethod == "splitting" )
779  {
780  std::cout << "Diffusion time : " << timeDiff << std::endl;
781  std::cout << "Reaction time : " << timeReac << std::endl;
782  }
783  else
784  {
785  std::cout << "Solution time : " << timeReacDiff << std::endl;
786  }
787 
788  std::cout << "\n\nThank you for using ETA_MonodomainSolver.\nI hope to meet you again soon!\n All the best for your simulation :P\n " ;
789  }
790 
791  // ---------------------------------------------------------------
792  // Before ending we test if the test has succeeded.
793  // We compute the last activation
794  // time and we compare it with the precomputed value.
795  // ---------------------------------------------------------------
796  Real fullActivationTime = activationTimeVector -> maxValue();
797  activationTimeVector.reset();
798  Real returnValue;
799 
800  Real err = std::abs (fullActivationTime - finalActivationTime) / std::abs (finalActivationTime);
801 
802  if ( Comm->MyPID() == 0 )
803  {
804  std::cout << "\nError: " << err << "\n" << "\nActivation time: " << fullActivationTime << "\n";
805  }
806 
807  MPI_Barrier (MPI_COMM_WORLD);
808  MPI_Finalize();
809  if ( err > 1e-12 )
810  {
811  if ( Comm->MyPID() == 0 )
812  {
813  std::cout << "\nTest failed!\n";
814  }
815  returnValue = EXIT_FAILURE; // Norm of solution did not match
816  }
817  else
818  {
819  returnValue = EXIT_SUCCESS;
820  }
821 
822 
823 
824  return ( returnValue );
825 }
VectorEpetra - The Epetra Vector format Wrapper.
void start()
Start the timer.
Definition: LifeChrono.hpp:93
GetPot(const int argc_, char **argv_, const char *FieldSeparator=0x0)
Definition: GetPot.hpp:507
void importFromHDF5(std::string const &fileName, std::string const &matrixName="matrix")
Read a matrix from a HDF5 (.h5) file.
int32_type Int
Generic integer data.
Definition: LifeV.hpp:188
Real & operator[](UInt const &i)
Operator [].
double Real
Generic real data.
Definition: LifeV.hpp:175
void stop()
Stop the timer.
Definition: LifeChrono.hpp:100
int main(int argc, char **argv)