Chaste  Release::2017.1
AbstractDynamicLinearPdeSolver.hpp
1 
2 /*
3 
4 Copyright (c) 2005-2017, University of Oxford.
5 All rights reserved.
6 
7 University of Oxford means the Chancellor, Masters and Scholars of the
8 University of Oxford, having an administrative office at Wellington
9 Square, Oxford OX1 2JD, UK.
10 
11 This file is part of Chaste.
12 
13 Redistribution and use in source and binary forms, with or without
14 modification, are permitted provided that the following conditions are met:
15  * Redistributions of source code must retain the above copyright notice,
16  this list of conditions and the following disclaimer.
17  * Redistributions in binary form must reproduce the above copyright notice,
18  this list of conditions and the following disclaimer in the documentation
19  and/or other materials provided with the distribution.
20  * Neither the name of the University of Oxford nor the names of its
21  contributors may be used to endorse or promote products derived from this
22  software without specific prior written permission.
23 
24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
28 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
30 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 
35 */
36 
37 #ifndef ABSTRACTDYNAMICLINEARPDESOLVER_HPP_
38 #define ABSTRACTDYNAMICLINEARPDESOLVER_HPP_
39 
40 #include "Exception.hpp"
41 #include "TimeStepper.hpp"
42 #include "AbstractLinearPdeSolver.hpp"
43 #include "PdeSimulationTime.hpp"
44 #include "AbstractTimeAdaptivityController.hpp"
45 #include "Hdf5DataWriter.hpp"
46 #include "Hdf5ToVtkConverter.hpp"
47 #include "Hdf5ToTxtConverter.hpp"
48 
55 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
56 class AbstractDynamicLinearPdeSolver : public AbstractLinearPdeSolver<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>
57 {
58  friend class TestSimpleLinearParabolicSolver;
59 protected:
60 
62  double mTstart;
63 
65  double mTend;
66 
68  bool mTimesSet;
69 
72 
75 
81 
87 
90 
93 
99 
105 
111 
113  std::string mOutputDirectory;
114 
116  std::string mFilenamePrefix;
117 
124 
127 
129  std::vector<int> mVariableColumnIds;
130 
135  void InitialiseHdf5Writer();
136 
143  void WriteOneStep(double time, Vec solution);
144 
145 public:
146 
153 
160  void SetTimes(double tStart, double tEnd);
161 
167  void SetTimeStep(double dt);
168 
177  void SetInitialCondition(Vec initialCondition);
178 
182  virtual Vec Solve();
183 
186 
192  void SetTimeAdaptivityController(AbstractTimeAdaptivityController* pTimeAdaptivityController);
193 
197  void SetOutputToVtk(bool output);
198 
202  void SetOutputToParallelVtk(bool output);
203 
207  void SetOutputToTxt(bool output);
208 
213  void SetOutputDirectoryAndPrefix(std::string outputDirectory, std::string prefix);
214 
219  void SetPrintingTimestepMultiple(unsigned multiple);
220 };
221 
222 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
224 {
225  // Check that everything is set up correctly
226  if ((mOutputDirectory=="") || (mFilenamePrefix==""))
227  {
228  EXCEPTION("Output directory or filename prefix has not been set");
229  }
230 
231  // Create writer
232  mpHdf5Writer = new Hdf5DataWriter(*(this->mpMesh)->GetDistributedVectorFactory(),
235 
236  // Set writer to output all nodes
237  mpHdf5Writer->DefineFixedDimension((this->mpMesh)->GetNumNodes());
238 
239  // Only used to get an estimate of the number of timesteps below
240  unsigned estimated_num_printing_timesteps = 1u + (unsigned)((mTend - mTstart)/(mIdealTimeStep*mPrintingTimestepMultiple));
241 
248  assert(mVariableColumnIds.empty());
249  for (unsigned i=0; i<PROBLEM_DIM; i++)
250  {
251  std::stringstream variable_name;
252  variable_name << "Variable_" << i;
253  mVariableColumnIds.push_back(mpHdf5Writer->DefineVariable(variable_name.str(),"undefined"));
254  }
255  mpHdf5Writer->DefineUnlimitedDimension("Time", "undefined", estimated_num_printing_timesteps);
256 
257  // End the define mode of the writer
259 }
260 
261 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
263  : AbstractLinearPdeSolver<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>(pMesh),
264  mTimesSet(false),
265  mInitialCondition(nullptr),
266  mMatrixIsAssembled(false),
267  mMatrixIsConstant(false),
268  mIdealTimeStep(-1.0),
271  mOutputToVtk(false),
272  mOutputToParallelVtk(false),
273  mOutputToTxt(false),
274  mOutputDirectory(""),
275  mFilenamePrefix(""),
277  mpHdf5Writer(nullptr)
278 {
279 }
280 
281 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
283 {
284  mTstart = tStart;
285  mTend = tEnd;
286 
287  if (mTstart >= mTend)
288  {
289  EXCEPTION("Start time has to be less than end time");
290  }
291 
292  mTimesSet = true;
293 }
294 
295 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
297 {
298  if (dt <= 0)
299  {
300  EXCEPTION("Time step has to be greater than zero");
301  }
302 
303  mIdealTimeStep = dt;
304 }
305 
306 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
308 {
309  assert(initialCondition != nullptr);
310  mInitialCondition = initialCondition;
311 }
312 
313 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
315 {
317  if (PROBLEM_DIM == 1)
318  {
320  }
321  else
322  {
324  }
325 }
326 
327 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
329 {
330  // Begin by checking that everything has been set up correctly
331  if (!mTimesSet)
332  {
333  EXCEPTION("SetTimes() has not been called");
334  }
335  if ((mIdealTimeStep <= 0.0) && (mpTimeAdaptivityController==nullptr))
336  {
337  EXCEPTION("SetTimeStep() has not been called");
338  }
339  if (mInitialCondition == nullptr)
340  {
341  EXCEPTION("SetInitialCondition() has not been called");
342  }
343 
344  // If required, initialise HDF5 writer and output initial condition to HDF5 file
345  bool print_output = (mOutputToVtk || mOutputToParallelVtk || mOutputToTxt);
346  if (print_output)
347  {
351  }
352 
354 
355  if (mIdealTimeStep < 0) // hasn't been set, so a controller must have been given
356  {
358  }
359 
360  /*
361  * Note: we use the mIdealTimeStep here (the original timestep that was passed in, or
362  * the last timestep suggested by the controller), rather than the last timestep used
363  * (mLastWorkingTimeStep), because the timestep will be very slightly altered by the
364  * stepper in the final timestep of the last printing-timestep-loop, and these floating
365  * point errors can add up and eventually cause exceptions being thrown.
366  */
368 
369  Vec solution = mInitialCondition;
370  Vec next_solution;
371 
372  while (!stepper.IsTimeAtEnd())
373  {
374  bool timestep_changed = false;
375 
377 
378  // Determine timestep to use
379  double new_dt;
381  {
382  // Get the timestep the controller wants to use and store it as the ideal timestep
384 
385  // Tell the stepper to use this timestep from now on...
386  stepper.ResetTimeStep(mIdealTimeStep);
387 
388  // ..but now get the timestep from the stepper, as the stepper might need
389  // to trim the timestep if it would take us over the end time
390  new_dt = stepper.GetNextTimeStep();
391  // Changes in timestep bigger than 0.001% will trigger matrix re-computation
392  timestep_changed = (fabs(new_dt/mLastWorkingTimeStep - 1.0) > 1e-5);
393  }
394  else
395  {
396  new_dt = stepper.GetNextTimeStep();
397 
398  //new_dt should be roughly the same size as mIdealTimeStep - we should never need to take a tiny step
399 
400  if (mMatrixIsConstant && fabs(new_dt/mIdealTimeStep - 1.0) > 1e-5)
401  {
402  // Here we allow for changes of up to 0.001%
403  // Note that the TimeStepper guarantees that changes in dt are no bigger than DBL_EPSILON*current_time
405  }
406  }
407 
408  // Save the timestep as the last one use, and also put it in PdeSimulationTime
409  // so everyone can see it
410  mLastWorkingTimeStep = new_dt;
412 
413  // Solve
414  try
415  {
416  // (This runs the cell ODE models in heart simulations)
417  this->PrepareForSetupLinearSystem(solution);
418  }
419  catch(Exception& e)
420  {
421  // We only need to clean up memory if we are NOT on the first PDE time step,
422  // as someone else cleans up the mInitialCondition vector in higher classes.
423  if (solution != mInitialCondition)
424  {
425  HeartEventHandler::BeginEvent(HeartEventHandler::COMMUNICATION);
426  PetscTools::Destroy(solution);
427  HeartEventHandler::EndEvent(HeartEventHandler::COMMUNICATION);
428  }
429  throw e;
430  }
431 
432  bool compute_matrix = (!mMatrixIsConstant || !mMatrixIsAssembled || timestep_changed);
433 
434  this->SetupLinearSystem(solution, compute_matrix);
435 
436  this->FinaliseLinearSystem(solution);
437 
438  if (compute_matrix)
439  {
441  }
442 
443  next_solution = this->mpLinearSystem->Solve(solution);
444 
445  if (mMatrixIsConstant)
446  {
447  mMatrixIsAssembled = true;
448  }
449 
450  this->FollowingSolveLinearSystem(next_solution);
451 
452  stepper.AdvanceOneTimeStep();
453 
454  // Avoid memory leaks
455  if (solution != mInitialCondition)
456  {
457  HeartEventHandler::BeginEvent(HeartEventHandler::COMMUNICATION);
458  PetscTools::Destroy(solution);
459  HeartEventHandler::EndEvent(HeartEventHandler::COMMUNICATION);
460  }
461  solution = next_solution;
462 
463  // If required, output next solution to HDF5 file
464  if (print_output && (stepper.GetTotalTimeStepsTaken()%mPrintingTimestepMultiple == 0) )
465  {
466  WriteOneStep(stepper.GetTime(), solution);
468  }
469  }
470 
471  // Avoid memory leaks
472  if (mpHdf5Writer != nullptr)
473  {
474  delete mpHdf5Writer;
475  mpHdf5Writer = nullptr;
476  }
477 
478  // Convert HDF5 output to other formats as required
479 
480  if (mOutputToVtk)
481  {
483  mFilenamePrefix, this->mpMesh, false, false);
484  }
486  {
488  mFilenamePrefix, this->mpMesh, true, false);
489  }
490  if (mOutputToTxt)
491  {
493  mFilenamePrefix, this->mpMesh);
494  }
495 
496  return solution;
497 }
498 
499 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
501 {
502  mMatrixIsAssembled = false;
503 }
504 
505 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
507 {
508  assert(pTimeAdaptivityController != nullptr);
509  assert(mpTimeAdaptivityController == NULL);
510  mpTimeAdaptivityController = pTimeAdaptivityController;
511 }
512 
513 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
515 {
516  mOutputToVtk = output;
517 }
518 
519 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
521 {
522  mOutputToParallelVtk = output;
523 }
524 
525 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
527 {
528  mOutputToTxt = output;
529 }
530 
531 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
533 {
534  mOutputDirectory = outputDirectory;
535  mFilenamePrefix = prefix;
536 }
537 
538 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
540 {
541  mPrintingTimestepMultiple = multiple;
542 }
543 
544 #endif /*ABSTRACTDYNAMICLINEARPDESOLVER_HPP_*/
AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > * mpMesh
double GetNextTimeStep()
virtual void PrepareForSetupLinearSystem(Vec currentSolution)
Vec Solve(Vec lhsGuess=nullptr)
double GetTime() const
virtual void InitialiseForSolve(Vec initialSolution=nullptr)
#define EXCEPTION(message)
Definition: Exception.hpp:143
void ResetTimeStep(double dt)
void SetOutputDirectoryAndPrefix(std::string outputDirectory, std::string prefix)
#define NEVER_REACHED
Definition: Exception.hpp:206
virtual void FollowingSolveLinearSystem(Vec currentSolution)
void AdvanceOneTimeStep()
bool IsTimeAtEnd() const
void SetTimes(double tStart, double tEnd)
void AdvanceAlongUnlimitedDimension()
AbstractTimeAdaptivityController * mpTimeAdaptivityController
int DefineVariable(const std::string &rVariableName, const std::string &rVariableUnits)
unsigned GetTotalTimeStepsTaken() const
double GetNextTimeStep(double currentTime, Vec currentSolution)
void PutVector(int variableID, Vec petscVector)
static void Destroy(Vec &rVec)
Definition: PetscTools.hpp:352
void SetTimeAdaptivityController(AbstractTimeAdaptivityController *pTimeAdaptivityController)
virtual void SetupLinearSystem(Vec currentSolution, bool computeMatrix)=0
void DefineFixedDimension(long dimensionSize)
void ResetKspSolver()
void WriteOneStep(double time, Vec solution)
virtual void FinaliseLinearSystem(Vec currentSolution)
void PutStripedVector(std::vector< int > variableIDs, Vec petscVector)
static void SetTime(double time)
double GetNextTime() const
static void SetPdeTimeStepAndNextTime(double timestep, double next_time)
AbstractDynamicLinearPdeSolver(AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > *pMesh)
void PutUnlimitedVariable(double value)
virtual void EndDefineMode()
void DefineUnlimitedDimension(const std::string &rVariableName, const std::string &rVariableUnits, unsigned estimatedLength=1)