AbstractCardiacProblem.cpp

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

Generated by  doxygen 1.6.2