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