Chaste  Release::3.4
AbstractCardiacProblem.cpp
1 /*
2 
3 Copyright (c) 2005-2016, University of Oxford.
4 All rights reserved.
5 
6 University of Oxford means the Chancellor, Masters and Scholars of the
7 University of Oxford, having an administrative office at Wellington
8 Square, Oxford OX1 2JD, UK.
9 
10 This file is part of Chaste.
11 
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions are met:
14  * Redistributions of source code must retain the above copyright notice,
15  this list of conditions and the following disclaimer.
16  * Redistributions in binary form must reproduce the above copyright notice,
17  this list of conditions and the following disclaimer in the documentation
18  and/or other materials provided with the distribution.
19  * Neither the name of the University of Oxford nor the names of its
20  contributors may be used to endorse or promote products derived from this
21  software without specific prior written permission.
22 
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 
34 */
35 
36 #include "AbstractCardiacProblem.hpp"
37 
38 #include "GenericMeshReader.hpp"
39 #include "Exception.hpp"
40 #include "HeartConfig.hpp"
41 #include "HeartEventHandler.hpp"
42 #include "TimeStepper.hpp"
43 #include "PetscTools.hpp"
44 #include "DistributedVector.hpp"
45 #include "ProgressReporter.hpp"
46 #include "LinearSystem.hpp"
47 #include "PostProcessingWriter.hpp"
48 #include "Hdf5ToMeshalyzerConverter.hpp"
49 #include "Hdf5ToCmguiConverter.hpp"
50 #include "Hdf5ToVtkConverter.hpp"
51 
52 
53 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
56  : mMeshFilename(""), // i.e. undefined
57  mAllocatedMemoryForMesh(false),
58  mWriteInfo(false),
59  mPrintOutput(true),
60  mpCardiacTissue(NULL),
61  mpSolver(NULL),
62  mpCellFactory(pCellFactory),
63  mpMesh(NULL),
64  mSolution(NULL),
65  mCurrentTime(0.0),
66  mpTimeAdaptivityController(NULL),
67  mpWriter(NULL)
68 {
69  assert(mNodesToOutput.empty());
70  if (!mpCellFactory)
71  {
72  EXCEPTION("AbstractCardiacProblem: Please supply a cell factory pointer to your cardiac problem constructor.");
73  }
74  HeartEventHandler::BeginEvent(HeartEventHandler::EVERYTHING);
75 }
76 
77 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
79  // It doesn't really matter what we initialise these to, as they'll be overwritten by
80  // the serialization methods
81  : mMeshFilename(""),
82  mAllocatedMemoryForMesh(false), // Handled by AbstractCardiacTissue
83  mWriteInfo(false),
84  mPrintOutput(true),
85  mVoltageColumnId(UINT_MAX),
86  mTimeColumnId(UINT_MAX),
87  mNodeColumnId(UINT_MAX),
88  mpCardiacTissue(NULL),
89  mpSolver(NULL),
90  mpCellFactory(NULL),
91  mpMesh(NULL),
92  mSolution(NULL),
93  mCurrentTime(0.0),
94  mpTimeAdaptivityController(NULL),
95  mpWriter(NULL)
96 {
97 }
98 
99 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
101 {
102  delete mpCardiacTissue;
103  if (mSolution)
104  {
105  PetscTools::Destroy(mSolution);
106  }
107 
108  if (mAllocatedMemoryForMesh)
109  {
110  delete mpMesh;
111  }
112 }
113 
114 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
116 {
117  HeartEventHandler::BeginEvent(HeartEventHandler::READ_MESH);
118  if (mpMesh)
119  {
121  {
122  WARNING("Using a non-distributed mesh in a parallel simulation is not a good idea.");
123  }
124  }
125  else
126  {
127  // If no mesh has been passed, we get it from the configuration file
128  try
129  {
130  if (HeartConfig::Instance()->GetLoadMesh())
131  {
132  CreateMeshFromHeartConfig();
133  std::auto_ptr<AbstractMeshReader<ELEMENT_DIM, SPACE_DIM> > p_mesh_reader
134  = GenericMeshReader<ELEMENT_DIM, SPACE_DIM>(HeartConfig::Instance()->GetMeshName());
135  mpMesh->ConstructFromMeshReader(*p_mesh_reader);
136  }
137  else if (HeartConfig::Instance()->GetCreateMesh())
138  {
139  CreateMeshFromHeartConfig();
140  assert(HeartConfig::Instance()->GetSpaceDimension()==SPACE_DIM);
141  double inter_node_space = HeartConfig::Instance()->GetInterNodeSpace();
142 
143  switch(HeartConfig::Instance()->GetSpaceDimension())
144  {
145  case 1:
146  {
147  c_vector<double, 1> fibre_length;
148  HeartConfig::Instance()->GetFibreLength(fibre_length);
149  mpMesh->ConstructRegularSlabMesh(inter_node_space, fibre_length[0]);
150  break;
151  }
152  case 2:
153  {
154  c_vector<double, 2> sheet_dimensions; //cm
155  HeartConfig::Instance()->GetSheetDimensions(sheet_dimensions);
156  mpMesh->ConstructRegularSlabMesh(inter_node_space, sheet_dimensions[0], sheet_dimensions[1]);
157  break;
158  }
159  case 3:
160  {
161  c_vector<double, 3> slab_dimensions; //cm
162  HeartConfig::Instance()->GetSlabDimensions(slab_dimensions);
163  mpMesh->ConstructRegularSlabMesh(inter_node_space, slab_dimensions[0], slab_dimensions[1], slab_dimensions[2]);
164  break;
165  }
166  default:
168  }
169  }
170  else
171  {
173  }
174 
175  mAllocatedMemoryForMesh = true;
176  }
177  catch (Exception& e)
178  {
179  EXCEPTION(std::string("No mesh given: define it in XML parameters file or call SetMesh()\n") + e.GetShortMessage());
180  }
181  }
182  mpCellFactory->SetMesh( mpMesh );
183  HeartEventHandler::EndEvent(HeartEventHandler::READ_MESH);
184 
185  HeartEventHandler::BeginEvent(HeartEventHandler::INITIALISE);
186 
187  // If the user requested transmural stuff, we fill in the mCellHeterogeneityAreas here
188  if (HeartConfig::Instance()->AreCellularTransmuralHeterogeneitiesRequested())
189  {
190  mpCellFactory->FillInCellularTransmuralAreas();
191  }
192 
193  delete mpCardiacTissue; // In case we're called twice
194  mpCardiacTissue = CreateCardiacTissue();
195 
196  HeartEventHandler::EndEvent(HeartEventHandler::INITIALISE);
197 
198  // Delete any previous solution, so we get a fresh initial condition
199  if (mSolution)
200  {
201  HeartEventHandler::BeginEvent(HeartEventHandler::COMMUNICATION);
202  PetscTools::Destroy(mSolution);
203  mSolution = NULL;
204  HeartEventHandler::EndEvent(HeartEventHandler::COMMUNICATION);
205  }
206 
207  // Always start at time zero
208  mCurrentTime = 0.0;
209 
210  // For Bidomain with bath, this is where we set up the electrodes
211  SetElectrodes();
212 }
213 
214 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
216 {
218 }
219 
220 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
222 {
223  this->mpBoundaryConditionsContainer = pBcc;
224 }
225 
226 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
228 {
229  if ( mpCardiacTissue == NULL ) // if tissue is NULL, Initialise() probably hasn't been called
230  {
231  EXCEPTION("Cardiac tissue is null, Initialise() probably hasn't been called");
232  }
233  if ( HeartConfig::Instance()->GetSimulationDuration() <= mCurrentTime)
234  {
235  EXCEPTION("End time should be in the future");
236  }
237  if (mPrintOutput)
238  {
239  if( (HeartConfig::Instance()->GetOutputDirectory()=="") || (HeartConfig::Instance()->GetOutputFilenamePrefix()==""))
240  {
241  EXCEPTION("Either explicitly specify not to print output (call PrintOutput(false)) or specify the output directory and filename prefix");
242  }
243  }
244 
245  double end_time = HeartConfig::Instance()->GetSimulationDuration();
246  double pde_time = HeartConfig::Instance()->GetPdeTimeStep();
247 
248  /*
249  * MatrixIsConstant stuff requires CONSTANT dt - do some checks to make sure
250  * the TimeStepper won't find non-constant dt.
251  * Note: printing_time does not have to divide end_time, but dt must divide
252  * printing_time and end_time.
253  * HeartConfig checks pde_dt divides printing dt.
254  */
256  if( fabs(end_time - pde_time*round(end_time/pde_time)) > 1e-10 )
257  {
258  EXCEPTION("PDE timestep does not seem to divide end time - check parameters");
259  }
260 }
261 
262 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
264 {
265  DistributedVectorFactory* p_factory = mpMesh->GetDistributedVectorFactory();
266  Vec initial_condition = p_factory->CreateVec(PROBLEM_DIM);
267  DistributedVector ic = p_factory->CreateDistributedVector(initial_condition);
268  std::vector<DistributedVector::Stripe> stripe;
269  stripe.reserve(PROBLEM_DIM);
270 
271  for (unsigned i=0; i<PROBLEM_DIM; i++)
272  {
273  stripe.push_back(DistributedVector::Stripe(ic, i));
274  }
275 
276  for (DistributedVector::Iterator index = ic.Begin();
277  index != ic.End();
278  ++index)
279  {
280  stripe[0][index] = mpCardiacTissue->GetCardiacCell(index.Global)->GetVoltage();
281  if (PROBLEM_DIM==2)
282  {
283  stripe[1][index] = 0;
284  }
285  }
286 
287  ic.Restore();
288 
289  return initial_condition;
290 }
291 
292 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
294 {
295  /*
296  * If this fails the mesh has already been set. We assert rather throw
297  * an exception to avoid a memory leak when checking it throws correctly.
298  */
299  assert(mpMesh == NULL);
300  assert(pMesh != NULL);
301  mAllocatedMemoryForMesh = false;
302  mpMesh = pMesh;
303 }
304 
305 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
307 {
308  mPrintOutput = printOutput;
309 }
310 
311 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
313 {
314  mWriteInfo = writeInfo;
315 }
316 
317 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
319 {
320  return mSolution;
321 }
322 
323 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
325 {
326  return mpMesh->GetDistributedVectorFactory()->CreateDistributedVector(mSolution);
327 }
328 
329 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
331 {
332  return mCurrentTime;
333 }
334 
335 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
337 {
338  assert (mpMesh);
339  return *mpMesh;
340 }
341 
342 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
344 {
345  if (mpCardiacTissue == NULL)
346  {
347  EXCEPTION("Tissue not yet set up, you may need to call Initialise() before GetTissue().");
348  }
349  return mpCardiacTissue;
350 }
351 
352 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
354  bool useAdaptivity,
356 {
357  if (useAdaptivity)
358  {
359  assert(pController);
360  mpTimeAdaptivityController = pController;
361  }
362  else
363  {
364  mpTimeAdaptivityController = NULL;
365  }
366 }
367 
368 
369 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
371 {
372  PreSolveChecks();
373 
374  std::vector<double> additional_stopping_times;
375  SetUpAdditionalStoppingTimes(additional_stopping_times);
376 
377  TimeStepper stepper(mCurrentTime,
378  HeartConfig::Instance()->GetSimulationDuration(),
379  HeartConfig::Instance()->GetPrintingTimeStep(),
380  false,
381  additional_stopping_times);
382  // Note that SetUpAdditionalStoppingTimes is a method from the BidomainWithBath class it adds
383  // electrode events into the regular time-stepping
384  // EXCEPTION("Electrode switch on/off events should coincide with printing time steps.");
385 
386  if (!mpBoundaryConditionsContainer) // the user didn't supply a bcc
387  {
388  // Set up the default bcc
389  mpDefaultBoundaryConditionsContainer.reset(new BoundaryConditionsContainer<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>);
390  for (unsigned problem_index=0; problem_index<PROBLEM_DIM; problem_index++)
391  {
392  mpDefaultBoundaryConditionsContainer->DefineZeroNeumannOnMeshBoundary(mpMesh, problem_index);
393  }
394  mpBoundaryConditionsContainer = mpDefaultBoundaryConditionsContainer;
395  }
396 
397  assert(mpSolver==NULL);
398  mpSolver = CreateSolver(); // passes mpBoundaryConditionsContainer to solver
399 
400  // If we have already run a simulation, use the old solution as initial condition
401  Vec initial_condition;
402  if (mSolution)
403  {
404  initial_condition = mSolution;
405  }
406  else
407  {
408  initial_condition = CreateInitialCondition();
409  }
410 
411  std::string progress_reporter_dir;
412 
413  if (mPrintOutput)
414  {
415  HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT);
416  bool extending_file = false;
417  try
418  {
419  extending_file = InitialiseWriter();
420  }
421  catch (Exception& e)
422  {
423  delete mpWriter;
424  mpWriter = NULL;
425  delete mpSolver;
426  if (mSolution != initial_condition)
427  {
428  /*
429  * A PETSc Vec is a pointer, so we *don't* need to free the memory if it is
430  * freed somewhere else (e.g. in the destructor). If this is a resumed solution
431  * we set initial_condition = mSolution earlier. mSolution is going to be
432  * cleaned up in the constructor. So, only PetscTools::Destroy( initial_condition ) when
433  * it is not equal to mSolution.
434  */
435  PetscTools::Destroy(initial_condition);
436  }
437  throw e;
438  }
439 
440  /*
441  * If we are resuming a simulation (i.e. mSolution already exists) and
442  * we are extending a .h5 file that already exists then there is no need
443  * to write the initial condition to file - it is already there as the
444  * final solution of the previous run.
445  */
446  if (!(mSolution && extending_file))
447  {
448  WriteOneStep(stepper.GetTime(), initial_condition);
449  mpWriter->AdvanceAlongUnlimitedDimension();
450  }
451  HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT);
452 
453  progress_reporter_dir = HeartConfig::Instance()->GetOutputDirectory();
454  }
455  else
456  {
457  progress_reporter_dir = ""; // progress printed to CHASTE_TEST_OUTPUT
458  }
459  BOOST_FOREACH(boost::shared_ptr<AbstractOutputModifier> p_output_modifier, mOutputModifiers)
460  {
461  p_output_modifier->InitialiseAtStart(this->mpMesh->GetDistributedVectorFactory());
462  p_output_modifier->ProcessSolutionAtTimeStep(stepper.GetTime(), initial_condition, PROBLEM_DIM);
463  }
464 
465 
466  /*
467  * Create a progress reporter so users can track how much has gone and
468  * estimate how much time is left. Note this has to be done after the
469  * InitialiseWriter above (if mPrintOutput==true).
470  */
471  ProgressReporter progress_reporter(progress_reporter_dir,
472  mCurrentTime,
473  HeartConfig::Instance()->GetSimulationDuration());
474  progress_reporter.Update(mCurrentTime);
475 
476  mpSolver->SetTimeStep(HeartConfig::Instance()->GetPdeTimeStep());
477  if (mpTimeAdaptivityController)
478  {
479  mpSolver->SetTimeAdaptivityController(mpTimeAdaptivityController);
480  }
481 
482  while (!stepper.IsTimeAtEnd())
483  {
484  // Solve from now up to the next printing time
485  mpSolver->SetTimes(stepper.GetTime(), stepper.GetNextTime());
486  mpSolver->SetInitialCondition( initial_condition );
487 
488  AtBeginningOfTimestep(stepper.GetTime());
489 
490  try
491  {
492  try
493  {
494  mSolution = mpSolver->Solve();
495  }
496  catch (const Exception &e)
497  {
498 #ifndef NDEBUG
500 #endif
501  throw e;
502  }
503 #ifndef NDEBUG
505 #endif
506  }
507  catch (const Exception& e)
508  {
509  // Free memory
510  delete mpSolver;
511  mpSolver = NULL;
512  if (initial_condition != mSolution)
513  {
514  /*
515  * A PETSc Vec is a pointer, so we *don't* need to free the memory if it is
516  * freed somewhere else (e.g. in the destructor). Later, in this while loop
517  * we will set initial_condition = mSolution (or, if this is a resumed solution
518  * it may also have been done when initial_condition was created). mSolution
519  * is going to be cleaned up in the destructor. So, only PetscTools::Destroy()
520  * initial_condition when it is not equal to mSolution (see #1695).
521  */
522  PetscTools::Destroy(initial_condition);
523  }
524 
525  // Re-throw
527  CloseFilesAndPostProcess();
528 
529  throw e;
530  }
531 
532  // Free old initial condition
533  HeartEventHandler::BeginEvent(HeartEventHandler::COMMUNICATION);
534  PetscTools::Destroy(initial_condition);
535  HeartEventHandler::EndEvent(HeartEventHandler::COMMUNICATION);
536 
537  // Initial condition for next loop is current solution
538  initial_condition = mSolution;
539 
540  // Update the current time
541  stepper.AdvanceOneTimeStep();
542  mCurrentTime = stepper.GetTime();
543 
544  // Print out details at current time if asked for
545  if (mWriteInfo)
546  {
547  HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT);
548  WriteInfo(stepper.GetTime());
549  HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT);
550  }
551 
552  BOOST_FOREACH(boost::shared_ptr<AbstractOutputModifier> p_output_modifier, mOutputModifiers)
553  {
554  p_output_modifier->ProcessSolutionAtTimeStep(stepper.GetTime(), mSolution, PROBLEM_DIM);
555  }
556  if (mPrintOutput)
557  {
558  // Writing data out to the file <FilenamePrefix>.dat
559  HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT);
560  WriteOneStep(stepper.GetTime(), mSolution);
561  // Just flags that we've finished a time-step; won't actually 'extend' unless new data is written.
562  mpWriter->AdvanceAlongUnlimitedDimension();
563 
564  HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT);
565  }
566 
567  progress_reporter.Update(stepper.GetTime());
568 
569  OnEndOfTimestep(stepper.GetTime());
570  }
571 
572  // Free solver
573  delete mpSolver;
574  mpSolver = NULL;
575 
576  // Close the file that stores voltage values
577  progress_reporter.PrintFinalising();
578  BOOST_FOREACH(boost::shared_ptr<AbstractOutputModifier> p_output_modifier, mOutputModifiers)
579  {
580  p_output_modifier->FinaliseAtEnd();
581  }
582  CloseFilesAndPostProcess();
583  HeartEventHandler::EndEvent(HeartEventHandler::EVERYTHING);
584 }
585 
586 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
588 {
589  // Close files
590  if (!mPrintOutput)
591  {
592  // Nothing to do
593  return;
594  }
595  delete mpWriter;
596  mpWriter = NULL;
597 
598  FileFinder test_output(HeartConfig::Instance()->GetOutputDirectory(), RelativeTo::ChasteTestOutput);
599 
600  /********************************************************************************
601  * Run all post processing.
602  *
603  * The PostProcessingWriter class examines what is requested in HeartConfig and
604  * adds the relevant data to the HDF5 file.
605  * This is converted to different visualizer formats along with the solution
606  * in the DATA_CONVERSION block below.
607  *********************************************************************************/
608 
609  HeartEventHandler::BeginEvent(HeartEventHandler::POST_PROC);
610  if(HeartConfig::Instance()->IsPostProcessingRequested())
611  {
613  test_output,
614  HeartConfig::Instance()->GetOutputFilenamePrefix());
615  post_writer.WritePostProcessingFiles();
616  }
617  HeartEventHandler::EndEvent(HeartEventHandler::POST_PROC);
618 
619  /********************************************************************************************
620  * Convert HDF5 datasets (solution and postprocessing maps) to different visualizer formats
621  ********************************************************************************************/
622 
623  HeartEventHandler::BeginEvent(HeartEventHandler::DATA_CONVERSION);
624  // Only if results files were written and we are outputting all nodes
625  if (mNodesToOutput.empty())
626  {
628  {
629  // Convert simulation data to Meshalyzer format
631  HeartConfig::Instance()->GetOutputFilenamePrefix(),
632  mpMesh,
633  HeartConfig::Instance()->GetOutputUsingOriginalNodeOrdering(),
634  HeartConfig::Instance()->GetVisualizerOutputPrecision() );
635  std::string subdirectory_name = converter.GetSubdirectory();
636  HeartConfig::Instance()->Write(false, subdirectory_name);
637  }
638 
639  if (HeartConfig::Instance()->GetVisualizeWithCmgui())
640  {
641  // Convert simulation data to Cmgui format
642  Hdf5ToCmguiConverter<ELEMENT_DIM,SPACE_DIM> converter(test_output,
643  HeartConfig::Instance()->GetOutputFilenamePrefix(),
644  mpMesh,
645  GetHasBath(),
646  HeartConfig::Instance()->GetVisualizerOutputPrecision() );
647  std::string subdirectory_name = converter.GetSubdirectory();
648  HeartConfig::Instance()->Write(false, subdirectory_name);
649  }
650 
651  if (HeartConfig::Instance()->GetVisualizeWithVtk())
652  {
653  // Convert simulation data to VTK format
654  Hdf5ToVtkConverter<ELEMENT_DIM,SPACE_DIM> converter(test_output,
655  HeartConfig::Instance()->GetOutputFilenamePrefix(),
656  mpMesh,
657  false,
658  HeartConfig::Instance()->GetOutputUsingOriginalNodeOrdering());
659  std::string subdirectory_name = converter.GetSubdirectory();
660  HeartConfig::Instance()->Write(false, subdirectory_name);
661  }
662 
663  if (HeartConfig::Instance()->GetVisualizeWithParallelVtk())
664  {
665  // Convert simulation data to parallel VTK (pvtu) format
666  Hdf5ToVtkConverter<ELEMENT_DIM,SPACE_DIM> converter(test_output,
667  HeartConfig::Instance()->GetOutputFilenamePrefix(),
668  mpMesh,
669  true,
670  HeartConfig::Instance()->GetOutputUsingOriginalNodeOrdering());
671  std::string subdirectory_name = converter.GetSubdirectory();
672  HeartConfig::Instance()->Write(false, subdirectory_name);
673  }
674  }
675  HeartEventHandler::EndEvent(HeartEventHandler::DATA_CONVERSION);
676 }
677 
678 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
680 {
681  if (!extending)
682  {
683  if ( mNodesToOutput.empty() )
684  {
685  //Set writer to output all nodes
686  mpWriter->DefineFixedDimension( mpMesh->GetNumNodes() );
687  }
688  else
689  {
690  //Output only the nodes indicted
691  mpWriter->DefineFixedDimension( mNodesToOutput, mpMesh->GetNumNodes() );
692  }
693  //mNodeColumnId = mpWriter->DefineVariable("Node", "dimensionless");
694  mVoltageColumnId = mpWriter->DefineVariable("V","mV");
695 
696  // Only used to get an estimate of the # of timesteps below
697  TimeStepper stepper(mCurrentTime,
698  HeartConfig::Instance()->GetSimulationDuration(),
699  HeartConfig::Instance()->GetPrintingTimeStep());
700 
701  mpWriter->DefineUnlimitedDimension("Time","msecs", stepper.EstimateTimeSteps()+1); // plus one for start and end points
702  }
703  else
704  {
705  mVoltageColumnId = mpWriter->GetVariableByName("V");
706  }
707 }
708 
709 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
711 {
712  mExtraVariablesId.clear();
713  // Check if any extra output variables have been requested
714  if (HeartConfig::Instance()->GetOutputVariablesProvided())
715  {
716  // Get their names in a vector
717  std::vector<std::string> output_variables;
718  HeartConfig::Instance()->GetOutputVariables(output_variables);
719  const unsigned num_vars = output_variables.size();
720  mExtraVariablesId.reserve(num_vars);
721 
722  // Loop over them
723  for (unsigned var_index=0; var_index<num_vars; var_index++)
724  {
725  // Get variable name
726  std::string var_name = output_variables[var_index];
727 
728  // Register it (or look it up) in the data writer
729  unsigned column_id;
730  if (extending)
731  {
732  column_id = this->mpWriter->GetVariableByName(var_name);
733  }
734  else
735  {
736  // Difficult to specify the units, as different cell models
737  // at different points in the mesh could be using different units.
738  column_id = this->mpWriter->DefineVariable(var_name, "unknown_units");
739  }
740 
741  // Store column id
742  mExtraVariablesId.push_back(column_id);
743  }
744  }
745 }
746 
747 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
749 {
750  // Get the variable names in a vector
751  std::vector<std::string> output_variables;
752  unsigned num_vars = mExtraVariablesId.size();
753  if (num_vars > 0)
754  {
755  HeartConfig::Instance()->GetOutputVariables(output_variables);
756  }
757  assert(output_variables.size() == num_vars);
758 
759  // Loop over the requested variables
760  for (unsigned var_index=0; var_index<num_vars; var_index++)
761  {
762  // Create vector for storing values over the local nodes
763  Vec variable_data = this->mpMesh->GetDistributedVectorFactory()->CreateVec();
764  DistributedVector distributed_var_data = this->mpMesh->GetDistributedVectorFactory()->CreateDistributedVector(variable_data);
765 
766  // Loop over the local nodes and gather the data
767  for (DistributedVector::Iterator index = distributed_var_data.Begin();
768  index!= distributed_var_data.End();
769  ++index)
770  {
771  // If the region is in the bath
772  if (HeartRegionCode::IsRegionBath( this->mpMesh->GetNode( index.Global )->GetRegion() ))
773  {
774  // Then we just pad the output with zeros, user currently needs to find a nice
775  // way to deal with this in processing and visualization.
776  distributed_var_data[index] = 0.0;
777  }
778  else
779  {
780  // Find the variable in the cell model and store its value
781  distributed_var_data[index] = this->mpCardiacTissue->GetCardiacCell(index.Global)->
782  GetAnyVariable(output_variables[var_index], mCurrentTime);
783  }
784  }
785  distributed_var_data.Restore();
786 
787  // Write it to disc
788  this->mpWriter->PutVector(mExtraVariablesId[var_index], variable_data);
789 
790  PetscTools::Destroy(variable_data);
791  }
792 }
793 
794 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
796 {
797  bool extend_file = (mSolution != NULL);
798 
799  // I think this is impossible to trip; certainly it's very difficult!
800  assert(!mpWriter);
801 
802  if (extend_file)
803  {
805  + "/" + HeartConfig::Instance()->GetOutputFilenamePrefix() + ".h5",
807  //We are going to test for existence before creating the file.
808  //Therefore we should make sure that this existence test is thread-safe.
809  //(If another process creates the file too early then we may get the wrong answer to the
810  //existence question).
811  PetscTools::Barrier("InitialiseWriter::Extension check");
812  if (!h5_file.Exists())
813  {
814  extend_file = false;
815  }
816  else // if it does exist check that it is sensible to extend it by running from the archive we loaded.
817  {
818  Hdf5DataReader reader(HeartConfig::Instance()->GetOutputDirectory(),
819  HeartConfig::Instance()->GetOutputFilenamePrefix(),
820  true);
821  std::vector<double> times = reader.GetUnlimitedDimensionValues();
822  if (times.back() > mCurrentTime)
823  {
824  EXCEPTION("Attempting to extend " << h5_file.GetAbsolutePath() <<
825  " with results from time = " << mCurrentTime <<
826  ", but it already contains results up to time = " << times.back() << "."
827  " Calling HeartConfig::Instance()->SetOutputDirectory() before Solve() will direct results elsewhere.");
828  }
829  }
830  PetscTools::Barrier("InitialiseWriter::Extension check");
831  }
832  mpWriter = new Hdf5DataWriter(*mpMesh->GetDistributedVectorFactory(),
835  !extend_file, // don't clear directory if extension requested
836  extend_file);
837 
838 
839  // Define columns, or get the variable IDs from the writer
840  DefineWriterColumns(extend_file);
841 
842  //Possibility of applying a permutation
843  if (HeartConfig::Instance()->GetOutputUsingOriginalNodeOrdering())
844  {
845  bool success = mpWriter->ApplyPermutation(mpMesh->rGetNodePermutation(), true/*unsafe mode - extending*/);
846  if (success == false)
847  {
848  //It's not really a permutation, so reset
850  }
851  }
852 
853  if (!extend_file)
854  {
855  mpWriter->EndDefineMode();
856  }
857 
858  return extend_file;
859 }
860 
861 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
863 {
864  mNodesToOutput = nodesToOutput;
865 }
866 
867 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
869 {
870  if( (HeartConfig::Instance()->GetOutputDirectory()=="") || (HeartConfig::Instance()->GetOutputFilenamePrefix()==""))
871  {
872  EXCEPTION("Data reader invalid as data writer cannot be initialised");
873  }
874  return Hdf5DataReader(HeartConfig::Instance()->GetOutputDirectory(), HeartConfig::Instance()->GetOutputFilenamePrefix());
875 }
876 
877 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
879 {
880  return false;
881 }
882 
883 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
885 {
886 }
887 
888 
890 // Explicit instantiation
892 
893 // Monodomain
894 template class AbstractCardiacProblem<1,1,1>;
895 template class AbstractCardiacProblem<1,2,1>;
896 template class AbstractCardiacProblem<1,3,1>;
897 template class AbstractCardiacProblem<2,2,1>;
898 template class AbstractCardiacProblem<3,3,1>;
899 
900 // Bidomain
901 template class AbstractCardiacProblem<1,1,2>;
902 template class AbstractCardiacProblem<2,2,2>;
903 template class AbstractCardiacProblem<3,3,2>;
904 
905 // Extended Bidomain
906 template class AbstractCardiacProblem<1,1,3>;
907 template class AbstractCardiacProblem<2,2,3>;
908 template class AbstractCardiacProblem<3,3,3>;
std::string GetOutputFilenamePrefix() const
double GetSimulationDuration() const
static bool IsRegionBath(HeartRegionType regionId)
static void Barrier(const std::string callerId="")
Definition: PetscTools.cpp:134
void PrintOutput(bool rPrintOutput)
void Update(double currentTime)
std::string GetAbsolutePath() const
Definition: FileFinder.cpp:221
double GetTime() const
DistributedVector CreateDistributedVector(Vec vec, bool readOnly=false)
void Write(bool useArchiveLocationInfo=false, std::string subfolderName="output")
void GetSlabDimensions(c_vector< double, 3 > &slabDimensions) const
#define EXCEPTION(message)
Definition: Exception.hpp:143
AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > & rGetMesh()
void SetOutputUsingOriginalNodeOrdering(bool useOriginal)
void GetOutputVariables(std::vector< std::string > &rOutputVariables) const
DistributedTetrahedralMeshPartitionType::type GetMeshPartitioning() const
double GetPdeTimeStep() const
#define NEVER_REACHED
Definition: Exception.hpp:206
std::string GetMeshName() const
void GetFibreLength(c_vector< double, 1 > &fibreLength) const
void AdvanceOneTimeStep()
bool IsTimeAtEnd() const
void GetSheetDimensions(c_vector< double, 2 > &sheetDimensions) const
void SetUseTimeAdaptivityController(bool useAdaptivity, AbstractTimeAdaptivityController *pController=NULL)
AbstractCardiacTissue< ELEMENT_DIM, SPACE_DIM > * GetTissue()
static void ReplicateException(bool flag)
Definition: PetscTools.cpp:198
std::vector< double > GetUnlimitedDimensionValues()
std::vector< unsigned > mNodesToOutput
unsigned EstimateTimeSteps() const
static void Destroy(Vec &rVec)
Definition: PetscTools.hpp:351
void SetMesh(AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > *pMesh)
double GetInterNodeSpace() const
static bool IsParallel()
Definition: PetscTools.cpp:97
bool Exists() const
Definition: FileFinder.cpp:180
AbstractCardiacCellFactory< ELEMENT_DIM, SPACE_DIM > * mpCellFactory
void SetOutputNodes(std::vector< unsigned > &rNodesToOutput)
virtual void DefineWriterColumns(bool extending)
void SetBoundaryConditionsContainer(BccType pBcc)
bool GetVisualizeWithMeshalyzer() const
std::string GetOutputDirectory() const
static std::string GetChasteTestOutputDirectory()
double GetNextTime() const
void SetWriteInfo(bool writeInfo=true)
DistributedVector GetSolutionDistributedVector()
std::string GetShortMessage() const
Definition: Exception.cpp:92
static HeartConfig * Instance()
void DefineExtraVariablesWriterColumns(bool extending)