AbstractCardiacProblem.cpp

00001 /*
00002 
00003 Copyright (c) 2005-2015, University of Oxford.
00004 All rights reserved.
00005 
00006 University of Oxford means the Chancellor, Masters and Scholars of the
00007 University of Oxford, having an administrative office at Wellington
00008 Square, Oxford OX1 2JD, UK.
00009 
00010 This file is part of Chaste.
00011 
00012 Redistribution and use in source and binary forms, with or without
00013 modification, are permitted provided that the following conditions are met:
00014  * Redistributions of source code must retain the above copyright notice,
00015    this list of conditions and the following disclaimer.
00016  * Redistributions in binary form must reproduce the above copyright notice,
00017    this list of conditions and the following disclaimer in the documentation
00018    and/or other materials provided with the distribution.
00019  * Neither the name of the University of Oxford nor the names of its
00020    contributors may be used to endorse or promote products derived from this
00021    software without specific prior written permission.
00022 
00023 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00024 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00025 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00026 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
00027 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00028 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00029 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00030 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
00032 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033 
00034 */
00035 
00036 #include "AbstractCardiacProblem.hpp"
00037 
00038 #include "GenericMeshReader.hpp"
00039 #include "Exception.hpp"
00040 #include "HeartConfig.hpp"
00041 #include "HeartEventHandler.hpp"
00042 #include "TimeStepper.hpp"
00043 #include "PetscTools.hpp"
00044 #include "DistributedVector.hpp"
00045 #include "ProgressReporter.hpp"
00046 #include "LinearSystem.hpp"
00047 #include "PostProcessingWriter.hpp"
00048 #include "Hdf5ToMeshalyzerConverter.hpp"
00049 #include "Hdf5ToCmguiConverter.hpp"
00050 #include "Hdf5ToVtkConverter.hpp"
00051 
00052 
00053 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00054 AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::AbstractCardiacProblem(
00055             AbstractCardiacCellFactory<ELEMENT_DIM,SPACE_DIM>* pCellFactory)
00056     : mMeshFilename(""), // i.e. undefined
00057       mAllocatedMemoryForMesh(false),
00058       mWriteInfo(false),
00059       mPrintOutput(true),
00060       mpCardiacTissue(NULL),
00061       mpSolver(NULL),
00062       mpCellFactory(pCellFactory),
00063       mpMesh(NULL),
00064       mSolution(NULL),
00065       mCurrentTime(0.0),
00066       mpTimeAdaptivityController(NULL),
00067       mpWriter(NULL)
00068 {
00069     assert(mNodesToOutput.empty());
00070     if (!mpCellFactory)
00071     {
00072         EXCEPTION("AbstractCardiacProblem: Please supply a cell factory pointer to your cardiac problem constructor.");
00073     }
00074     HeartEventHandler::BeginEvent(HeartEventHandler::EVERYTHING);
00075 }
00076 
00077 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00078 AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::AbstractCardiacProblem()
00079     // It doesn't really matter what we initialise these to, as they'll be overwritten by
00080     // the serialization methods
00081     : mMeshFilename(""),
00082       mAllocatedMemoryForMesh(false), // Handled by AbstractCardiacTissue
00083       mWriteInfo(false),
00084       mPrintOutput(true),
00085       mVoltageColumnId(UINT_MAX),
00086       mTimeColumnId(UINT_MAX),
00087       mNodeColumnId(UINT_MAX),
00088       mpCardiacTissue(NULL),
00089       mpSolver(NULL),
00090       mpCellFactory(NULL),
00091       mpMesh(NULL),
00092       mSolution(NULL),
00093       mCurrentTime(0.0),
00094       mpTimeAdaptivityController(NULL),
00095       mpWriter(NULL)
00096 {
00097 }
00098 
00099 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00100 AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::~AbstractCardiacProblem()
00101 {
00102     delete mpCardiacTissue;
00103     if (mSolution)
00104     {
00105         PetscTools::Destroy(mSolution);
00106     }
00107 
00108     if (mAllocatedMemoryForMesh)
00109     {
00110         delete mpMesh;
00111     }
00112 }
00113 
00114 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00115 void AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::Initialise()
00116 {
00117     HeartEventHandler::BeginEvent(HeartEventHandler::READ_MESH);
00118     if (mpMesh)
00119     {
00120         if (PetscTools::IsParallel() && !dynamic_cast<DistributedTetrahedralMesh<ELEMENT_DIM,SPACE_DIM>*>(mpMesh))
00121         {
00122             WARNING("Using a non-distributed mesh in a parallel simulation is not a good idea.");
00123         }
00124     }
00125     else
00126     {
00127         // If no mesh has been passed, we get it from the configuration file
00128         try
00129         {
00130             if (HeartConfig::Instance()->GetLoadMesh())
00131             {
00132                 CreateMeshFromHeartConfig();
00133                 std::auto_ptr<AbstractMeshReader<ELEMENT_DIM, SPACE_DIM> > p_mesh_reader
00134                     = GenericMeshReader<ELEMENT_DIM, SPACE_DIM>(HeartConfig::Instance()->GetMeshName());
00135                 mpMesh->ConstructFromMeshReader(*p_mesh_reader);
00136             }
00137             else if (HeartConfig::Instance()->GetCreateMesh())
00138             {
00139                 CreateMeshFromHeartConfig();
00140                 assert(HeartConfig::Instance()->GetSpaceDimension()==SPACE_DIM);
00141                 double inter_node_space = HeartConfig::Instance()->GetInterNodeSpace();
00142 
00143                 switch(HeartConfig::Instance()->GetSpaceDimension())
00144                 {
00145                     case 1:
00146                     {
00147                         c_vector<double, 1> fibre_length;
00148                         HeartConfig::Instance()->GetFibreLength(fibre_length);
00149                         mpMesh->ConstructRegularSlabMesh(inter_node_space, fibre_length[0]);
00150                         break;
00151                     }
00152                     case 2:
00153                     {
00154                         c_vector<double, 2> sheet_dimensions; //cm
00155                         HeartConfig::Instance()->GetSheetDimensions(sheet_dimensions);
00156                         mpMesh->ConstructRegularSlabMesh(inter_node_space, sheet_dimensions[0], sheet_dimensions[1]);
00157                         break;
00158                     }
00159                     case 3:
00160                     {
00161                         c_vector<double, 3> slab_dimensions; //cm
00162                         HeartConfig::Instance()->GetSlabDimensions(slab_dimensions);
00163                         mpMesh->ConstructRegularSlabMesh(inter_node_space, slab_dimensions[0], slab_dimensions[1], slab_dimensions[2]);
00164                         break;
00165                     }
00166                     default:
00167                         NEVER_REACHED;
00168                 }
00169             }
00170             else
00171             {
00172                 NEVER_REACHED;
00173             }
00174 
00175             mAllocatedMemoryForMesh = true;
00176         }
00177         catch (Exception& e)
00178         {
00179             EXCEPTION(std::string("No mesh given: define it in XML parameters file or call SetMesh()\n") + e.GetShortMessage());
00180         }
00181     }
00182     mpCellFactory->SetMesh( mpMesh );
00183     HeartEventHandler::EndEvent(HeartEventHandler::READ_MESH);
00184 
00185     HeartEventHandler::BeginEvent(HeartEventHandler::INITIALISE);
00186 
00187     // If the user requested transmural stuff, we fill in the mCellHeterogeneityAreas here
00188     if (HeartConfig::Instance()->AreCellularTransmuralHeterogeneitiesRequested())
00189     {
00190         mpCellFactory->FillInCellularTransmuralAreas();
00191     }
00192 
00193     delete mpCardiacTissue; // In case we're called twice
00194     mpCardiacTissue = CreateCardiacTissue();
00195 
00196     HeartEventHandler::EndEvent(HeartEventHandler::INITIALISE);
00197 
00198     // Delete any previous solution, so we get a fresh initial condition
00199     if (mSolution)
00200     {
00201         HeartEventHandler::BeginEvent(HeartEventHandler::COMMUNICATION);
00202         PetscTools::Destroy(mSolution);
00203         mSolution = NULL;
00204         HeartEventHandler::EndEvent(HeartEventHandler::COMMUNICATION);
00205     }
00206 
00207     // Always start at time zero
00208     mCurrentTime = 0.0;
00209 
00210     // For Bidomain with bath, this is where we set up the electrodes
00211     SetElectrodes();
00212 }
00213 
00214 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00215 void AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::CreateMeshFromHeartConfig()
00216 {
00217     mpMesh = new DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>(HeartConfig::Instance()->GetMeshPartitioning());
00218 }
00219 
00220 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00221 void AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::SetBoundaryConditionsContainer(boost::shared_ptr<BoundaryConditionsContainer<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM> > pBcc)
00222 {
00223     this->mpBoundaryConditionsContainer = pBcc;
00224 }
00225 
00226 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00227 void AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::PreSolveChecks()
00228 {
00229     if ( mpCardiacTissue == NULL ) // if tissue is NULL, Initialise() probably hasn't been called
00230     {
00231         EXCEPTION("Cardiac tissue is null, Initialise() probably hasn't been called");
00232     }
00233     if ( HeartConfig::Instance()->GetSimulationDuration() <= mCurrentTime)
00234     {
00235         EXCEPTION("End time should be in the future");
00236     }
00237     if (mPrintOutput)
00238     {
00239         if( (HeartConfig::Instance()->GetOutputDirectory()=="") || (HeartConfig::Instance()->GetOutputFilenamePrefix()==""))
00240         {
00241             EXCEPTION("Either explicitly specify not to print output (call PrintOutput(false)) or specify the output directory and filename prefix");
00242         }
00243     }
00244 
00245     double end_time = HeartConfig::Instance()->GetSimulationDuration();
00246     double pde_time = HeartConfig::Instance()->GetPdeTimeStep();
00247 
00248     /*
00249      * MatrixIsConstant stuff requires CONSTANT dt - do some checks to make sure
00250      * the TimeStepper won't find non-constant dt.
00251      * Note: printing_time does not have to divide end_time, but dt must divide
00252      * printing_time and end_time.
00253      * HeartConfig checks pde_dt divides printing dt.
00254      */
00256     if( fabs(end_time - pde_time*round(end_time/pde_time)) > 1e-10 )
00257     {
00258         EXCEPTION("PDE timestep does not seem to divide end time - check parameters");
00259     }
00260 }
00261 
00262 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00263 Vec AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::CreateInitialCondition()
00264 {
00265     DistributedVectorFactory* p_factory = mpMesh->GetDistributedVectorFactory();
00266     Vec initial_condition = p_factory->CreateVec(PROBLEM_DIM);
00267     DistributedVector ic = p_factory->CreateDistributedVector(initial_condition);
00268     std::vector<DistributedVector::Stripe> stripe;
00269     stripe.reserve(PROBLEM_DIM);
00270 
00271     for (unsigned i=0; i<PROBLEM_DIM; i++)
00272     {
00273         stripe.push_back(DistributedVector::Stripe(ic, i));
00274     }
00275 
00276     for (DistributedVector::Iterator index = ic.Begin();
00277          index != ic.End();
00278          ++index)
00279     {
00280         stripe[0][index] = mpCardiacTissue->GetCardiacCell(index.Global)->GetVoltage();
00281         if (PROBLEM_DIM==2)
00282         {
00283             stripe[1][index] = 0;
00284         }
00285     }
00286 
00287     ic.Restore();
00288 
00289     return initial_condition;
00290 }
00291 
00292 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00293 void AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::SetMesh(AbstractTetrahedralMesh<ELEMENT_DIM,SPACE_DIM>* pMesh)
00294 {
00295     /*
00296      * If this fails the mesh has already been set. We assert rather throw
00297      * an exception to avoid a memory leak when checking it throws correctly.
00298      */
00299     assert(mpMesh == NULL);
00300     assert(pMesh != NULL);
00301     mAllocatedMemoryForMesh = false;
00302     mpMesh = pMesh;
00303 }
00304 
00305 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00306 void AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::PrintOutput(bool printOutput)
00307 {
00308     mPrintOutput = printOutput;
00309 }
00310 
00311 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00312 void AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::SetWriteInfo(bool writeInfo)
00313 {
00314     mWriteInfo = writeInfo;
00315 }
00316 
00317 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00318 Vec AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::GetSolution()
00319 {
00320     return mSolution;
00321 }
00322 
00323 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00324 DistributedVector AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::GetSolutionDistributedVector()
00325 {
00326     return mpMesh->GetDistributedVectorFactory()->CreateDistributedVector(mSolution);
00327 }
00328 
00329 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00330 double AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::GetCurrentTime()
00331 {
00332     return mCurrentTime;
00333 }
00334 
00335 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00336 AbstractTetrahedralMesh<ELEMENT_DIM,SPACE_DIM> & AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::rGetMesh()
00337 {
00338     assert (mpMesh);
00339     return *mpMesh;
00340 }
00341 
00342 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00343 AbstractCardiacTissue<ELEMENT_DIM,SPACE_DIM>* AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::GetTissue()
00344 {
00345     if (mpCardiacTissue == NULL)
00346     {
00347         EXCEPTION("Tissue not yet set up, you may need to call Initialise() before GetTissue().");
00348     }
00349     return mpCardiacTissue;
00350 }
00351 
00352 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00353 void AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::SetUseTimeAdaptivityController(
00354         bool useAdaptivity,
00355         AbstractTimeAdaptivityController* pController)
00356 {
00357     if (useAdaptivity)
00358     {
00359         assert(pController);
00360         mpTimeAdaptivityController = pController;
00361     }
00362     else
00363     {
00364         mpTimeAdaptivityController = NULL;
00365     }
00366 }
00367 
00368 
00369 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00370 void AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::Solve()
00371 {
00372     PreSolveChecks();
00373 
00374     std::vector<double> additional_stopping_times;
00375     SetUpAdditionalStoppingTimes(additional_stopping_times);
00376 
00377     TimeStepper stepper(mCurrentTime,
00378                         HeartConfig::Instance()->GetSimulationDuration(),
00379                         HeartConfig::Instance()->GetPrintingTimeStep(),
00380                         false,
00381                         additional_stopping_times);
00382     // Note that SetUpAdditionalStoppingTimes is a method from the BidomainWithBath class it adds
00383     // electrode events into the regular time-stepping
00384     //    EXCEPTION("Electrode switch on/off events should coincide with printing time steps.");
00385 
00386     if (!mpBoundaryConditionsContainer) // the user didn't supply a bcc
00387     {
00388         // Set up the default bcc
00389         mpDefaultBoundaryConditionsContainer.reset(new BoundaryConditionsContainer<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>);
00390         for (unsigned problem_index=0; problem_index<PROBLEM_DIM; problem_index++)
00391         {
00392             mpDefaultBoundaryConditionsContainer->DefineZeroNeumannOnMeshBoundary(mpMesh, problem_index);
00393         }
00394         mpBoundaryConditionsContainer = mpDefaultBoundaryConditionsContainer;
00395     }
00396 
00397     assert(mpSolver==NULL);
00398     mpSolver = CreateSolver(); // passes mpBoundaryConditionsContainer to solver
00399 
00400     // If we have already run a simulation, use the old solution as initial condition
00401     Vec initial_condition;
00402     if (mSolution)
00403     {
00404         initial_condition = mSolution;
00405     }
00406     else
00407     {
00408         initial_condition = CreateInitialCondition();
00409     }
00410 
00411     std::string progress_reporter_dir;
00412 
00413     if (mPrintOutput)
00414     {
00415         HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT);
00416         bool extending_file = false;
00417         try
00418         {
00419             extending_file = InitialiseWriter();
00420         }
00421         catch (Exception& e)
00422         {
00423             delete mpWriter;
00424             mpWriter = NULL;
00425             delete mpSolver;
00426             if (mSolution != initial_condition)
00427             {
00428                 /*
00429                  * A PETSc Vec is a pointer, so we *don't* need to free the memory if it is
00430                  * freed somewhere else (e.g. in the destructor). If this is a resumed solution
00431                  * we set initial_condition = mSolution earlier. mSolution is going to be
00432                  * cleaned up in the constructor. So, only PetscTools::Destroy( initial_condition ) when
00433                  * it is not equal to mSolution.
00434                  */
00435                 PetscTools::Destroy(initial_condition);
00436             }
00437             throw e;
00438         }
00439 
00440         /*
00441          * If we are resuming a simulation (i.e. mSolution already exists) and
00442          * we are extending a .h5 file that already exists then there is no need
00443          * to write the initial condition to file - it is already there as the
00444          * final solution of the previous run.
00445          */
00446         if (!(mSolution && extending_file))
00447         {
00448             WriteOneStep(stepper.GetTime(), initial_condition);
00449             mpWriter->AdvanceAlongUnlimitedDimension();
00450         }
00451         HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT);
00452 
00453         progress_reporter_dir = HeartConfig::Instance()->GetOutputDirectory();
00454     }
00455     else
00456     {
00457         progress_reporter_dir = ""; // progress printed to CHASTE_TEST_OUTPUT
00458     }
00459     BOOST_FOREACH(boost::shared_ptr<AbstractOutputModifier> p_output_modifier, mOutputModifiers)
00460     {
00461         p_output_modifier->InitialiseAtStart(this->mpMesh->GetDistributedVectorFactory());
00462         p_output_modifier->ProcessSolutionAtTimeStep(stepper.GetTime(), initial_condition, PROBLEM_DIM);
00463     }
00464 
00465 
00466     /*
00467      * Create a progress reporter so users can track how much has gone and
00468      * estimate how much time is left. Note this has to be done after the
00469      * InitialiseWriter above (if mPrintOutput==true).
00470      */
00471     ProgressReporter progress_reporter(progress_reporter_dir,
00472                                        mCurrentTime,
00473                                        HeartConfig::Instance()->GetSimulationDuration());
00474     progress_reporter.Update(mCurrentTime);
00475 
00476     mpSolver->SetTimeStep(HeartConfig::Instance()->GetPdeTimeStep());
00477     if (mpTimeAdaptivityController)
00478     {
00479         mpSolver->SetTimeAdaptivityController(mpTimeAdaptivityController);
00480     }
00481 
00482     while (!stepper.IsTimeAtEnd())
00483     {
00484         // Solve from now up to the next printing time
00485         mpSolver->SetTimes(stepper.GetTime(), stepper.GetNextTime());
00486         mpSolver->SetInitialCondition( initial_condition );
00487 
00488         AtBeginningOfTimestep(stepper.GetTime());
00489 
00490         try
00491         {
00492             try
00493             {
00494                 mSolution = mpSolver->Solve();
00495             }
00496             catch (const Exception &e)
00497             {
00498 #ifndef NDEBUG
00499                 PetscTools::ReplicateException(true);
00500 #endif
00501                 throw e;
00502             }
00503 #ifndef NDEBUG
00504             PetscTools::ReplicateException(false);
00505 #endif
00506         }
00507         catch (const Exception& e)
00508         {
00509             // Free memory
00510             delete mpSolver;
00511             mpSolver = NULL;
00512             if (initial_condition != mSolution)
00513             {
00514                 /*
00515                  * A PETSc Vec is a pointer, so we *don't* need to free the memory if it is
00516                  * freed somewhere else (e.g. in the destructor). Later, in this while loop
00517                  * we will set initial_condition = mSolution (or, if this is a resumed solution
00518                  * it may also have been done when initial_condition was created). mSolution
00519                  * is going to be cleaned up in the destructor. So, only PetscTools::Destroy()
00520                  * initial_condition when it is not equal to mSolution (see #1695).
00521                  */
00522                 PetscTools::Destroy(initial_condition);
00523             }
00524 
00525             // Re-throw
00526             HeartEventHandler::Reset();
00527             CloseFilesAndPostProcess();
00528 
00529             throw e;
00530         }
00531 
00532         // Free old initial condition
00533         HeartEventHandler::BeginEvent(HeartEventHandler::COMMUNICATION);
00534         PetscTools::Destroy(initial_condition);
00535         HeartEventHandler::EndEvent(HeartEventHandler::COMMUNICATION);
00536 
00537         // Initial condition for next loop is current solution
00538         initial_condition = mSolution;
00539 
00540         // Update the current time
00541         stepper.AdvanceOneTimeStep();
00542         mCurrentTime = stepper.GetTime();
00543 
00544         // Print out details at current time if asked for
00545         if (mWriteInfo)
00546         {
00547             HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT);
00548             WriteInfo(stepper.GetTime());
00549             HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT);
00550         }
00551 
00552         BOOST_FOREACH(boost::shared_ptr<AbstractOutputModifier> p_output_modifier, mOutputModifiers)
00553         {
00554             p_output_modifier->ProcessSolutionAtTimeStep(stepper.GetTime(), mSolution, PROBLEM_DIM);
00555         }
00556         if (mPrintOutput)
00557         {
00558             // Writing data out to the file <FilenamePrefix>.dat
00559             HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT);
00560             WriteOneStep(stepper.GetTime(), mSolution);
00561             // Just flags that we've finished a time-step; won't actually 'extend' unless new data is written.
00562             mpWriter->AdvanceAlongUnlimitedDimension();
00563 
00564             HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT);
00565         }
00566 
00567         progress_reporter.Update(stepper.GetTime());
00568 
00569         OnEndOfTimestep(stepper.GetTime());
00570     }
00571 
00572     // Free solver
00573     delete mpSolver;
00574     mpSolver = NULL;
00575 
00576     // Close the file that stores voltage values
00577     progress_reporter.PrintFinalising();
00578     BOOST_FOREACH(boost::shared_ptr<AbstractOutputModifier> p_output_modifier, mOutputModifiers)
00579     {
00580         p_output_modifier->FinaliseAtEnd();
00581     }
00582     CloseFilesAndPostProcess();
00583     HeartEventHandler::EndEvent(HeartEventHandler::EVERYTHING);
00584 }
00585 
00586 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00587 void AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::CloseFilesAndPostProcess()
00588 {
00589     // Close files
00590     if (!mPrintOutput)
00591     {
00592         // Nothing to do
00593         return;
00594     }
00595     delete mpWriter;
00596     mpWriter = NULL;
00597 
00598     FileFinder test_output(HeartConfig::Instance()->GetOutputDirectory(), RelativeTo::ChasteTestOutput);
00599 
00600     /********************************************************************************
00601      * Run all post processing.
00602      *
00603      * The PostProcessingWriter class examines what is requested in HeartConfig and
00604      * adds the relevant data to the HDF5 file.
00605      * This is converted to different visualizer formats along with the solution
00606      * in the DATA_CONVERSION block below.
00607      *********************************************************************************/
00608 
00609     HeartEventHandler::BeginEvent(HeartEventHandler::POST_PROC);
00610     if(HeartConfig::Instance()->IsPostProcessingRequested())
00611     {
00612         PostProcessingWriter<ELEMENT_DIM, SPACE_DIM> post_writer(*mpMesh,
00613                                                                  test_output,
00614                                                                  HeartConfig::Instance()->GetOutputFilenamePrefix());
00615         post_writer.WritePostProcessingFiles();
00616     }
00617     HeartEventHandler::EndEvent(HeartEventHandler::POST_PROC);
00618 
00619     /********************************************************************************************
00620      * Convert HDF5 datasets (solution and postprocessing maps) to different visualizer formats
00621      ********************************************************************************************/
00622 
00623     HeartEventHandler::BeginEvent(HeartEventHandler::DATA_CONVERSION);
00624     // Only if results files were written and we are outputting all nodes
00625     if (mNodesToOutput.empty())
00626     {
00627         if (HeartConfig::Instance()->GetVisualizeWithMeshalyzer())
00628         {
00629             // Convert simulation data to Meshalyzer format
00630             Hdf5ToMeshalyzerConverter<ELEMENT_DIM,SPACE_DIM> converter(test_output,
00631                     HeartConfig::Instance()->GetOutputFilenamePrefix(),
00632                     mpMesh,
00633                     HeartConfig::Instance()->GetOutputUsingOriginalNodeOrdering(),
00634                     HeartConfig::Instance()->GetVisualizerOutputPrecision() );
00635             std::string subdirectory_name = converter.GetSubdirectory();
00636             HeartConfig::Instance()->Write(false, subdirectory_name);
00637         }
00638 
00639         if (HeartConfig::Instance()->GetVisualizeWithCmgui())
00640         {
00641             // Convert simulation data to Cmgui format
00642             Hdf5ToCmguiConverter<ELEMENT_DIM,SPACE_DIM> converter(test_output,
00643                     HeartConfig::Instance()->GetOutputFilenamePrefix(),
00644                     mpMesh,
00645                     GetHasBath(),
00646                     HeartConfig::Instance()->GetVisualizerOutputPrecision() );
00647             std::string subdirectory_name = converter.GetSubdirectory();
00648             HeartConfig::Instance()->Write(false, subdirectory_name);
00649         }
00650 
00651         if (HeartConfig::Instance()->GetVisualizeWithVtk())
00652         {
00653             // Convert simulation data to VTK format
00654             Hdf5ToVtkConverter<ELEMENT_DIM,SPACE_DIM> converter(test_output,
00655                     HeartConfig::Instance()->GetOutputFilenamePrefix(),
00656                     mpMesh,
00657                     false,
00658                     HeartConfig::Instance()->GetOutputUsingOriginalNodeOrdering());
00659             std::string subdirectory_name = converter.GetSubdirectory();
00660             HeartConfig::Instance()->Write(false, subdirectory_name);
00661         }
00662 
00663         if (HeartConfig::Instance()->GetVisualizeWithParallelVtk())
00664         {
00665             // Convert simulation data to parallel VTK (pvtu) format
00666             Hdf5ToVtkConverter<ELEMENT_DIM,SPACE_DIM> converter(test_output,
00667                     HeartConfig::Instance()->GetOutputFilenamePrefix(),
00668                     mpMesh,
00669                     true,
00670                     HeartConfig::Instance()->GetOutputUsingOriginalNodeOrdering());
00671             std::string subdirectory_name = converter.GetSubdirectory();
00672             HeartConfig::Instance()->Write(false, subdirectory_name);
00673         }
00674     }
00675     HeartEventHandler::EndEvent(HeartEventHandler::DATA_CONVERSION);
00676 }
00677 
00678 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00679 void AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::DefineWriterColumns(bool extending)
00680 {
00681     if (!extending)
00682     {
00683         if ( mNodesToOutput.empty() )
00684         {
00685             //Set writer to output all nodes
00686             mpWriter->DefineFixedDimension( mpMesh->GetNumNodes() );
00687         }
00688         else
00689         {
00690             //Output only the nodes indicted
00691             mpWriter->DefineFixedDimension( mNodesToOutput, mpMesh->GetNumNodes() );
00692         }
00693         //mNodeColumnId = mpWriter->DefineVariable("Node", "dimensionless");
00694         mVoltageColumnId = mpWriter->DefineVariable("V","mV");
00695 
00696         // Only used to get an estimate of the # of timesteps below
00697         TimeStepper stepper(mCurrentTime,
00698                             HeartConfig::Instance()->GetSimulationDuration(),
00699                             HeartConfig::Instance()->GetPrintingTimeStep());
00700 
00701         mpWriter->DefineUnlimitedDimension("Time","msecs", stepper.EstimateTimeSteps()+1); // plus one for start and end points
00702     }
00703     else
00704     {
00705         mVoltageColumnId = mpWriter->GetVariableByName("V");
00706     }
00707 }
00708 
00709 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00710 void AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::DefineExtraVariablesWriterColumns(bool extending)
00711 {
00712     mExtraVariablesId.clear();
00713     // Check if any extra output variables have been requested
00714     if (HeartConfig::Instance()->GetOutputVariablesProvided())
00715     {
00716         // Get their names in a vector
00717         std::vector<std::string> output_variables;
00718         HeartConfig::Instance()->GetOutputVariables(output_variables);
00719         const unsigned num_vars = output_variables.size();
00720         mExtraVariablesId.reserve(num_vars);
00721 
00722         // Loop over them
00723         for (unsigned var_index=0; var_index<num_vars; var_index++)
00724         {
00725             // Get variable name
00726             std::string var_name = output_variables[var_index];
00727 
00728             // Register it (or look it up) in the data writer
00729             unsigned column_id;
00730             if (extending)
00731             {
00732                 column_id = this->mpWriter->GetVariableByName(var_name);
00733             }
00734             else
00735             {
00736                 // Difficult to specify the units, as different cell models
00737                 // at different points in the mesh could be using different units.
00738                 column_id = this->mpWriter->DefineVariable(var_name, "unknown_units");
00739             }
00740 
00741             // Store column id
00742             mExtraVariablesId.push_back(column_id);
00743         }
00744     }
00745 }
00746 
00747 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00748 void AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::WriteExtraVariablesOneStep()
00749 {
00750     // Get the variable names in a vector
00751     std::vector<std::string> output_variables;
00752     unsigned num_vars = mExtraVariablesId.size();
00753     if (num_vars > 0)
00754     {
00755         HeartConfig::Instance()->GetOutputVariables(output_variables);
00756     }
00757     assert(output_variables.size() == num_vars);
00758 
00759     // Loop over the requested variables
00760     for (unsigned var_index=0; var_index<num_vars; var_index++)
00761     {
00762         // Create vector for storing values over the local nodes
00763         Vec variable_data =  this->mpMesh->GetDistributedVectorFactory()->CreateVec();
00764         DistributedVector distributed_var_data = this->mpMesh->GetDistributedVectorFactory()->CreateDistributedVector(variable_data);
00765 
00766         // Loop over the local nodes and gather the data
00767         for (DistributedVector::Iterator index = distributed_var_data.Begin();
00768              index!= distributed_var_data.End();
00769              ++index)
00770         {
00771             // If the region is in the bath
00772             if (HeartRegionCode::IsRegionBath( this->mpMesh->GetNode( index.Global )->GetRegion() ))
00773             {
00774                 // Then we just pad the output with zeros, user currently needs to find a nice
00775                 // way to deal with this in processing and visualization.
00776                 distributed_var_data[index] = 0.0;
00777             }
00778             else
00779             {
00780                 // Find the variable in the cell model and store its value
00781                 distributed_var_data[index] = this->mpCardiacTissue->GetCardiacCell(index.Global)->
00782                                                 GetAnyVariable(output_variables[var_index], mCurrentTime);
00783             }
00784         }
00785         distributed_var_data.Restore();
00786 
00787         // Write it to disc
00788         this->mpWriter->PutVector(mExtraVariablesId[var_index], variable_data);
00789 
00790         PetscTools::Destroy(variable_data);
00791     }
00792 }
00793 
00794 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00795 bool AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::InitialiseWriter()
00796 {
00797     bool extend_file = (mSolution != NULL);
00798 
00799     // I think this is impossible to trip; certainly it's very difficult!
00800     assert(!mpWriter);
00801 
00802     if (extend_file)
00803     {
00804         FileFinder h5_file(OutputFileHandler::GetChasteTestOutputDirectory() + HeartConfig::Instance()->GetOutputDirectory()
00805                        + "/" + HeartConfig::Instance()->GetOutputFilenamePrefix() + ".h5",
00806                        RelativeTo::Absolute);
00807         //We are going to test for existence before creating the file.
00808         //Therefore we should make sure that this existence test is thread-safe.
00809         //(If another process creates the file too early then we may get the wrong answer to the
00810         //existence question).
00811         PetscTools::Barrier("InitialiseWriter::Extension check");
00812         if (!h5_file.Exists())
00813         {
00814             extend_file = false;
00815         }
00816         else // if it does exist check that it is sensible to extend it by running from the archive we loaded.
00817         {
00818             Hdf5DataReader reader(HeartConfig::Instance()->GetOutputDirectory(),
00819                                   HeartConfig::Instance()->GetOutputFilenamePrefix(),
00820                                   true);
00821             std::vector<double> times = reader.GetUnlimitedDimensionValues();
00822             if (times.back() > mCurrentTime)
00823             {
00824                 EXCEPTION("Attempting to extend " << h5_file.GetAbsolutePath() <<
00825                           " with results from time = " << mCurrentTime <<
00826                           ", but it already contains results up to time = " << times.back() << "."
00827                           " Calling HeartConfig::Instance()->SetOutputDirectory() before Solve() will direct results elsewhere.");
00828             }
00829         }
00830         PetscTools::Barrier("InitialiseWriter::Extension check");
00831     }
00832     mpWriter = new Hdf5DataWriter(*mpMesh->GetDistributedVectorFactory(),
00833                                   HeartConfig::Instance()->GetOutputDirectory(),
00834                                   HeartConfig::Instance()->GetOutputFilenamePrefix(),
00835                                   !extend_file, // don't clear directory if extension requested
00836                                   extend_file);
00837 
00838 
00839     // Define columns, or get the variable IDs from the writer
00840     DefineWriterColumns(extend_file);
00841 
00842     //Possibility of applying a permutation
00843     if (HeartConfig::Instance()->GetOutputUsingOriginalNodeOrdering())
00844     {
00845         bool success = mpWriter->ApplyPermutation(mpMesh->rGetNodePermutation(), true/*unsafe mode - extending*/);
00846         if (success == false)
00847         {
00848             //It's not really a permutation, so reset
00849             HeartConfig::Instance()->SetOutputUsingOriginalNodeOrdering(false);
00850         }
00851     }
00852 
00853     if (!extend_file)
00854     {
00855         mpWriter->EndDefineMode();
00856     }
00857 
00858     return extend_file;
00859 }
00860 
00861 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00862 void AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::SetOutputNodes(std::vector<unsigned> &nodesToOutput)
00863 {
00864     mNodesToOutput = nodesToOutput;
00865 }
00866 
00867 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00868 Hdf5DataReader AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::GetDataReader()
00869 {
00870     if( (HeartConfig::Instance()->GetOutputDirectory()=="") || (HeartConfig::Instance()->GetOutputFilenamePrefix()==""))
00871     {
00872         EXCEPTION("Data reader invalid as data writer cannot be initialised");
00873     }
00874     return Hdf5DataReader(HeartConfig::Instance()->GetOutputDirectory(), HeartConfig::Instance()->GetOutputFilenamePrefix());
00875 }
00876 
00877 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00878 bool AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::GetHasBath()
00879 {
00880     return false;
00881 }
00882 
00883 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00884 void AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::SetElectrodes()
00885 {
00886 }
00887 
00888 
00890 // Explicit instantiation
00892 
00893 // Monodomain
00894 template class AbstractCardiacProblem<1,1,1>;
00895 template class AbstractCardiacProblem<1,2,1>;
00896 template class AbstractCardiacProblem<1,3,1>;
00897 template class AbstractCardiacProblem<2,2,1>;
00898 template class AbstractCardiacProblem<3,3,1>;
00899 
00900 // Bidomain
00901 template class AbstractCardiacProblem<1,1,2>;
00902 template class AbstractCardiacProblem<2,2,2>;
00903 template class AbstractCardiacProblem<3,3,2>;
00904 
00905 // Extended Bidomain
00906 template class AbstractCardiacProblem<1,1,3>;
00907 template class AbstractCardiacProblem<2,2,3>;
00908 template class AbstractCardiacProblem<3,3,3>;

Generated by  doxygen 1.6.2