AdaptiveBidomainProblem.cpp

00001 /*
00002 
00003 Copyright (C) Fujitsu Laboratories of Europe, 2009-2010
00004 
00005 */
00006 
00007 /*
00008 
00009 Copyright (C) University of Oxford, 2005-2011
00010 
00011 University of Oxford means the Chancellor, Masters and Scholars of the
00012 University of Oxford, having an administrative office at Wellington
00013 Square, Oxford OX1 2JD, UK.
00014 
00015 This file is part of Chaste.
00016 
00017 Chaste is free software: you can redistribute it and/or modify it
00018 under the terms of the GNU Lesser General Public License as published
00019 by the Free Software Foundation, either version 2.1 of the License, or
00020 (at your option) any later version.
00021 
00022 Chaste is distributed in the hope that it will be useful, but WITHOUT
00023 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00024 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
00025 License for more details. The offer of Chaste under the terms of the
00026 License is subject to the License being interpreted in accordance with
00027 English Law and subject to any action against the University of Oxford
00028 being under the jurisdiction of the English Courts.
00029 
00030 You should have received a copy of the GNU Lesser General Public License
00031 along with Chaste. If not, see <http://www.gnu.org/licenses/>.
00032 
00033 */
00034 
00035 
00036 
00037 #ifdef CHASTE_ADAPTIVITY
00038 
00039 #include "AdaptiveBidomainProblem.hpp"
00040 #include "BidomainSolver.hpp"
00041 
00042 #include "VtkMeshWriter.hpp"
00043 #include "TetrahedralMesh.hpp"
00044 #include "BidomainTissue.hpp"
00045 #include "HeartRegionCodes.hpp"
00046 #include "HeartConfig.hpp"
00047 #include "Exception.hpp"
00048 #include "DistributedVector.hpp"
00049 #include "ReplicatableVector.hpp"
00050 #include "ProgressReporter.hpp"
00051 #include "PetscTools.hpp"
00052 #include "RegularStimulus.hpp"
00053 
00054 AdaptiveBidomainProblem::AdaptiveBidomainProblem(
00055             AbstractCardiacCellFactory<3>* pCellFactory, bool hasBath)
00056     : BidomainProblem<3>(pCellFactory, hasBath),
00057       mIsMeshAdapting(true),
00058       mInitializeFromVtu(false),
00059       mUseNeumannBoundaryConditions(false),
00060       mNeumannStimulusIndex(0),
00061       mNeumannStimulusLowerValue(DBL_MAX),
00062       mNeumannStimulusUpperValue(-DBL_MAX),
00063       mNeumannStimulusMagnitude(0.0),
00064       mNeumannStimulusDuration(0.0),
00065       mNeumannStimulusPeriod(DBL_MAX)
00066 //      mGoodEdgeRange(0.0),
00067 //      mBadEdgeCriterion(0.0)
00068 {
00069     mFixedExtracellularPotentialNodes.resize(0);
00070     mpAdaptiveMesh = new AdaptiveTetrahedralMesh;
00071 }
00072 
00073 AdaptiveBidomainProblem::~AdaptiveBidomainProblem()
00074 {
00075     delete mpAdaptiveMesh;
00076 }
00077 
00078 void AdaptiveBidomainProblem::DoNotAdaptMesh()
00079 {
00080     mIsMeshAdapting = false;
00081 }
00082 
00083 //void AdaptiveBidomainProblem::SetAdaptCriterion(double range, double criterion)
00084 //{
00085 //    mGoodEdgeRange = range;
00086 //    mBadEdgeCriterion = criterion;
00087 //}
00088 
00089 void AdaptiveBidomainProblem::AddCurrentSolutionToAdaptiveMesh( Vec solution )
00090 {
00091     HeartEventHandler::BeginEvent(HeartEventHandler::USER1);
00092 
00093     ReplicatableVector replicatable_solution( solution );
00094     std::vector<double> vm_for_vtk, phi_for_vtk;
00095     vm_for_vtk.resize(mpMesh->GetNumNodes());
00096     phi_for_vtk.resize(mpMesh->GetNumNodes());
00097 
00098     for (AbstractTetrahedralMesh<3,3>::NodeIterator it=mpMesh->GetNodeIteratorBegin();
00099          it != mpMesh->GetNodeIteratorEnd();
00100          ++it)
00101     {
00102         vm_for_vtk[it->GetIndex()]  = replicatable_solution[2*it->GetIndex()];
00103         phi_for_vtk[it->GetIndex()] = replicatable_solution[2*it->GetIndex()+1];
00104     }
00105     mpAdaptiveMesh->AddPointData("Vm", vm_for_vtk);
00106     mpAdaptiveMesh->AddPointData("Phi", phi_for_vtk);
00107 
00108     std::vector<std::string> state_variable_names = mpBidomainTissue->GetCardiacCell(0)->rGetStateVariableNames();
00109     unsigned number_of_state_variables = state_variable_names.size();
00110     std::vector< std::vector<double> > state_variable_values;
00111     state_variable_values.resize( mpMesh->GetNumNodes() );
00112 
00113     // add state variable to vtu file as point data
00114     for (unsigned variable=0; variable<number_of_state_variables; variable++)
00115     {
00116         if (variable != mpBidomainTissue->GetCardiacCell(0)->GetVoltageIndex())
00117         {
00118             std::vector<double> variable_for_vtk;
00119             variable_for_vtk.resize(mpMesh->GetNumNodes());
00120             for (AbstractTetrahedralMesh<3,3>::NodeIterator it=mpMesh->GetNodeIteratorBegin();
00121                  it != mpMesh->GetNodeIteratorEnd();
00122                  ++it)
00123             {
00124                 variable_for_vtk[it->GetIndex()] = mpBidomainTissue->GetCardiacCell(it->GetIndex())->rGetStateVariables()[variable];
00125             }
00126             mpAdaptiveMesh->AddPointData(state_variable_names[variable], variable_for_vtk);
00127         }
00128     }
00129 
00130     HeartEventHandler::EndEvent(HeartEventHandler::USER1);
00131 }
00132 
00133 void AdaptiveBidomainProblem::InitializeSolutionOnAdaptedMesh( VtkMeshReader<3,3>* reader )
00134 {
00135     HeartEventHandler::BeginEvent(HeartEventHandler::USER1);
00136 
00137     std::vector<double> adapted_vm, adapted_phi;
00138 
00139     reader->GetPointData("Vm", adapted_vm);
00140     reader->GetPointData("Phi", adapted_phi);
00141 
00142     Vec solution = mpMesh->GetDistributedVectorFactory()->CreateVec(2);
00143     DistributedVector nic = mpMesh->GetDistributedVectorFactory()->CreateDistributedVector(solution);
00144     std::vector<DistributedVector::Stripe> stripe;
00145     stripe.reserve(2);
00146 
00147     for (unsigned i=0; i<2; i++)
00148     {
00149         stripe.push_back(DistributedVector::Stripe(nic, i));
00150     }
00151 
00152     for (DistributedVector::Iterator it = nic.Begin();
00153              it != nic.End();
00154              ++it)
00155     {
00156         stripe[0][it] = adapted_vm[it.Global];
00157         stripe[1][it] = adapted_phi[it.Global];
00158     }
00159 
00160     nic.Restore();
00161 
00162     std::vector<std::string> state_variable_names = mpBidomainTissue->GetCardiacCell(0)->rGetStateVariableNames();
00163     unsigned number_of_state_variables = state_variable_names.size();
00164 
00165     for (unsigned variable=0; variable<number_of_state_variables; variable++)
00166     {
00167         if (variable != mpBidomainTissue->GetCardiacCell(0)->GetVoltageIndex())
00168         {
00169             std::vector<double> adapted_state_variable;
00170             reader->GetPointData( state_variable_names[variable], adapted_state_variable);
00171 
00172             for (DistributedVector::Iterator it = nic.Begin();
00173                      it != nic.End();
00174                      ++it)
00175             {
00176                 mpBidomainTissue->GetCardiacCell(it.Global)->SetStateVariable(variable, adapted_state_variable[it.Global]);
00177             }
00178         }
00179         else
00180         {
00181             for (DistributedVector::Iterator it = nic.Begin();
00182                      it != nic.End();
00183                      ++it)
00184             {
00185                 mpBidomainTissue->GetCardiacCell(it.Global)->SetStateVariable(variable, adapted_vm[it.Global]);
00186             }
00187 
00188         }
00189     }
00190 
00191     if (mSolution)
00192     {
00193         VecDestroy(mSolution);
00194     }
00195 
00196     mSolution = solution;
00197 
00198     HeartEventHandler::EndEvent(HeartEventHandler::USER1);
00199 }
00200 
00201 void AdaptiveBidomainProblem::AdaptMesh()
00202 {
00203     HeartEventHandler::BeginEvent(HeartEventHandler::USER1);
00204     mpAdaptiveMesh->AdaptMesh();
00205     HeartEventHandler::EndEvent(HeartEventHandler::USER1);
00206 
00207     if ( mpAdaptiveMesh->GetAdaptSuccess() )
00208     {
00209         if (mWriteInfo)
00210         {
00211             HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT);
00212             std::cout << "Adapt completed. New mesh has " << mpAdaptiveMesh->GetNumNodes() << " nodes" << std::endl;
00213             HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT);
00214         }
00215         // Adapt succeeded, need new mesh, boundary conditions, solver
00216         delete mpMesh;
00217         DistributedTetrahedralMesh<3,3>* p_new_mesh = new DistributedTetrahedralMesh<3,3>;
00218         VtkMeshReader<3,3> mesh_reader( mpAdaptiveMesh->GetVtkUnstructuredGrid() );
00219         HeartEventHandler::BeginEvent(HeartEventHandler::READ_MESH);
00220         p_new_mesh->ConstructFromMeshReader(mesh_reader);
00221         HeartEventHandler::EndEvent(HeartEventHandler::READ_MESH);
00222         mpMesh = p_new_mesh;
00223 
00224         mpCellFactory->SetMesh( mpMesh );
00225 
00226         if (mUseNeumannBoundaryConditions)
00227         {
00228             SetupNeumannBoundaryConditionOnMesh();
00229         }
00230         else
00231         {
00232             boost::shared_ptr<BoundaryConditionsContainer<3, 3, 2> > p_new_bcc(new BoundaryConditionsContainer<3, 3, 2>);
00233             for (unsigned problem_index=0; problem_index<2; problem_index++)
00234             {
00235                 p_new_bcc->DefineZeroNeumannOnMeshBoundary(mpMesh, problem_index);
00236             }
00237             mpBoundaryConditionsContainer = p_new_bcc;
00238         }
00239 
00240         delete mpBidomainTissue;
00241         BidomainTissue<3>* new_bidomain_tissue;
00242         new_bidomain_tissue = new BidomainTissue<3>(mpCellFactory);
00243         mpBidomainTissue = new_bidomain_tissue;
00244         mpCardiacTissue = mpBidomainTissue;
00245 
00246         delete mpSolver;
00247         BidomainSolver<3,3>* p_new_solver;
00248         p_new_solver = new BidomainSolver<3,3>(false, mpMesh, mpBidomainTissue,
00249                                                mpBoundaryConditionsContainer.get(), 2);
00250         mpSolver = p_new_solver;
00251         mpSolver->SetTimeStep(HeartConfig::Instance()->GetPdeTimeStep());
00252 
00253         InitializeSolutionOnAdaptedMesh( &mesh_reader );
00254     }
00255     else
00256     {
00257         NEVER_REACHED;
00258     }
00259 }
00260 
00261 void AdaptiveBidomainProblem::SetNeumannStimulusMagnitudeAndDuration(double magnitude, double duration, double period)
00262 {
00263     mNeumannStimulusMagnitude = magnitude;
00264     mNeumannStimulusDuration  = duration;
00265     mNeumannStimulusPeriod = std::min( period, HeartConfig::Instance()->GetSimulationDuration() );
00266 }
00267 
00268 void AdaptiveBidomainProblem::UseNeumannBoundaryCondition(unsigned index)
00269 {
00270     mUseNeumannBoundaryConditions = true;
00271     mNeumannStimulusIndex = index;
00272 }
00273 
00274 double AdaptiveBidomainProblem::GetTargetError()
00275 {
00276     return HeartConfig::Instance()->GetTargetErrorForAdaptivity();
00277 }
00278 
00279 double AdaptiveBidomainProblem::GetSigma()
00280 {
00281     return HeartConfig::Instance()->GetSigmaForAdaptivity();
00282 }
00283 
00284 double AdaptiveBidomainProblem::GetMaxEdgeLength()
00285 {
00286     return HeartConfig::Instance()->GetMaxEdgeLengthForAdaptivity();
00287 }
00288 
00289 double AdaptiveBidomainProblem::GetMinEdgeLength()
00290 {
00291     return HeartConfig::Instance()->GetMinEdgeLengthForAdaptivity();
00292 }
00293 
00294 double AdaptiveBidomainProblem::GetGradation()
00295 {
00296     return HeartConfig::Instance()->GetGradationForAdaptivity();
00297 }
00298 
00299 unsigned AdaptiveBidomainProblem::GetMaxMeshNodes()
00300 {
00301     return HeartConfig::Instance()->GetMaxNodesForAdaptivity();
00302 }
00303 
00304 unsigned AdaptiveBidomainProblem::GetNumAdaptSweeps()
00305 {
00306     return HeartConfig::Instance()->GetNumberOfAdaptiveSweeps();
00307 }
00308 
00309 void AdaptiveBidomainProblem::SetupNeumannBoundaryConditionOnMesh()
00310 {
00311     boost::shared_ptr<BoundaryConditionsContainer<3, 3, 2> > p_new_bcc(new BoundaryConditionsContainer<3, 3, 2>);
00312 
00313     mpNeumannStimulusBoundaryCondition = new StimulusBoundaryCondition<3>(mpNeumannStimulus);
00314     ConstBoundaryCondition<3>* p_zero_bc = new ConstBoundaryCondition<3>(0.0);
00315 
00316     // loop over boundary elements
00317     AbstractTetrahedralMesh<3,3>::BoundaryElementIterator iter;
00318     iter = mpMesh->GetBoundaryElementIteratorBegin();
00319 
00320     while (iter != mpMesh->GetBoundaryElementIteratorEnd())
00321     {
00322         double x = ((*iter)->CalculateCentroid())[mNeumannStimulusIndex];
00324         if ( (x-mNeumannStimulusLowerValue)*(x-mNeumannStimulusLowerValue) <= 1e-10 )
00325         {
00326             p_new_bcc->AddNeumannBoundaryCondition(*iter, mpNeumannStimulusBoundaryCondition);
00327         }
00328         iter++;
00329     }
00330 
00331     // Ground other end of domain
00332     for (AbstractTetrahedralMesh<3,3>::NodeIterator node_iter=mpMesh->GetNodeIteratorBegin();
00333          node_iter != mpMesh->GetNodeIteratorEnd();
00334          ++node_iter)
00335     {
00336         if (fabs((*node_iter).rGetLocation()[mNeumannStimulusIndex] - mNeumannStimulusUpperValue) < 1e-6)
00337         {
00338             p_new_bcc->AddDirichletBoundaryCondition(&(*node_iter), p_zero_bc, 1);
00339         }
00340     }
00341 
00342     mpBoundaryConditionsContainer = p_new_bcc;
00343 }
00344 
00345 void AdaptiveBidomainProblem::LoadSimulationFromVtuFile()
00346 {
00347     mInitializeFromVtu = true;
00348     AbstractCardiacProblem<3,3,2>::Initialise();
00349 }
00350 
00351 void AdaptiveBidomainProblem::Solve()
00352 {
00353     OutputFileHandler file_handler(HeartConfig::Instance()->GetOutputDirectory(), false);
00354 
00355     mOutputDirectory = file_handler.GetOutputDirectoryFullPath();
00356     mOutputFilenamePrefix = HeartConfig::Instance()->GetOutputFilenamePrefix();
00357 
00358     PreSolveChecks();
00359 
00360     mpNeumannStimulus = new RegularStimulus(mNeumannStimulusMagnitude, mNeumannStimulusDuration, mNeumannStimulusPeriod, 0.0);
00361 
00362     if (mUseNeumannBoundaryConditions)
00363     {
00364         // Determine the values a and b to apply Neumann bcs at x_i=a (stimulus), x_i=b (ground)
00365         double local_min = DBL_MAX;
00366         double local_max = -DBL_MAX;
00367 
00368         for (AbstractTetrahedralMesh<3,3>::NodeIterator iter=mpMesh->GetNodeIteratorBegin();
00369              iter != mpMesh->GetNodeIteratorEnd();
00370              ++iter)
00371         {
00372             double value = (*iter).rGetLocation()[mNeumannStimulusIndex];
00373             if(value < local_min)
00374             {
00375                 local_min = value;
00376             }
00377             if(value > local_max)
00378             {
00379                local_max = value;
00380             }
00381         }
00382 
00383         int mpi_ret = MPI_Allreduce(&local_min, &mNeumannStimulusLowerValue, 1, MPI_DOUBLE, MPI_MIN, PETSC_COMM_WORLD);
00384         assert(mpi_ret == MPI_SUCCESS);
00385         mpi_ret = MPI_Allreduce(&local_max, &mNeumannStimulusUpperValue, 1, MPI_DOUBLE, MPI_MAX, PETSC_COMM_WORLD);
00386         assert(mpi_ret == MPI_SUCCESS);
00387 
00388         SetupNeumannBoundaryConditionOnMesh();
00389     }
00390     else
00391     {
00392         if(mpBoundaryConditionsContainer == NULL) // the user didnt supply a bcc
00393         {
00394             // set up the default bcc
00395             boost::shared_ptr<BoundaryConditionsContainer<3, 3, 2> > p_allocated_memory(new BoundaryConditionsContainer<3, 3, 2>);
00396             mpDefaultBoundaryConditionsContainer = p_allocated_memory;
00397             for (unsigned problem_index=0; problem_index<2; problem_index++)
00398             {
00399                 mpDefaultBoundaryConditionsContainer->DefineZeroNeumannOnMeshBoundary(mpMesh, problem_index);
00400             }
00401             mpBoundaryConditionsContainer = mpDefaultBoundaryConditionsContainer;
00402         }
00403     }
00404 
00405     mpSolver = new BidomainSolver<3,3>(false, mpMesh, mpBidomainTissue,
00406                                        mpBoundaryConditionsContainer.get(), 2);
00407     mSolution = CreateInitialCondition();
00408 
00409     TimeStepper stepper(0.0, HeartConfig::Instance()->GetSimulationDuration(),
00410                         HeartConfig::Instance()->GetPrintingTimeStep());
00411 
00412     std::string progress_reporter_dir;
00413 
00414     assert( !mPrintOutput );
00415 
00416     progress_reporter_dir = ""; // progress printed to CHASTE_TEST_OUTPUT
00417 
00418     // create a progress reporter so users can track how much has gone and
00419     // estimate how much time is left. (Note this has to be done after the
00420     // InitialiseWriter above (if mPrintOutput==true)
00421     ProgressReporter progress_reporter(progress_reporter_dir, 0.0, HeartConfig::Instance()->GetSimulationDuration());
00422     progress_reporter.Update(0);
00423 
00424     // Initialize adaptive mesh using Chaste mesh
00425 //    mpAdaptiveMesh->ConstructFromMesh( mpMesh );
00426 //    mpAdaptiveMesh->CalculateSENListAndSids();
00427 
00428     // Get initial condition from file, or add Chaste initial condition to adaptive mesh
00429     if ( mInitializeFromVtu )
00430     {
00431         mpAdaptiveMesh->ConstructFromVtuFile( HeartConfig::Instance()->GetMeshName() );
00432         mpAdaptiveMesh->CalculateSENListAndSids();
00433 
00434         VtkMeshReader<3,3> mesh_reader( HeartConfig::Instance()->GetMeshName() );
00435 
00436         InitializeSolutionOnAdaptedMesh( &mesh_reader );
00437     }
00438     else
00439     {
00440         mpAdaptiveMesh->ConstructFromMesh( mpMesh );
00441         mpAdaptiveMesh->CalculateSENListAndSids();
00442         AddCurrentSolutionToAdaptiveMesh( mSolution );
00443     }
00444 
00445     // Use printing time step as adaptive time step...
00446 
00447     unsigned count = 0;
00448 
00449     // Set up filenames for convenient ParaView visualization
00450     std::ostringstream adapt_file_name, solution_file_name;
00451 
00452 //    {
00453 //        std::cout << "Values of Vm at node 2,000,000" << std::endl;
00454 //        ReplicatableVector replicatable_solution( mSolution );
00455 //        std::cout << replicatable_solution[2*2e6] << std::endl;        // Vm at node x is mSolution[2*x] (phi is mSolution[2*x+1])
00456 //    }
00457 
00458     mpSolver->SetTimeStep(HeartConfig::Instance()->GetPdeTimeStep());
00459 
00460     while ( !stepper.IsTimeAtEnd() )
00461     {
00462         {
00463             solution_file_name.str("");
00464             solution_file_name << mOutputFilenamePrefix << std::setw(4) << std::setfill('0') << count << ".vtu";
00465             HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT);
00466             mpAdaptiveMesh->WriteMeshToFile( mOutputDirectory, solution_file_name.str() );
00467             HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT);
00468         }
00469 
00470 
00471         // Adapt the mesh
00472         if ( mIsMeshAdapting && ( count > 0 || mInitializeFromVtu ) )
00473         {
00474             AdaptMesh();
00475         }
00476 
00477 //        // For non-adapting heart simulation, we may want to refine the mesh *ONCE* after the end of the stimulus
00478         // (in order to change the resolution).
00479 //        if ( (! mIsMeshAdapting) && (count == 1) )
00480 //        {
00481 //            AdaptMesh();
00482 //        }
00483 
00484         // Solve from now up to the next printing time step
00485         mpSolver->SetTimes(stepper.GetTime(), stepper.GetNextTime());
00486         mpSolver->SetInitialCondition( mSolution );
00487 
00488         try
00489         {
00490             Vec new_solution = mpSolver->Solve();
00491             VecDestroy(mSolution);
00492             mSolution = new_solution;
00493 //            ReplicatableVector replicatable_solution( mSolution );
00494 //            std::cout << replicatable_solution[2*2e6] << std::endl;        // Vm at node x is mSolution[2*x]
00495         }
00496         catch (Exception &e)
00497         {
00498             // Free memory.
00499 
00500             delete mpSolver;
00501             mpSolver=NULL;
00502             if (!mUseNeumannBoundaryConditions)
00503             {
00504                 mpDefaultBoundaryConditionsContainer = mpBoundaryConditionsContainer;
00505             }
00506             delete mpNeumannStimulus;
00507 
00508             PetscTools::ReplicateException(true);
00509             // Re-throw
00510             HeartEventHandler::Reset();//EndEvent(HeartEventHandler::EVERYTHING);
00511 
00512             CloseFilesAndPostProcess();
00513             throw e;
00514         }
00515         PetscTools::ReplicateException(false);
00516 
00517         // Add current solution into the node "point" data
00518         AddCurrentSolutionToAdaptiveMesh( mSolution );
00519 
00520         // update the current time
00521         stepper.AdvanceOneTimeStep();
00522 
00523         if (mWriteInfo)
00524         {
00525             HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT);
00526             WriteInfo(stepper.GetTime());
00527             HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT);
00528         }
00529 
00530         progress_reporter.Update(stepper.GetTime());
00531 
00532         OnEndOfTimestep(stepper.GetTime());
00533 
00534         count++;
00535     }
00536 
00537     {
00538         solution_file_name.str("");
00539         solution_file_name << mOutputFilenamePrefix << std::setw(4) << std::setfill('0') << count << ".vtu";
00540         HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT);
00541         mpAdaptiveMesh->WriteMeshToFile( mOutputDirectory, solution_file_name.str() );
00542         HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT);
00543     }
00544 
00545     // Free solver
00546     delete mpSolver;
00547 
00548     if (!mUseNeumannBoundaryConditions)
00549     {
00550         mpDefaultBoundaryConditionsContainer = mpBoundaryConditionsContainer;
00551     }
00552     delete mpNeumannStimulus;
00553 
00554     // close the file that stores voltage values
00555     progress_reporter.PrintFinalising();
00556     CloseFilesAndPostProcess();
00557     HeartEventHandler::EndEvent(HeartEventHandler::EVERYTHING);
00558 }
00559 
00560 #endif //CHASTE_ADAPTIVITY
Generated on Thu Dec 22 13:00:06 2011 for Chaste by  doxygen 1.6.3