Chaste Release::3.1
|
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>;