void AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::Solve() { PreSolveChecks(); std::vector<double> additional_stopping_times; SetUpAdditionalStoppingTimes(additional_stopping_times); TimeStepper stepper(mCurrentTime, HeartConfig::Instance()->GetSimulationDuration(), HeartConfig::Instance()->GetPrintingTimeStep(), false, additional_stopping_times); // Note that SetUpAdditionalStoppingTimes is a method from the BidomainWithBath class it adds // electrode events into the regular time-stepping // EXCEPTION("Electrode switch on/off events should coincide with printing time steps."); if (!mpBoundaryConditionsContainer) // the user didn't supply a bcc { // Set up the default bcc mpDefaultBoundaryConditionsContainer.reset(new BoundaryConditionsContainer<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>); for (unsigned problem_index=0; problem_index<PROBLEM_DIM; problem_index++) { mpDefaultBoundaryConditionsContainer->DefineZeroNeumannOnMeshBoundary(mpMesh, problem_index); } mpBoundaryConditionsContainer = mpDefaultBoundaryConditionsContainer; } assert(mpSolver==NULL); mpSolver = CreateSolver(); // passes mpBoundaryConditionsContainer to solver // If we have already run a simulation, use the old solution as initial condition Vec initial_condition; if (mSolution) { initial_condition = mSolution; } else { initial_condition = CreateInitialCondition(); } std::string progress_reporter_dir; if (mPrintOutput) { HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT); bool extending_file = false; try { extending_file = InitialiseWriter(); } catch (Exception& e) { delete mpWriter; mpWriter = NULL; delete mpSolver; if (mSolution != initial_condition) { /* * A PETSc Vec is a pointer, so we *don't* need to free the memory if it is * freed somewhere else (e.g. in the destructor). If this is a resumed solution * we set initial_condition = mSolution earlier. mSolution is going to be * cleaned up in the constructor. So, only PetscTools::Destroy( initial_condition ) when * it is not equal to mSolution. */ PetscTools::Destroy(initial_condition); } throw e; } /* * If we are resuming a simulation (i.e. mSolution already exists) and * we are extending a .h5 file that already exists then there is no need * to write the initial condition to file - it is already there as the * final solution of the previous run. */ if (!(mSolution && extending_file)) { WriteOneStep(stepper.GetTime(), initial_condition); mpWriter->AdvanceAlongUnlimitedDimension(); } HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT); progress_reporter_dir = HeartConfig::Instance()->GetOutputDirectory(); } else { progress_reporter_dir = ""; // progress printed to CHASTE_TEST_OUTPUT } BOOST_FOREACH(boost::shared_ptr<AbstractOutputModifier> p_output_modifier, mOutputModifiers) { p_output_modifier->InitialiseAtStart(this->mpMesh->GetDistributedVectorFactory()); p_output_modifier->ProcessSolutionAtTimeStep(stepper.GetTime(), initial_condition, PROBLEM_DIM); }
Vec AbstractDynamicLinearPdeSolver<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>::Solve() { // Begin by checking that everything has been set up correctly if (!mTimesSet) { EXCEPTION("SetTimes() has not been called"); } if ((mIdealTimeStep <= 0.0) && (mpTimeAdaptivityController==NULL)) { EXCEPTION("SetTimeStep() has not been called"); } if (mInitialCondition == NULL) { EXCEPTION("SetInitialCondition() has not been called"); } // If required, initialise HDF5 writer and output initial condition to HDF5 file bool print_output = (mOutputToVtk || mOutputToParallelVtk || mOutputToTxt); if (print_output) { InitialiseHdf5Writer(); WriteOneStep(mTstart, mInitialCondition); mpHdf5Writer->AdvanceAlongUnlimitedDimension(); } this->InitialiseForSolve(mInitialCondition); if (mIdealTimeStep < 0) // hasn't been set, so a controller must have been given { mIdealTimeStep = mpTimeAdaptivityController->GetNextTimeStep(mTstart, mInitialCondition); } /* * Note: we use the mIdealTimeStep here (the original timestep that was passed in, or * the last timestep suggested by the controller), rather than the last timestep used * (mLastWorkingTimeStep), because the timestep will be very slightly altered by the * stepper in the final timestep of the last printing-timestep-loop, and these floating * point errors can add up and eventually cause exceptions being thrown. */ TimeStepper stepper(mTstart, mTend, mIdealTimeStep, mMatrixIsConstant); Vec solution = mInitialCondition; Vec next_solution; while (!stepper.IsTimeAtEnd()) { bool timestep_changed = false; PdeSimulationTime::SetTime(stepper.GetTime()); // Determine timestep to use double new_dt; if (mpTimeAdaptivityController) { // Get the timestep the controller wants to use and store it as the ideal timestep mIdealTimeStep = mpTimeAdaptivityController->GetNextTimeStep(stepper.GetTime(), solution); // Tell the stepper to use this timestep from now on... stepper.ResetTimeStep(mIdealTimeStep); // ..but now get the timestep from the stepper, as the stepper might need // to trim the timestep if it would take us over the end time new_dt = stepper.GetNextTimeStep(); // Changes in timestep bigger than 0.001% will trigger matrix re-computation timestep_changed = (fabs(new_dt/mLastWorkingTimeStep - 1.0) > 1e-5); } else { new_dt = stepper.GetNextTimeStep(); //new_dt should be roughly the same size as mIdealTimeStep - we should never need to take a tiny step if (mMatrixIsConstant && fabs(new_dt/mIdealTimeStep - 1.0) > 1e-5) { // Here we allow for changes of up to 0.001% // Note that the TimeStepper guarantees that changes in dt are no bigger than DBL_EPSILON*current_time NEVER_REACHED; } } // Save the timestep as the last one use, and also put it in PdeSimulationTime // so everyone can see it mLastWorkingTimeStep = new_dt; PdeSimulationTime::SetPdeTimeStepAndNextTime(new_dt, stepper.GetNextTime()); // Solve try { // (This runs the cell ODE models in heart simulations) this->PrepareForSetupLinearSystem(solution); } catch(Exception& e) { // We only need to clean up memory if we are NOT on the first PDE time step, // as someone else cleans up the mInitialCondition vector in higher classes. if (solution != mInitialCondition) { HeartEventHandler::BeginEvent(HeartEventHandler::COMMUNICATION); PetscTools::Destroy(solution); HeartEventHandler::EndEvent(HeartEventHandler::COMMUNICATION); } throw e; } bool compute_matrix = (!mMatrixIsConstant || !mMatrixIsAssembled || timestep_changed); this->SetupLinearSystem(solution, compute_matrix); this->FinaliseLinearSystem(solution); if (compute_matrix) { this->mpLinearSystem->ResetKspSolver(); } next_solution = this->mpLinearSystem->Solve(solution); if (mMatrixIsConstant) { mMatrixIsAssembled = true; } this->FollowingSolveLinearSystem(next_solution); stepper.AdvanceOneTimeStep(); // Avoid memory leaks if (solution != mInitialCondition) { HeartEventHandler::BeginEvent(HeartEventHandler::COMMUNICATION); PetscTools::Destroy(solution); HeartEventHandler::EndEvent(HeartEventHandler::COMMUNICATION); } solution = next_solution; // If required, output next solution to HDF5 file if (print_output && (stepper.GetTotalTimeStepsTaken()%mPrintingTimestepMultiple == 0) ) { WriteOneStep(stepper.GetTime(), solution); mpHdf5Writer->AdvanceAlongUnlimitedDimension(); } } // Avoid memory leaks if (mpHdf5Writer != NULL) { delete mpHdf5Writer; mpHdf5Writer = NULL; } // Convert HDF5 output to other formats as required if (mOutputToVtk) { Hdf5ToVtkConverter<ELEMENT_DIM,SPACE_DIM> converter(FileFinder(mOutputDirectory, RelativeTo::ChasteTestOutput), mFilenamePrefix, this->mpMesh, false, false); } if (mOutputToParallelVtk) { Hdf5ToVtkConverter<ELEMENT_DIM,SPACE_DIM> converter(FileFinder(mOutputDirectory, RelativeTo::ChasteTestOutput), mFilenamePrefix, this->mpMesh, true, false); } if (mOutputToTxt) { Hdf5ToTxtConverter<ELEMENT_DIM,SPACE_DIM> converter(FileFinder(mOutputDirectory, RelativeTo::ChasteTestOutput), mFilenamePrefix, this->mpMesh); } return solution; }