Chaste Commit::1fd4e48e3990e67db148bc1bc4cf6991a0049d0c
AbstractDynamicLinearPdeSolver.hpp
1
2/*
3
4Copyright (c) 2005-2024, University of Oxford.
5All rights reserved.
6
7University of Oxford means the Chancellor, Masters and Scholars of the
8University of Oxford, having an administrative office at Wellington
9Square, Oxford OX1 2JD, UK.
10
11This file is part of Chaste.
12
13Redistribution and use in source and binary forms, with or without
14modification, 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
24THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
28LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
30GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33OF 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
55template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
56class AbstractDynamicLinearPdeSolver : public AbstractLinearPdeSolver<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>
57{
58 friend class TestSimpleLinearParabolicSolver;
59protected:
60
62 double mTstart;
63
65 double mTend;
66
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
136
143 void WriteOneStep(double time, Vec solution);
144
145public:
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
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
222template<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(),
233 mOutputDirectory,
234 mFilenamePrefix);
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
258 mpHdf5Writer->EndDefineMode();
259}
260
261template<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),
269 mLastWorkingTimeStep(-1),
270 mpTimeAdaptivityController(nullptr),
271 mOutputToVtk(false),
272 mOutputToParallelVtk(false),
273 mOutputToTxt(false),
274 mOutputDirectory(""),
275 mFilenamePrefix(""),
276 mPrintingTimestepMultiple(1),
277 mpHdf5Writer(nullptr)
278{
279}
280
281template<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
295template<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
306template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
308{
309 assert(initialCondition != nullptr);
310 mInitialCondition = initialCondition;
311}
312
313template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
315{
316 mpHdf5Writer->PutUnlimitedVariable(time);
317 if (PROBLEM_DIM == 1)
318 {
319 mpHdf5Writer->PutVector(mVariableColumnIds[0], solution);
320 }
321 else
322 {
323 mpHdf5Writer->PutStripedVector(mVariableColumnIds, solution);
324 }
325}
326
327template<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 {
348 InitialiseHdf5Writer();
349 WriteOneStep(mTstart, mInitialCondition);
350 mpHdf5Writer->AdvanceAlongUnlimitedDimension();
351 }
352
353 this->InitialiseForSolve(mInitialCondition);
354
355 if (mIdealTimeStep < 0) // hasn't been set, so a controller must have been given
356 {
357 mIdealTimeStep = mpTimeAdaptivityController->GetNextTimeStep(mTstart, mInitialCondition);
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 */
367 TimeStepper stepper(mTstart, mTend, mIdealTimeStep, mMatrixIsConstant);
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;
380 if (mpTimeAdaptivityController)
381 {
382 // Get the timestep the controller wants to use and store it as the ideal timestep
383 mIdealTimeStep = mpTimeAdaptivityController->GetNextTimeStep(stepper.GetTime(), solution);
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 {
440 this->mpLinearSystem->ResetKspSolver();
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);
467 mpHdf5Writer->AdvanceAlongUnlimitedDimension();
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 }
485 if (mOutputToParallelVtk)
486 {
488 mFilenamePrefix, this->mpMesh, true, false);
489 }
490 if (mOutputToTxt)
491 {
493 mFilenamePrefix, this->mpMesh);
494 }
495
496 return solution;
497}
498
499template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
504
505template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
507{
508 assert(pTimeAdaptivityController != nullptr);
509 assert(mpTimeAdaptivityController == NULL);
510 mpTimeAdaptivityController = pTimeAdaptivityController;
511}
512
513template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
518
519template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
521{
522 mOutputToParallelVtk = output;
523}
524
525template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
530
531template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
533{
534 mOutputDirectory = outputDirectory;
535 mFilenamePrefix = prefix;
536}
537
538template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
540{
541 mPrintingTimestepMultiple = multiple;
542}
543
544#endif /*ABSTRACTDYNAMICLINEARPDESOLVER_HPP_*/
#define EXCEPTION(message)
#define NEVER_REACHED
AbstractDynamicLinearPdeSolver(AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > *pMesh)
void SetTimes(double tStart, double tEnd)
void SetTimeAdaptivityController(AbstractTimeAdaptivityController *pTimeAdaptivityController)
void SetOutputDirectoryAndPrefix(std::string outputDirectory, std::string prefix)
void WriteOneStep(double time, Vec solution)
AbstractTimeAdaptivityController * mpTimeAdaptivityController
static void SetPdeTimeStepAndNextTime(double timestep, double next_time)
static void SetTime(double time)
static void Destroy(Vec &rVec)
void ResetTimeStep(double dt)
double GetNextTimeStep()
bool IsTimeAtEnd() const
unsigned GetTotalTimeStepsTaken() const
double GetTime() const
void AdvanceOneTimeStep()
double GetNextTime() const