Chaste  Release::2017.1
AbstractCardiacProblem.cpp
1 /*
2 
3 Copyright (c) 2005-2017, 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  mUseHdf5DataWriterCache(false),
69  mHdf5DataWriterChunkSizeAndAlignment(0)
70 {
71  assert(mNodesToOutput.empty());
72  if (!mpCellFactory)
73  {
74  EXCEPTION("AbstractCardiacProblem: Please supply a cell factory pointer to your cardiac problem constructor.");
75  }
76  HeartEventHandler::BeginEvent(HeartEventHandler::EVERYTHING);
77 }
78 
79 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
81  // It doesn't really matter what we initialise these to, as they'll be overwritten by
82  // the serialization methods
83  : mMeshFilename(""),
84  mAllocatedMemoryForMesh(false), // Handled by AbstractCardiacTissue
85  mWriteInfo(false),
86  mPrintOutput(true),
87  mVoltageColumnId(UINT_MAX),
88  mTimeColumnId(UINT_MAX),
89  mNodeColumnId(UINT_MAX),
90  mpCardiacTissue(NULL),
91  mpSolver(NULL),
92  mpCellFactory(NULL),
93  mpMesh(NULL),
94  mSolution(NULL),
95  mCurrentTime(0.0),
97  mpWriter(NULL),
100 {
101 }
102 
103 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
105 {
106  delete mpCardiacTissue;
107  if (mSolution)
108  {
110  }
111 
113  {
114  delete mpMesh;
115  }
116 }
117 
118 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
120 {
121  HeartEventHandler::BeginEvent(HeartEventHandler::READ_MESH);
122  if (mpMesh)
123  {
125  {
126  WARNING("Using a non-distributed mesh in a parallel simulation is not a good idea.");
127  }
128  }
129  else
130  {
131  // If no mesh has been passed, we get it from the configuration file
132  try
133  {
134  if (HeartConfig::Instance()->GetLoadMesh())
135  {
137  std::shared_ptr<AbstractMeshReader<ELEMENT_DIM, SPACE_DIM> > p_mesh_reader
138  = GenericMeshReader<ELEMENT_DIM, SPACE_DIM>(HeartConfig::Instance()->GetMeshName());
139  mpMesh->ConstructFromMeshReader(*p_mesh_reader);
140  }
141  else if (HeartConfig::Instance()->GetCreateMesh())
142  {
144  assert(HeartConfig::Instance()->GetSpaceDimension()==SPACE_DIM);
145  double inter_node_space = HeartConfig::Instance()->GetInterNodeSpace();
146 
147  switch(HeartConfig::Instance()->GetSpaceDimension())
148  {
149  case 1:
150  {
151  c_vector<double, 1> fibre_length;
152  HeartConfig::Instance()->GetFibreLength(fibre_length);
153  mpMesh->ConstructRegularSlabMesh(inter_node_space, fibre_length[0]);
154  break;
155  }
156  case 2:
157  {
158  c_vector<double, 2> sheet_dimensions; //cm
159  HeartConfig::Instance()->GetSheetDimensions(sheet_dimensions);
160  mpMesh->ConstructRegularSlabMesh(inter_node_space, sheet_dimensions[0], sheet_dimensions[1]);
161  break;
162  }
163  case 3:
164  {
165  c_vector<double, 3> slab_dimensions; //cm
166  HeartConfig::Instance()->GetSlabDimensions(slab_dimensions);
167  mpMesh->ConstructRegularSlabMesh(inter_node_space, slab_dimensions[0], slab_dimensions[1], slab_dimensions[2]);
168  break;
169  }
170  default:
172  }
173  }
174  else
175  {
177  }
178 
180  }
181  catch (Exception& e)
182  {
183  EXCEPTION(std::string("No mesh given: define it in XML parameters file or call SetMesh()\n") + e.GetShortMessage());
184  }
185  }
186  mpCellFactory->SetMesh( mpMesh );
187  HeartEventHandler::EndEvent(HeartEventHandler::READ_MESH);
188 
189  HeartEventHandler::BeginEvent(HeartEventHandler::INITIALISE);
190 
191  // If the user requested transmural stuff, we fill in the mCellHeterogeneityAreas here
192  if (HeartConfig::Instance()->AreCellularTransmuralHeterogeneitiesRequested())
193  {
194  mpCellFactory->FillInCellularTransmuralAreas();
195  }
196 
197  delete mpCardiacTissue; // In case we're called twice
199 
200  HeartEventHandler::EndEvent(HeartEventHandler::INITIALISE);
201 
202  // Delete any previous solution, so we get a fresh initial condition
203  if (mSolution)
204  {
205  HeartEventHandler::BeginEvent(HeartEventHandler::COMMUNICATION);
207  mSolution = NULL;
208  HeartEventHandler::EndEvent(HeartEventHandler::COMMUNICATION);
209  }
210 
211  // Always start at time zero
212  mCurrentTime = 0.0;
213 
214  // For Bidomain with bath, this is where we set up the electrodes
215  SetElectrodes();
216 }
217 
218 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
220 {
222 }
223 
224 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
226 {
227  this->mpBoundaryConditionsContainer = pBcc;
228 }
229 
230 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
232 {
233  if (mpCardiacTissue == NULL) // if tissue is NULL, Initialise() probably hasn't been called
234  {
235  EXCEPTION("Cardiac tissue is null, Initialise() probably hasn't been called");
236  }
237  if (HeartConfig::Instance()->GetSimulationDuration() <= mCurrentTime)
238  {
239  EXCEPTION("End time should be in the future");
240  }
241  if (mPrintOutput)
242  {
243  if ((HeartConfig::Instance()->GetOutputDirectory()=="") || (HeartConfig::Instance()->GetOutputFilenamePrefix()==""))
244  {
245  EXCEPTION("Either explicitly specify not to print output (call PrintOutput(false)) or specify the output directory and filename prefix");
246  }
247  }
248 
249  double end_time = HeartConfig::Instance()->GetSimulationDuration();
250  double pde_time = HeartConfig::Instance()->GetPdeTimeStep();
251 
252  /*
253  * MatrixIsConstant stuff requires CONSTANT dt - do some checks to make sure
254  * the TimeStepper won't find non-constant dt.
255  * Note: printing_time does not have to divide end_time, but dt must divide
256  * printing_time and end_time.
257  * HeartConfig checks pde_dt divides printing dt.
258  */
260  if (fabs(end_time - pde_time*round(end_time/pde_time)) > 1e-10)
261  {
262  EXCEPTION("PDE timestep does not seem to divide end time - check parameters");
263  }
264 }
265 
266 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
268 {
269  DistributedVectorFactory* p_factory = mpMesh->GetDistributedVectorFactory();
270  Vec initial_condition = p_factory->CreateVec(PROBLEM_DIM);
271  DistributedVector ic = p_factory->CreateDistributedVector(initial_condition);
272  std::vector<DistributedVector::Stripe> stripe;
273  stripe.reserve(PROBLEM_DIM);
274 
275  for (unsigned i=0; i<PROBLEM_DIM; i++)
276  {
277  stripe.push_back(DistributedVector::Stripe(ic, i));
278  }
279 
280  for (DistributedVector::Iterator index = ic.Begin();
281  index != ic.End();
282  ++index)
283  {
284  stripe[0][index] = mpCardiacTissue->GetCardiacCell(index.Global)->GetVoltage();
285  if (PROBLEM_DIM==2)
286  {
287  stripe[1][index] = 0;
288  }
289  }
290 
291  ic.Restore();
292 
293  return initial_condition;
294 }
295 
296 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
298 {
299  /*
300  * If this fails the mesh has already been set. We assert rather throw
301  * an exception to avoid a memory leak when checking it throws correctly.
302  */
303  assert(mpMesh == NULL);
304  assert(pMesh != NULL);
305  mAllocatedMemoryForMesh = false;
306  mpMesh = pMesh;
307 }
308 
309 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
311 {
312  mPrintOutput = printOutput;
313 }
314 
315 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
317 {
318  mWriteInfo = writeInfo;
319 }
320 
321 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
323 {
324  return mSolution;
325 }
326 
327 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
329 {
330  return mpMesh->GetDistributedVectorFactory()->CreateDistributedVector(mSolution);
331 }
332 
333 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
335 {
336  return mCurrentTime;
337 }
338 
339 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
341 {
342  assert (mpMesh);
343  return *mpMesh;
344 }
345 
346 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
348 {
349  if (mpCardiacTissue == NULL)
350  {
351  EXCEPTION("Tissue not yet set up, you may need to call Initialise() before GetTissue().");
352  }
353  return mpCardiacTissue;
354 }
355 
356 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
358  bool useAdaptivity,
360 {
361  if (useAdaptivity)
362  {
363  assert(pController);
364  mpTimeAdaptivityController = pController;
365  }
366  else
367  {
369  }
370 }
371 
372 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
374 {
375  PreSolveChecks();
376 
377  std::vector<double> additional_stopping_times;
378  SetUpAdditionalStoppingTimes(additional_stopping_times);
379 
380  TimeStepper stepper(mCurrentTime,
381  HeartConfig::Instance()->GetSimulationDuration(),
382  HeartConfig::Instance()->GetPrintingTimeStep(),
383  false,
384  additional_stopping_times);
385  // Note that SetUpAdditionalStoppingTimes is a method from the BidomainWithBath class it adds
386  // electrode events into the regular time-stepping
387  // EXCEPTION("Electrode switch on/off events should coincide with printing time steps.");
388 
389  if (!mpBoundaryConditionsContainer) // the user didn't supply a bcc
390  {
391  // Set up the default bcc
393  for (unsigned problem_index=0; problem_index<PROBLEM_DIM; problem_index++)
394  {
395  mpDefaultBoundaryConditionsContainer->DefineZeroNeumannOnMeshBoundary(mpMesh, problem_index);
396  }
398  }
399 
400  assert(mpSolver==NULL);
401  mpSolver = CreateSolver(); // passes mpBoundaryConditionsContainer to solver
402 
403  // If we have already run a simulation, use the old solution as initial condition
404  Vec initial_condition;
405  if (mSolution)
406  {
407  initial_condition = mSolution;
408  }
409  else
410  {
411  initial_condition = CreateInitialCondition();
412  }
413 
414  std::string progress_reporter_dir;
415 
416  if (mPrintOutput)
417  {
418  HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT);
419  bool extending_file = false;
420  try
421  {
422  extending_file = InitialiseWriter();
423  }
424  catch (Exception& e)
425  {
426  delete mpWriter;
427  mpWriter = NULL;
428  delete mpSolver;
429  if (mSolution != initial_condition)
430  {
431  /*
432  * A PETSc Vec is a pointer, so we *don't* need to free the memory if it is
433  * freed somewhere else (e.g. in the destructor). If this is a resumed solution
434  * we set initial_condition = mSolution earlier. mSolution is going to be
435  * cleaned up in the constructor. So, only PetscTools::Destroy( initial_condition ) when
436  * it is not equal to mSolution.
437  */
438  PetscTools::Destroy(initial_condition);
439  }
440  throw e;
441  }
442 
443  /*
444  * If we are resuming a simulation (i.e. mSolution already exists) and
445  * we are extending a .h5 file that already exists then there is no need
446  * to write the initial condition to file - it is already there as the
447  * final solution of the previous run.
448  */
449  if (!(mSolution && extending_file))
450  {
451  WriteOneStep(stepper.GetTime(), initial_condition);
453  }
454  HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT);
455 
456  progress_reporter_dir = HeartConfig::Instance()->GetOutputDirectory();
457  }
458  else
459  {
460  progress_reporter_dir = ""; // progress printed to CHASTE_TEST_OUTPUT
461  }
462  BOOST_FOREACH(boost::shared_ptr<AbstractOutputModifier> p_output_modifier, mOutputModifiers)
463  {
464  p_output_modifier->InitialiseAtStart(this->mpMesh->GetDistributedVectorFactory());
465  p_output_modifier->ProcessSolutionAtTimeStep(stepper.GetTime(), initial_condition, PROBLEM_DIM);
466  }
467 
468 
469  /*
470  * Create a progress reporter so users can track how much has gone and
471  * estimate how much time is left. Note this has to be done after the
472  * InitialiseWriter above (if mPrintOutput==true).
473  */
474  ProgressReporter progress_reporter(progress_reporter_dir,
475  mCurrentTime,
476  HeartConfig::Instance()->GetSimulationDuration());
477  progress_reporter.Update(mCurrentTime);
478 
479  mpSolver->SetTimeStep(HeartConfig::Instance()->GetPdeTimeStep());
481  {
482  mpSolver->SetTimeAdaptivityController(mpTimeAdaptivityController);
483  }
484 
485  while (!stepper.IsTimeAtEnd())
486  {
487  // Solve from now up to the next printing time
488  mpSolver->SetTimes(stepper.GetTime(), stepper.GetNextTime());
489  mpSolver->SetInitialCondition( initial_condition );
490 
491  AtBeginningOfTimestep(stepper.GetTime());
492 
493  try
494  {
495  try
496  {
497  mSolution = mpSolver->Solve();
498  }
499  catch (const Exception &e)
500  {
501 #ifndef NDEBUG
503 #endif
504  throw e;
505  }
506 #ifndef NDEBUG
508 #endif
509  }
510  catch (const Exception& e)
511  {
512  // Free memory
513  delete mpSolver;
514  mpSolver = NULL;
515  if (initial_condition != mSolution)
516  {
517  /*
518  * A PETSc Vec is a pointer, so we *don't* need to free the memory if it is
519  * freed somewhere else (e.g. in the destructor). Later, in this while loop
520  * we will set initial_condition = mSolution (or, if this is a resumed solution
521  * it may also have been done when initial_condition was created). mSolution
522  * is going to be cleaned up in the destructor. So, only PetscTools::Destroy()
523  * initial_condition when it is not equal to mSolution (see #1695).
524  */
525  PetscTools::Destroy(initial_condition);
526  }
527 
528  // Re-throw
531 
532  throw e;
533  }
534 
535  // Free old initial condition
536  HeartEventHandler::BeginEvent(HeartEventHandler::COMMUNICATION);
537  PetscTools::Destroy(initial_condition);
538  HeartEventHandler::EndEvent(HeartEventHandler::COMMUNICATION);
539 
540  // Initial condition for next loop is current solution
541  initial_condition = mSolution;
542 
543  // Update the current time
544  stepper.AdvanceOneTimeStep();
545  mCurrentTime = stepper.GetTime();
546 
547  // Print out details at current time if asked for
548  if (mWriteInfo)
549  {
550  HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT);
551  WriteInfo(stepper.GetTime());
552  HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT);
553  }
554 
555  BOOST_FOREACH(boost::shared_ptr<AbstractOutputModifier> p_output_modifier, mOutputModifiers)
556  {
557  p_output_modifier->ProcessSolutionAtTimeStep(stepper.GetTime(), mSolution, PROBLEM_DIM);
558  }
559  if (mPrintOutput)
560  {
561  // Writing data out to the file <FilenamePrefix>.dat
562  HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT);
563  WriteOneStep(stepper.GetTime(), mSolution);
564  // Just flags that we've finished a time-step; won't actually 'extend' unless new data is written.
566 
567  HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT);
568  }
569 
570  progress_reporter.Update(stepper.GetTime());
571 
572  OnEndOfTimestep(stepper.GetTime());
573  }
574 
575  // Free solver
576  delete mpSolver;
577  mpSolver = NULL;
578 
579  // Close the file that stores voltage values
580  progress_reporter.PrintFinalising();
581  BOOST_FOREACH(boost::shared_ptr<AbstractOutputModifier> p_output_modifier, mOutputModifiers)
582  {
583  p_output_modifier->FinaliseAtEnd();
584  }
586  HeartEventHandler::EndEvent(HeartEventHandler::EVERYTHING);
587 }
588 
589 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
591 {
592  // Close files
593  if (!mPrintOutput)
594  {
595  // Nothing to do
596  return;
597  }
598  HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT);
599  // If write caching is on, the next line might actually take a significant amount of time.
600  delete mpWriter;
601  mpWriter = NULL;
602  HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT);
603 
604  FileFinder test_output(HeartConfig::Instance()->GetOutputDirectory(), RelativeTo::ChasteTestOutput);
605 
606  /********************************************************************************
607  * Run all post processing.
608  *
609  * The PostProcessingWriter class examines what is requested in HeartConfig and
610  * adds the relevant data to the HDF5 file.
611  * This is converted to different visualizer formats along with the solution
612  * in the DATA_CONVERSION block below.
613  *********************************************************************************/
614 
615  HeartEventHandler::BeginEvent(HeartEventHandler::POST_PROC);
616  if (HeartConfig::Instance()->IsPostProcessingRequested())
617  {
619  test_output,
620  HeartConfig::Instance()->GetOutputFilenamePrefix(),
621  "V",
623  post_writer.WritePostProcessingFiles();
624  }
625  HeartEventHandler::EndEvent(HeartEventHandler::POST_PROC);
626 
627  /********************************************************************************************
628  * Convert HDF5 datasets (solution and postprocessing maps) to different visualizer formats
629  ********************************************************************************************/
630 
631  HeartEventHandler::BeginEvent(HeartEventHandler::DATA_CONVERSION);
632  // Only if results files were written and we are outputting all nodes
633  if (mNodesToOutput.empty())
634  {
636  {
637  // Convert simulation data to Meshalyzer format
639  HeartConfig::Instance()->GetOutputFilenamePrefix(),
640  mpMesh,
641  HeartConfig::Instance()->GetOutputUsingOriginalNodeOrdering(),
642  HeartConfig::Instance()->GetVisualizerOutputPrecision() );
643  std::string subdirectory_name = converter.GetSubdirectory();
644  HeartConfig::Instance()->Write(false, subdirectory_name);
645  }
646 
647  if (HeartConfig::Instance()->GetVisualizeWithCmgui())
648  {
649  // Convert simulation data to Cmgui format
650  Hdf5ToCmguiConverter<ELEMENT_DIM,SPACE_DIM> converter(test_output,
651  HeartConfig::Instance()->GetOutputFilenamePrefix(),
652  mpMesh,
653  GetHasBath(),
654  HeartConfig::Instance()->GetVisualizerOutputPrecision() );
655  std::string subdirectory_name = converter.GetSubdirectory();
656  HeartConfig::Instance()->Write(false, subdirectory_name);
657  }
658 
659  if (HeartConfig::Instance()->GetVisualizeWithVtk())
660  {
661  // Convert simulation data to VTK format
662  Hdf5ToVtkConverter<ELEMENT_DIM,SPACE_DIM> converter(test_output,
663  HeartConfig::Instance()->GetOutputFilenamePrefix(),
664  mpMesh,
665  false,
666  HeartConfig::Instance()->GetOutputUsingOriginalNodeOrdering());
667  std::string subdirectory_name = converter.GetSubdirectory();
668  HeartConfig::Instance()->Write(false, subdirectory_name);
669  }
670 
671  if (HeartConfig::Instance()->GetVisualizeWithParallelVtk())
672  {
673  // Convert simulation data to parallel VTK (pvtu) format
674  Hdf5ToVtkConverter<ELEMENT_DIM,SPACE_DIM> converter(test_output,
675  HeartConfig::Instance()->GetOutputFilenamePrefix(),
676  mpMesh,
677  true,
678  HeartConfig::Instance()->GetOutputUsingOriginalNodeOrdering());
679  std::string subdirectory_name = converter.GetSubdirectory();
680  HeartConfig::Instance()->Write(false, subdirectory_name);
681  }
682  }
683  HeartEventHandler::EndEvent(HeartEventHandler::DATA_CONVERSION);
684 }
685 
686 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
688 {
689  if (!extending)
690  {
691  if (mNodesToOutput.empty() )
692  {
693  //Set writer to output all nodes
694  mpWriter->DefineFixedDimension( mpMesh->GetNumNodes() );
695  }
696  else
697  {
698  //Output only the nodes indicted
700  }
701  //mNodeColumnId = mpWriter->DefineVariable("Node", "dimensionless");
703 
704  // Only used to get an estimate of the # of timesteps below
705  TimeStepper stepper(mCurrentTime,
706  HeartConfig::Instance()->GetSimulationDuration(),
707  HeartConfig::Instance()->GetPrintingTimeStep());
708 
709  mpWriter->DefineUnlimitedDimension("Time","msecs", stepper.EstimateTimeSteps()+1); // plus one for start and end points
710  }
711  else
712  {
714  }
715 }
716 
717 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
719 {
720  mExtraVariablesId.clear();
721  // Check if any extra output variables have been requested
722  if (HeartConfig::Instance()->GetOutputVariablesProvided())
723  {
724  // Get their names in a vector
725  std::vector<std::string> output_variables;
726  HeartConfig::Instance()->GetOutputVariables(output_variables);
727  const unsigned num_vars = output_variables.size();
728  mExtraVariablesId.reserve(num_vars);
729 
730  // Loop over them
731  for (unsigned var_index=0; var_index<num_vars; var_index++)
732  {
733  // Get variable name
734  std::string var_name = output_variables[var_index];
735 
736  // Register it (or look it up) in the data writer
737  unsigned column_id;
738  if (extending)
739  {
740  column_id = this->mpWriter->GetVariableByName(var_name);
741  }
742  else
743  {
744  // Difficult to specify the units, as different cell models
745  // at different points in the mesh could be using different units.
746  column_id = this->mpWriter->DefineVariable(var_name, "unknown_units");
747  }
748 
749  // Store column id
750  mExtraVariablesId.push_back(column_id);
751  }
752  }
753 }
754 
755 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
757 {
758  // Get the variable names in a vector
759  std::vector<std::string> output_variables;
760  unsigned num_vars = mExtraVariablesId.size();
761  if (num_vars > 0)
762  {
763  HeartConfig::Instance()->GetOutputVariables(output_variables);
764  }
765  assert(output_variables.size() == num_vars);
766 
767  // Loop over the requested variables
768  for (unsigned var_index=0; var_index<num_vars; var_index++)
769  {
770  // Create vector for storing values over the local nodes
771  Vec variable_data = this->mpMesh->GetDistributedVectorFactory()->CreateVec();
772  DistributedVector distributed_var_data = this->mpMesh->GetDistributedVectorFactory()->CreateDistributedVector(variable_data);
773 
774  // Loop over the local nodes and gather the data
775  for (DistributedVector::Iterator index = distributed_var_data.Begin();
776  index!= distributed_var_data.End();
777  ++index)
778  {
779  // If the region is in the bath
780  if (HeartRegionCode::IsRegionBath( this->mpMesh->GetNode( index.Global )->GetRegion() ))
781  {
782  // Then we just pad the output with zeros, user currently needs to find a nice
783  // way to deal with this in processing and visualization.
784  distributed_var_data[index] = 0.0;
785  }
786  else
787  {
788  // Find the variable in the cell model and store its value
789  distributed_var_data[index] = this->mpCardiacTissue->GetCardiacCell(index.Global)->
790  GetAnyVariable(output_variables[var_index], mCurrentTime);
791  }
792  }
793  distributed_var_data.Restore();
794 
795  // Write it to disc
796  this->mpWriter->PutVector(mExtraVariablesId[var_index], variable_data);
797 
798  PetscTools::Destroy(variable_data);
799  }
800 }
801 
802 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
804 {
805  bool extend_file = (mSolution != NULL);
806 
807  // I think this is impossible to trip; certainly it's very difficult!
808  assert(!mpWriter);
809 
810  if (extend_file)
811  {
813  + "/" + HeartConfig::Instance()->GetOutputFilenamePrefix() + ".h5",
815  //We are going to test for existence before creating the file.
816  //Therefore we should make sure that this existence test is thread-safe.
817  //(If another process creates the file too early then we may get the wrong answer to the
818  //existence question).
819  PetscTools::Barrier("InitialiseWriter::Extension check");
820  if (!h5_file.Exists())
821  {
822  extend_file = false;
823  }
824  else // if it does exist check that it is sensible to extend it by running from the archive we loaded.
825  {
826  Hdf5DataReader reader(HeartConfig::Instance()->GetOutputDirectory(),
827  HeartConfig::Instance()->GetOutputFilenamePrefix(),
828  true);
829  std::vector<double> times = reader.GetUnlimitedDimensionValues();
830  if (times.back() > mCurrentTime)
831  {
832  EXCEPTION("Attempting to extend " << h5_file.GetAbsolutePath() <<
833  " with results from time = " << mCurrentTime <<
834  ", but it already contains results up to time = " << times.back() << "."
835  " Calling HeartConfig::Instance()->SetOutputDirectory() before Solve() will direct results elsewhere.");
836  }
837  }
838  PetscTools::Barrier("InitialiseWriter::Extension check");
839  }
840  mpWriter = new Hdf5DataWriter(*mpMesh->GetDistributedVectorFactory(),
843  !extend_file, // don't clear directory if extension requested
844  extend_file,
845  "Data",
847 
848  /* If user has specified a chunk size and alignment parameter, pass it
849  * through. We set them to the same value as we think this is the most
850  * likely use case, specifically on striped filesystems where a chunk
851  * should squeeze into a stripe.
852  * Only happens if !extend_file, i.e. we're NOT loading a checkpoint, or
853  * we are loading a checkpoint but the H5 file doesn't exist yet.
854  */
855  if (!extend_file && mHdf5DataWriterChunkSizeAndAlignment)
856  {
859  }
860 
861  // Define columns, or get the variable IDs from the writer
862  DefineWriterColumns(extend_file);
863 
864  //Possibility of applying a permutation
865  if (HeartConfig::Instance()->GetOutputUsingOriginalNodeOrdering())
866  {
867  bool success = mpWriter->ApplyPermutation(mpMesh->rGetNodePermutation(), true/*unsafe mode - extending*/);
868  if (success == false)
869  {
870  //It's not really a permutation, so reset
872  }
873  }
874 
875  if (!extend_file)
876  {
878  }
879 
880  return extend_file;
881 }
882 
883 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
885 {
886  mUseHdf5DataWriterCache = useCache;
887 }
888 
889 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
891 {
893 }
894 
895 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
897 {
898  mNodesToOutput = nodesToOutput;
899 }
900 
901 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
903 {
904  if ((HeartConfig::Instance()->GetOutputDirectory()=="") || (HeartConfig::Instance()->GetOutputFilenamePrefix()==""))
905  {
906  EXCEPTION("Data reader invalid as data writer cannot be initialised");
907  }
908  return Hdf5DataReader(HeartConfig::Instance()->GetOutputDirectory(), HeartConfig::Instance()->GetOutputFilenamePrefix());
909 }
910 
911 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
913 {
914  return false;
915 }
916 
917 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
919 {
920 }
921 
922 // Explicit instantiation
923 
924 // Monodomain
925 template class AbstractCardiacProblem<1,1,1>;
926 template class AbstractCardiacProblem<1,2,1>;
927 template class AbstractCardiacProblem<1,3,1>;
928 template class AbstractCardiacProblem<2,2,1>;
929 template class AbstractCardiacProblem<3,3,1>;
930 
931 // Bidomain
932 template class AbstractCardiacProblem<1,1,2>;
933 template class AbstractCardiacProblem<2,2,2>;
934 template class AbstractCardiacProblem<3,3,2>;
935 
936 // Extended Bidomain
937 template class AbstractCardiacProblem<1,1,3>;
938 template class AbstractCardiacProblem<2,2,3>;
939 template class AbstractCardiacProblem<3,3,3>;
virtual void WriteOneStep(double time, Vec voltageVec)=0
bool ApplyPermutation(const std::vector< unsigned > &rPermutation, bool unsafeExtendingMode=false)
std::string GetOutputFilenamePrefix() const
double GetSimulationDuration() const
static bool IsRegionBath(HeartRegionType regionId)
std::vector< boost::shared_ptr< AbstractOutputModifier > > mOutputModifiers
static void Barrier(const std::string callerId="")
Definition: PetscTools.cpp:134
void PrintOutput(bool rPrintOutput)
int GetVariableByName(const std::string &rVariableName)
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
std::vector< unsigned > mExtraVariablesId
AbstractTimeAdaptivityController * mpTimeAdaptivityController
virtual void SetUpAdditionalStoppingTimes(std::vector< double > &rAdditionalStoppingTimes)
double GetPdeTimeStep() const
#define NEVER_REACHED
Definition: Exception.hpp:206
std::string GetMeshName() const
void GetFibreLength(c_vector< double, 1 > &fibreLength) const
void SetTargetChunkSize(hsize_t targetSize)
void AdvanceOneTimeStep()
bool IsTimeAtEnd() const
AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > * mpMesh
void SetHdf5DataWriterTargetChunkSizeAndAlignment(hsize_t size)
void GetSheetDimensions(c_vector< double, 2 > &sheetDimensions) const
void SetUseTimeAdaptivityController(bool useAdaptivity, AbstractTimeAdaptivityController *pController=NULL)
virtual void WriteInfo(double time)=0
AbstractCardiacTissue< ELEMENT_DIM, SPACE_DIM > * GetTissue()
void AdvanceAlongUnlimitedDimension()
static void ReplicateException(bool flag)
Definition: PetscTools.cpp:198
std::vector< double > GetUnlimitedDimensionValues()
int DefineVariable(const std::string &rVariableName, const std::string &rVariableUnits)
std::vector< unsigned > mNodesToOutput
virtual void OnEndOfTimestep(double time)
void PutVector(int variableID, Vec petscVector)
unsigned EstimateTimeSteps() const
static void Destroy(Vec &rVec)
Definition: PetscTools.hpp:352
void SetMesh(AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > *pMesh)
double GetInterNodeSpace() const
void SetUseHdf5DataWriterCache(bool useCache=true)
static bool IsParallel()
Definition: PetscTools.cpp:97
bool Exists() const
Definition: FileFinder.cpp:180
AbstractCardiacCellFactory< ELEMENT_DIM, SPACE_DIM > * mpCellFactory
void DefineFixedDimension(long dimensionSize)
void SetOutputNodes(std::vector< unsigned > &rNodesToOutput)
AbstractDynamicLinearPdeSolver< ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM > * mpSolver
virtual void DefineWriterColumns(bool extending)
virtual AbstractCardiacTissue< ELEMENT_DIM, SPACE_DIM > * CreateCardiacTissue()=0
void SetBoundaryConditionsContainer(BccType pBcc)
bool GetVisualizeWithMeshalyzer() const
void SetAlignment(hsize_t alignment)
std::string GetOutputDirectory() const
AbstractCardiacTissue< ELEMENT_DIM, SPACE_DIM > * mpCardiacTissue
static std::string GetChasteTestOutputDirectory()
double GetNextTime() const
void SetWriteInfo(bool writeInfo=true)
virtual AbstractDynamicLinearPdeSolver< ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM > * CreateSolver()=0
DistributedVector GetSolutionDistributedVector()
std::string GetShortMessage() const
Definition: Exception.cpp:88
static HeartConfig * Instance()
virtual void AtBeginningOfTimestep(double time)
void DefineExtraVariablesWriterColumns(bool extending)
virtual void EndDefineMode()
void DefineUnlimitedDimension(const std::string &rVariableName, const std::string &rVariableUnits, unsigned estimatedLength=1)