00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "AbstractCardiacProblem.hpp"
00030
00031 #include "TrianglesMeshReader.hpp"
00032 #include "TetrahedralMesh.hpp"
00033 #include "ParallelTetrahedralMesh.hpp"
00034 #include "MeshalyzerMeshWriter.hpp"
00035 #include "Hdf5ToMeshalyzerConverter.hpp"
00036 #include "Exception.hpp"
00037 #include "HeartConfig.hpp"
00038 #include "HeartEventHandler.hpp"
00039 #include "TimeStepper.hpp"
00040 #include "PetscTools.hpp"
00041 #include "DistributedVector.hpp"
00042 #include "ProgressReporter.hpp"
00043
00044 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00045 AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::AbstractCardiacProblem(
00046 AbstractCardiacCellFactory<SPACE_DIM>* pCellFactory)
00047 : mMeshFilename(""),
00048 mNodesPerProcessorFilename(""),
00049 mOutputDirectory(""),
00050 mOutputFilenamePrefix(""),
00051 mUseMatrixBasedRhsAssembly(true),
00052 mpBoundaryConditionsContainer(NULL),
00053 mpDefaultBoundaryConditionsContainer(NULL),
00054 mpCellFactory(pCellFactory),
00055 mpMesh(NULL),
00056 mpWriter(NULL)
00057 {
00058 mWriteInfo = false;
00059 mPrintOutput = true;
00060 mCallChaste2Meshalyzer = false;
00061 mpCardiacPde = NULL;
00062 mpAssembler = NULL;
00063 mSolution = NULL;
00064 mAllocatedMemoryForMesh = false;
00065 assert(mNodesToOutput.empty());
00066
00067 HeartEventHandler::BeginEvent(HeartEventHandler::EVERYTHING);
00068 }
00069
00070 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00071 AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::~AbstractCardiacProblem()
00072 {
00073 if(mpDefaultBoundaryConditionsContainer!=NULL)
00074 {
00075 delete mpDefaultBoundaryConditionsContainer;
00076 }
00077
00078 delete mpCardiacPde;
00079 if (mSolution)
00080 {
00081 VecDestroy(mSolution);
00082 }
00083
00084 if (mAllocatedMemoryForMesh)
00085 {
00086 delete mpMesh;
00087 }
00088 };
00089
00090 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00091 void AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::Initialise()
00092 {
00093 mOutputDirectory = HeartConfig::Instance()->GetOutputDirectory();
00094 mOutputFilenamePrefix = HeartConfig::Instance()->GetOutputFilenamePrefix();
00095
00096 if (mpMesh==NULL)
00097 {
00098
00099 try
00100 {
00102 if(HeartConfig::Instance()->GetLoadMesh())
00103 {
00104 TrianglesMeshReader<SPACE_DIM, SPACE_DIM> mesh_reader(HeartConfig::Instance()->GetMeshName());
00105 if (SPACE_DIM == 1)
00106 {
00108 mpMesh = new TetrahedralMesh<SPACE_DIM, SPACE_DIM>();
00109 }
00110 else
00111 {
00112 mpMesh = new ParallelTetrahedralMesh<SPACE_DIM, SPACE_DIM>();
00113 }
00114
00115 HeartEventHandler::BeginEvent(HeartEventHandler::READ_MESH);
00116 mpMesh->ConstructFromMeshReader(mesh_reader);
00117 HeartEventHandler::EndEvent(HeartEventHandler::READ_MESH);
00118 }
00119 else
00120 {
00121 if(HeartConfig::Instance()->GetCreateMesh())
00122 {
00123 switch(HeartConfig::Instance()->GetSpaceDimension())
00124 {
00125 case 1:
00126 {
00127 c_vector<double, 1> fibre_length;
00128 HeartConfig::Instance()->GetFibreLength(fibre_length);
00129 double inter_node_space = HeartConfig::Instance()->GetInterNodeSpace();
00130
00131 mpMesh = new TetrahedralMesh<SPACE_DIM, SPACE_DIM>();
00132
00133 unsigned slab_nodes_x = (unsigned)round(fibre_length[0]/inter_node_space);
00134
00135 static_cast<TetrahedralMesh<SPACE_DIM, SPACE_DIM>*>(mpMesh)->ConstructLinearMesh(slab_nodes_x);
00136
00137
00138
00139
00140
00141
00142 double mesh_scale_factor = inter_node_space;
00143 static_cast<TetrahedralMesh<SPACE_DIM, SPACE_DIM>*>(mpMesh)->Scale(mesh_scale_factor, 1, 1);
00144 break;
00145 }
00146 case 2:
00147 {
00148 c_vector<double, 2> sheet_dimensions;
00149 HeartConfig::Instance()->GetSheetDimensions(sheet_dimensions);
00150 double inter_node_space = HeartConfig::Instance()->GetInterNodeSpace();
00151
00152 mpMesh = new TetrahedralMesh<SPACE_DIM, SPACE_DIM>();
00153
00154 unsigned slab_nodes_x = (unsigned)round(sheet_dimensions[0]/inter_node_space);
00155 unsigned slab_nodes_y = (unsigned)round(sheet_dimensions[1]/inter_node_space);
00156
00157 static_cast<TetrahedralMesh<SPACE_DIM, SPACE_DIM>*>(mpMesh)->ConstructRectangularMesh(slab_nodes_x, slab_nodes_y);
00158
00159
00160
00161
00162
00163
00164
00165 double mesh_scale_factor = inter_node_space;
00166 static_cast<TetrahedralMesh<SPACE_DIM, SPACE_DIM>*>(mpMesh)->Scale(mesh_scale_factor, mesh_scale_factor, mesh_scale_factor);
00167 break;
00168 }
00169 case 3:
00170 {
00171 c_vector<double, 3> slab_dimensions;
00172 HeartConfig::Instance()->GetSlabDimensions(slab_dimensions);
00173 double inter_node_space = HeartConfig::Instance()->GetInterNodeSpace();
00174
00175 mpMesh = new TetrahedralMesh<SPACE_DIM, SPACE_DIM>();
00176
00177 unsigned slab_nodes_x = (unsigned)round(slab_dimensions[0]/inter_node_space);
00178 unsigned slab_nodes_y = (unsigned)round(slab_dimensions[1]/inter_node_space);
00179 unsigned slab_nodes_z = (unsigned)round(slab_dimensions[2]/inter_node_space);
00180
00181 static_cast<TetrahedralMesh<SPACE_DIM, SPACE_DIM>*>(mpMesh)->ConstructCuboid(slab_nodes_x,
00182 slab_nodes_y,
00183 slab_nodes_z,
00184 true);
00185
00186
00187
00188
00189
00190
00191 double mesh_scale_factor = inter_node_space;
00192 static_cast<TetrahedralMesh<SPACE_DIM, SPACE_DIM>*>(mpMesh)->Scale(mesh_scale_factor, mesh_scale_factor, mesh_scale_factor);
00193 break;
00194 }
00195 default:
00196 NEVER_REACHED;
00197 }
00198 }
00199 else
00200 {
00201 NEVER_REACHED;
00202 }
00203 }
00204
00205 mAllocatedMemoryForMesh = true;
00206 }
00207 catch (Exception& e)
00208 {
00209 EXCEPTION(std::string("No mesh given: define it in XML parameters file or call SetMesh()\n") + e.GetMessage());
00210 }
00211 }
00212 mpCellFactory->SetMesh( mpMesh );
00213
00214 if (mNodesPerProcessorFilename != "")
00215 {
00216 mpMesh->ReadNodesPerProcessorFile(mNodesPerProcessorFilename);
00217 }
00219 delete mpCardiacPde;
00220 mpCardiacPde = CreateCardiacPde();
00221 }
00222
00223 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00224 void AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::SetNodesPerProcessorFilename(const std::string& filename)
00225 {
00226 mNodesPerProcessorFilename = filename;
00227 }
00228
00229 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00230 void AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::SetBoundaryConditionsContainer(BoundaryConditionsContainer<SPACE_DIM, SPACE_DIM, PROBLEM_DIM> *bcc)
00231 {
00232 this->mpBoundaryConditionsContainer = bcc;
00233 }
00234
00235 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00236 void AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::PreSolveChecks()
00237 {
00238 if ( mpCardiacPde == NULL )
00239 {
00240 EXCEPTION("Pde is null, Initialise() probably hasn't been called");
00241 }
00242 if ( HeartConfig::Instance()->GetSimulationDuration() <= 0.0)
00243 {
00244 EXCEPTION("End time should be greater than 0");
00245 }
00246 if (mPrintOutput==true)
00247 {
00248 if( (mOutputDirectory=="") || (mOutputFilenamePrefix==""))
00249 {
00250 EXCEPTION("Either explicitly specify not to print output (call PrintOutput(false)) or specify the output directory and filename prefix");
00251 }
00252 }
00253 }
00254
00255 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00256 Vec AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::CreateInitialCondition()
00257 {
00258
00259
00260
00261
00262 Vec initial_condition = DistributedVector::CreateVec(PROBLEM_DIM);
00263 DistributedVector ic(initial_condition);
00264 std::vector<DistributedVector::Stripe> stripe;
00265 stripe.reserve(PROBLEM_DIM);
00266
00267 for (unsigned i=0; i<PROBLEM_DIM; i++)
00268 {
00269 stripe.push_back(DistributedVector::Stripe(ic, i));
00270 }
00271
00272 for (DistributedVector::Iterator index = DistributedVector::Begin();
00273 index != DistributedVector::End();
00274 ++index)
00275 {
00276 stripe[0][index] = mpCardiacPde->GetCardiacCell(index.Global)->GetVoltage();
00277 if (PROBLEM_DIM==2)
00278 {
00279 stripe[1][index] = 0;
00280 }
00281 }
00282
00283 ic.Restore();
00284
00285 return initial_condition;
00286 }
00287
00288 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00289 void AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::ConvertOutputToMeshalyzerFormat(bool call)
00290 {
00291 mCallChaste2Meshalyzer=call;
00292 }
00293
00294 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00295 void AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::SetMesh(AbstractMesh<SPACE_DIM,SPACE_DIM>* pMesh)
00296 {
00297
00298
00299 assert(mpMesh==NULL);
00300 assert(pMesh!=NULL);
00301 mAllocatedMemoryForMesh = false;
00302 mpMesh = pMesh;
00303 }
00304
00305 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00306 void AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::PrintOutput(bool printOutput)
00307 {
00308 mPrintOutput = printOutput;
00309 }
00310
00311 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00312 void AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::SetWriteInfo(bool writeInfo)
00313 {
00314 mWriteInfo = writeInfo;
00315 }
00316
00317 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00318 Vec AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::GetSolution()
00319 {
00320 return mSolution;
00321 }
00322
00323 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00324 AbstractMesh<SPACE_DIM,SPACE_DIM> & AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::rGetMesh()
00325 {
00326 assert (mpMesh);
00327 return *mpMesh;
00328 }
00329
00330 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00331 AbstractCardiacPde<SPACE_DIM>* AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::GetPde()
00332 {
00333 return mpCardiacPde;
00334 }
00335
00336 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00337 void AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::Solve()
00338 {
00339 PreSolveChecks();
00340
00341 if(mpBoundaryConditionsContainer == NULL)
00342 {
00343
00344 mpDefaultBoundaryConditionsContainer = new BoundaryConditionsContainer<SPACE_DIM, SPACE_DIM, PROBLEM_DIM>;
00345 for (unsigned problem_index=0; problem_index<PROBLEM_DIM; problem_index++)
00346 {
00347 mpDefaultBoundaryConditionsContainer->DefineZeroNeumannOnMeshBoundary(mpMesh, problem_index);
00348 }
00349 mpBoundaryConditionsContainer = mpDefaultBoundaryConditionsContainer;
00350 }
00351
00352 mpAssembler = CreateAssembler();
00353 Vec initial_condition = CreateInitialCondition();
00354
00355 TimeStepper stepper(0.0, HeartConfig::Instance()->GetSimulationDuration(),
00356 HeartConfig::Instance()->GetPrintingTimeStep());
00357
00358 std::string progress_reporter_dir;
00359
00360 if (mPrintOutput)
00361 {
00362 HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT);
00363 InitialiseWriter();
00364 WriteOneStep(stepper.GetTime(), initial_condition);
00365 HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT);
00366
00367 progress_reporter_dir = mOutputDirectory;
00368 }
00369 else
00370 {
00371 progress_reporter_dir = "";
00372 }
00373
00374
00375
00376
00377 ProgressReporter progress_reporter(progress_reporter_dir, 0.0, HeartConfig::Instance()->GetSimulationDuration());
00378 progress_reporter.Update(0);
00379
00380
00381 if (mSolution)
00382 {
00383 VecDestroy(mSolution);
00384 }
00385
00386 while ( !stepper.IsTimeAtEnd() )
00387 {
00388
00389 mpAssembler->SetTimes(stepper.GetTime(), stepper.GetNextTime(), HeartConfig::Instance()->GetPdeTimeStep());
00390 mpAssembler->SetInitialCondition( initial_condition );
00391
00392 try
00393 {
00394 mSolution = mpAssembler->Solve();
00395 }
00396 catch (Exception &e)
00397 {
00398
00399 delete mpAssembler;
00400
00401
00402 PetscTools::ReplicateException(true);
00403
00404 HeartEventHandler::Reset();
00405
00406 CloseFilesAndPostProcess();
00407 throw e;
00408 }
00409 PetscTools::ReplicateException(false);
00410
00411
00412 VecDestroy(initial_condition);
00413
00414
00415 initial_condition = mSolution;
00416
00417
00418 stepper.AdvanceOneTimeStep();
00419
00420 if (mPrintOutput)
00421 {
00422
00423 if (mWriteInfo)
00424 {
00425 WriteInfo(stepper.GetTime());
00426 }
00427
00428
00429 HeartEventHandler::BeginEvent(HeartEventHandler::WRITE_OUTPUT);
00430 mpWriter->AdvanceAlongUnlimitedDimension();
00431 WriteOneStep(stepper.GetTime(), mSolution);
00432 HeartEventHandler::EndEvent(HeartEventHandler::WRITE_OUTPUT);
00433 }
00434
00435 progress_reporter.Update(stepper.GetTime());
00436
00437 OnEndOfTimestep(stepper.GetTime());
00438 }
00439
00440
00441 delete mpAssembler;
00442
00443
00444 progress_reporter.PrintFinalising();
00445 CloseFilesAndPostProcess();
00446 HeartEventHandler::EndEvent(HeartEventHandler::EVERYTHING);
00447 }
00448
00449 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00450 void AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::CloseFilesAndPostProcess()
00451 {
00452
00453 if (!mPrintOutput)
00454 {
00455
00456 return;
00457 }
00458 mpWriter->Close();
00459 delete mpWriter;
00460
00461 HeartEventHandler::BeginEvent(HeartEventHandler::USER2);
00462
00463 if (mCallChaste2Meshalyzer && mNodesToOutput.empty())
00464 {
00465
00466 std::string output_directory = mOutputDirectory + "/output";
00467 Hdf5ToMeshalyzerConverter converter(mOutputDirectory, output_directory, mOutputFilenamePrefix);
00468
00469
00470 if (PetscTools::AmMaster())
00471 {
00472
00473 MeshalyzerMeshWriter<SPACE_DIM,SPACE_DIM> mesh_writer(output_directory, mOutputFilenamePrefix+"_mesh", false);
00474
00475 try
00476 {
00477
00478 TrianglesMeshReader<SPACE_DIM,SPACE_DIM> mesh_reader(mpMesh->GetMeshFileBaseName());
00479 mesh_writer.WriteFilesUsingMeshReader(mesh_reader, mpMesh->rGetNodePermutation());
00480 }
00481 catch(Exception& e)
00482 {
00483
00485 mesh_writer.WriteFilesUsingMesh(*mpMesh);
00486 }
00487
00488
00489 HeartConfig::Instance()->Write(output_directory, mOutputFilenamePrefix+"_parameters.xml");
00490 }
00491 }
00492 HeartEventHandler::EndEvent(HeartEventHandler::USER2);
00493 }
00494
00495 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00496 void AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::DefineWriterColumns()
00497 {
00498 if ( mNodesToOutput.empty() )
00499 {
00500
00501 mpWriter->DefineFixedDimension( mpMesh->GetNumNodes() );
00502 }
00503 else
00504 {
00505
00506 mpWriter->DefineFixedDimension( mNodesToOutput, mpMesh->GetNumNodes() );
00507 }
00508
00509 mVoltageColumnId = mpWriter->DefineVariable("V","mV");
00510
00511 mpWriter->DefineUnlimitedDimension("Time","msecs");
00512
00513 }
00514
00515 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00516 void AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::WriteOneStep(double time, Vec voltageVec)
00517 {
00518 mpWriter->PutUnlimitedVariable(time);
00519
00520
00521 mpWriter->PutVector(mVoltageColumnId, voltageVec);
00522 }
00523
00524 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00525 void AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::InitialiseWriter()
00526 {
00527 mpWriter = new Hdf5DataWriter(mOutputDirectory,mOutputFilenamePrefix);
00528 DefineWriterColumns();
00529 mpWriter->EndDefineMode();
00530 }
00531
00532 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00533 void AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::SetOutputNodes(std::vector<unsigned> &nodesToOutput)
00534 {
00535 mNodesToOutput = nodesToOutput;
00536 }
00537
00538 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00539 Hdf5DataReader AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::GetDataReader()
00540 {
00541 if( (mOutputDirectory=="") || (mOutputFilenamePrefix==""))
00542 {
00543 EXCEPTION("Data reader invalid as data writer cannot be initialised");
00544 }
00545 return Hdf5DataReader(mOutputDirectory, mOutputFilenamePrefix);
00546 }
00547
00548 template<unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00549 void AbstractCardiacProblem<SPACE_DIM,PROBLEM_DIM>::UseMatrixBasedRhsAssembly(bool usematrix)
00550 {
00551 mUseMatrixBasedRhsAssembly = usematrix;
00552 }
00553
00554
00556
00558
00559
00560 template class AbstractCardiacProblem<1,1>;
00561 template class AbstractCardiacProblem<2,1>;
00562 template class AbstractCardiacProblem<3,1>;
00563
00564
00565 template class AbstractCardiacProblem<1,2>;
00566 template class AbstractCardiacProblem<2,2>;
00567 template class AbstractCardiacProblem<3,2>;