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 "MatrixBasedBidomainSolver.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         MatrixBasedBidomainSolver<3,3>* p_new_solver;
00248         p_new_solver = new MatrixBasedBidomainSolver<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];
00323         if ( (x-mNeumannStimulusLowerValue)*(x-mNeumannStimulusLowerValue) <= 1e-10 )
00324         {
00325             p_new_bcc->AddNeumannBoundaryCondition(*iter, mpNeumannStimulusBoundaryCondition);
00326         }
00327         iter++;
00328     }
00329 
00330     // Ground other end of domain
00331     for (AbstractTetrahedralMesh<3,3>::NodeIterator node_iter=mpMesh->GetNodeIteratorBegin();
00332          node_iter != mpMesh->GetNodeIteratorEnd();
00333          ++node_iter)
00334     {
00335         if (fabs((*node_iter).rGetLocation()[mNeumannStimulusIndex] - mNeumannStimulusUpperValue) < 1e-6)
00336         {
00337             p_new_bcc->AddDirichletBoundaryCondition(&(*node_iter), p_zero_bc, 1);
00338         }
00339     }
00340 
00341     mpBoundaryConditionsContainer = p_new_bcc;
00342 }
00343 
00344 void AdaptiveBidomainProblem::LoadSimulationFromVtuFile()
00345 {
00346     mInitializeFromVtu = true;
00347     AbstractCardiacProblem<3,3,2>::Initialise();
00348 }
00349 
00350 void AdaptiveBidomainProblem::Solve()
00351 {
00352     OutputFileHandler file_handler(HeartConfig::Instance()->GetOutputDirectory(), false);
00353 
00354     mOutputDirectory = file_handler.GetOutputDirectoryFullPath();
00355     mOutputFilenamePrefix = HeartConfig::Instance()->GetOutputFilenamePrefix();
00356 
00357     PreSolveChecks();
00358 
00359     mpNeumannStimulus = new RegularStimulus(mNeumannStimulusMagnitude, mNeumannStimulusDuration, mNeumannStimulusPeriod, 0.0);
00360 
00361     if (mUseNeumannBoundaryConditions)
00362     {
00363         // Determine the values a and b to apply Neumann bcs at x_i=a (stimulus), x_i=b (ground)
00364         double local_min = DBL_MAX;
00365         double local_max = -DBL_MAX;
00366 
00367         for (AbstractTetrahedralMesh<3,3>::NodeIterator iter=mpMesh->GetNodeIteratorBegin();
00368              iter != mpMesh->GetNodeIteratorEnd();
00369              ++iter)
00370         {
00371             double value = (*iter).rGetLocation()[mNeumannStimulusIndex];
00372             if(value < local_min)
00373             {
00374                 local_min = value;
00375             }
00376             if(value > local_max)
00377             {
00378                local_max = value;
00379             }
00380         }
00381 
00382         int mpi_ret = MPI_Allreduce(&local_min, &mNeumannStimulusLowerValue, 1, MPI_DOUBLE, MPI_MIN, PETSC_COMM_WORLD);
00383         assert(mpi_ret == MPI_SUCCESS);
00384         mpi_ret = MPI_Allreduce(&local_max, &mNeumannStimulusUpperValue, 1, MPI_DOUBLE, MPI_MAX, PETSC_COMM_WORLD);
00385         assert(mpi_ret == MPI_SUCCESS);
00386 
00387         SetupNeumannBoundaryConditionOnMesh();
00388     }
00389     else
00390     {
00391         if(mpBoundaryConditionsContainer == NULL) // the user didnt supply a bcc
00392         {
00393             // set up the default bcc
00394             boost::shared_ptr<BoundaryConditionsContainer<3, 3, 2> > p_allocated_memory(new BoundaryConditionsContainer<3, 3, 2>);
00395             mpDefaultBoundaryConditionsContainer = p_allocated_memory;
00396             for (unsigned problem_index=0; problem_index<2; problem_index++)
00397             {
00398                 mpDefaultBoundaryConditionsContainer->DefineZeroNeumannOnMeshBoundary(mpMesh, problem_index);
00399             }
00400             mpBoundaryConditionsContainer = mpDefaultBoundaryConditionsContainer;
00401         }
00402     }
00403 
00404     mpSolver = new MatrixBasedBidomainSolver<3,3>(false, mpMesh, mpBidomainTissue,
00405                                                   mpBoundaryConditionsContainer.get(), 2);
00406     mSolution = CreateInitialCondition();
00407 
00408     TimeStepper stepper(0.0, HeartConfig::Instance()->GetSimulationDuration(),
00409                         HeartConfig::Instance()->GetPrintingTimeStep());
00410 
00411     std::string progress_reporter_dir;
00412 
00413     assert( !mPrintOutput );
00414 
00415     progress_reporter_dir = ""; // progress printed to CHASTE_TEST_OUTPUT
00416 
00417     // create a progress reporter so users can track how much has gone and
00418     // estimate how much time is left. (Note this has to be done after the
00419     // InitialiseWriter above (if mPrintOutput==true)
00420     ProgressReporter progress_reporter(progress_reporter_dir, 0.0, HeartConfig::Instance()->GetSimulationDuration());
00421     progress_reporter.Update(0);
00422 
00423     // Initialize adaptive mesh using Chaste mesh
00424 //    mpAdaptiveMesh->ConstructFromMesh( mpMesh );
00425 //    mpAdaptiveMesh->CalculateSENListAndSids();
00426 
00427     // Get initial condition from file, or add Chaste initial condition to adaptive mesh
00428     if ( mInitializeFromVtu )
00429     {
00430         mpAdaptiveMesh->ConstructFromVtuFile( HeartConfig::Instance()->GetMeshName() );
00431         mpAdaptiveMesh->CalculateSENListAndSids();
00432 
00433         VtkMeshReader<3,3> mesh_reader( HeartConfig::Instance()->GetMeshName() );
00434 
00435         InitializeSolutionOnAdaptedMesh( &mesh_reader );
00436     }
00437     else
00438     {
00439         mpAdaptiveMesh->ConstructFromMesh( mpMesh );
00440         mpAdaptiveMesh->CalculateSENListAndSids();
00441         AddCurrentSolutionToAdaptiveMesh( mSolution );
00442     }
00443 
00444     // Use printing time step as adaptive time step...
00445 
00446     unsigned count = 0;
00447 
00448     // Set up filenames for convenient ParaView visualization
00449     std::ostringstream adapt_file_name, solution_file_name;
00450 
00451 //    {
00452 //        std::cout << "Values of Vm at node 2,000,000" << std::endl;
00453 //        ReplicatableVector replicatable_solution( mSolution );
00454 //        std::cout << replicatable_solution[2*2e6] << std::endl;        // Vm at node x is mSolution[2*x] (phi is mSolution[2*x+1])
00455 //    }
00456 
00457     mpSolver->SetTimeStep(HeartConfig::Instance()->GetPdeTimeStep());
00458 
00459     while ( !stepper.IsTimeAtEnd() )
00460     {
00461         {
00462             solution_file_name.str("");
00463             solution_file_name << mOutputFilenamePrefix << std::setw(4) << std::setfill('0') << count << ".vtu";
00464             HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT);
00465             mpAdaptiveMesh->WriteMeshToFile( mOutputDirectory, solution_file_name.str() );
00466             HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT);
00467         }
00468 
00469 
00470         // Adapt the mesh
00471         if ( mIsMeshAdapting && ( count > 0 || mInitializeFromVtu ) )
00472         {
00473             AdaptMesh();
00474         }
00475 
00476 //        // For non-adapting heart simulation, we may want to refine the mesh *ONCE* after the end of the stimulus
00477         // (in order to change the resolution).
00478 //        if ( (! mIsMeshAdapting) && (count == 1) )
00479 //        {
00480 //            AdaptMesh();
00481 //        }
00482 
00483         // Solve from now up to the next printing time step
00484         mpSolver->SetTimes(stepper.GetTime(), stepper.GetNextTime());
00485         mpSolver->SetInitialCondition( mSolution );
00486 
00487         try
00488         {
00489             Vec new_solution = mpSolver->Solve();
00490             VecDestroy(mSolution);
00491             mSolution = new_solution;
00492 //            ReplicatableVector replicatable_solution( mSolution );
00493 //            std::cout << replicatable_solution[2*2e6] << std::endl;        // Vm at node x is mSolution[2*x]
00494         }
00495         catch (Exception &e)
00496         {
00497             // Free memory.
00498 
00499             delete mpSolver;
00500             mpSolver=NULL;
00501             if (!mUseNeumannBoundaryConditions)
00502             {
00503                 mpDefaultBoundaryConditionsContainer = mpBoundaryConditionsContainer;
00504             }
00505             delete mpNeumannStimulus;
00506 
00507             PetscTools::ReplicateException(true);
00508             // Re-throw
00509             HeartEventHandler::Reset();//EndEvent(HeartEventHandler::EVERYTHING);
00510 
00511             CloseFilesAndPostProcess();
00512             throw e;
00513         }
00514         PetscTools::ReplicateException(false);
00515 
00516         // Add current solution into the node "point" data
00517         AddCurrentSolutionToAdaptiveMesh( mSolution );
00518 
00519         // update the current time
00520         stepper.AdvanceOneTimeStep();
00521 
00522         if (mWriteInfo)
00523         {
00524             HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT);
00525             WriteInfo(stepper.GetTime());
00526             HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT);
00527         }
00528 
00529         progress_reporter.Update(stepper.GetTime());
00530 
00531         OnEndOfTimestep(stepper.GetTime());
00532 
00533         count++;
00534     }
00535 
00536     {
00537         solution_file_name.str("");
00538         solution_file_name << mOutputFilenamePrefix << std::setw(4) << std::setfill('0') << count << ".vtu";
00539         HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT);
00540         mpAdaptiveMesh->WriteMeshToFile( mOutputDirectory, solution_file_name.str() );
00541         HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT);
00542     }
00543 
00544     // Free solver
00545     delete mpSolver;
00546 
00547     if (!mUseNeumannBoundaryConditions)
00548     {
00549         mpDefaultBoundaryConditionsContainer = mpBoundaryConditionsContainer;
00550     }
00551     delete mpNeumannStimulus;
00552 
00553     // close the file that stores voltage values
00554     progress_reporter.PrintFinalising();
00555     CloseFilesAndPostProcess();
00556     HeartEventHandler::EndEvent(HeartEventHandler::EVERYTHING);
00557 }
00558 
00559 #endif //CHASTE_ADAPTIVITY

Generated on Tue May 31 14:31:41 2011 for Chaste by  doxygen 1.5.5