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 "CardiacElectroMechanicsProblem.hpp"
00030
00031 #include "OutputFileHandler.hpp"
00032 #include "ReplicatableVector.hpp"
00033 #include "HeartConfig.hpp"
00034 #include "LogFile.hpp"
00035 #include "ChastePoint.hpp"
00036 #include "Element.hpp"
00037 #include "BoundaryConditionsContainer.hpp"
00038 #include "AbstractDynamicAssemblerMixin.hpp"
00039 #include "TimeStepper.hpp"
00040 #include "QuadraturePointsGroup.hpp"
00041 #include "TrianglesMeshWriter.hpp"
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052 template<unsigned DIM>
00053 void CardiacElectroMechanicsProblem<DIM>::DetermineWatchedNodes()
00054 {
00055 assert(mIsWatchedLocation);
00056
00057
00058 double min_dist = DBL_MAX;
00059 unsigned node_index = UNSIGNED_UNSET;
00060 for(unsigned i=0; i<mpElectricsMesh->GetNumNodes(); i++)
00061 {
00062 double dist = norm_2(mWatchedLocation - mpElectricsMesh->GetNode(i)->rGetLocation());
00063 if(dist < min_dist)
00064 {
00065 min_dist = dist;
00066 node_index = i;
00067 }
00068 }
00069
00070
00071 assert(node_index != UNSIGNED_UNSET);
00072 c_vector<double,DIM> pos = mpElectricsMesh->GetNode(node_index)->rGetLocation();
00073
00074 if(min_dist > 1e-8)
00075 {
00076 #define COVERAGE_IGNORE
00077 std::cout << "ERROR: Could not find an electrics node very close to requested watched location - "
00078 << "min distance was " << min_dist << " for node " << node_index
00079 << " at location " << pos << std::flush;;
00080
00082
00083 assert(0);
00084 #undef COVERAGE_IGNORE
00085 }
00086 else
00087 {
00088 LOG_AND_COUT(2,"Chosen electrics node "<<node_index<<" at location " << pos << " to be watched");
00089 mWatchedElectricsNodeIndex = node_index;
00090 }
00091
00092
00093 min_dist = DBL_MAX;
00094 node_index = UNSIGNED_UNSET;
00095 c_vector<double,DIM> pos_at_min;
00096
00097 for(unsigned i=0; i<mpMechanicsMesh->GetNumNodes(); i++)
00098 {
00099 c_vector<double,DIM> position = mpMechanicsMesh->GetNode(i)->rGetLocation();
00100
00101 double dist = norm_2(position-mWatchedLocation);
00102
00103 if(dist < min_dist)
00104 {
00105 min_dist = dist;
00106 node_index = i;
00107 pos_at_min = position;
00108 }
00109 }
00110
00111
00112 assert(node_index != UNSIGNED_UNSET);
00113
00114 if(min_dist > 1e-8)
00115 {
00116 #define COVERAGE_IGNORE
00117 std::cout << "ERROR: Could not find a mechanics node very close to requested watched location - "
00118 << "min distance was " << min_dist << " for node " << node_index
00119 << " at location " << pos_at_min;
00120
00122
00123 assert(0);
00124 #undef COVERAGE_IGNORE
00125 }
00126 else
00127 {
00128 LOG_AND_COUT(2,"Chosen electrics node "<<node_index<<" at location " << pos << " to be watched");
00129 mWatchedMechanicsNodeIndex = node_index;
00130 }
00131
00132 OutputFileHandler handler(mOutputDirectory);
00133 mpWatchedLocationFile = handler.OpenOutputFile("watched.txt");
00134 }
00135
00136 template<unsigned DIM>
00137 void CardiacElectroMechanicsProblem<DIM>::WriteWatchedLocationData(double time, Vec voltage)
00138 {
00139 assert(mIsWatchedLocation);
00140
00141 std::vector<c_vector<double,DIM> >& deformed_position = mpCardiacMechAssembler->rGetDeformedPosition();
00142
00144 ReplicatableVector voltage_replicated(voltage);
00145 double V=voltage_replicated[mWatchedElectricsNodeIndex];
00146
00148
00149 double Ca = mpMonodomainProblem->GetMonodomainPde()->GetCardiacCell(mWatchedElectricsNodeIndex)->rGetStateVariables()[3];
00150
00151 *mpWatchedLocationFile << time << " ";
00152 for(unsigned i=0; i<DIM; i++)
00153 {
00154 *mpWatchedLocationFile << deformed_position[mWatchedMechanicsNodeIndex](i) << " ";
00155 }
00156 *mpWatchedLocationFile << V << " " << Ca << "\n";
00157 mpWatchedLocationFile->flush();
00158 }
00159
00160
00161 template<unsigned DIM>
00162 CardiacElectroMechanicsProblem<DIM>::CardiacElectroMechanicsProblem(
00163 TetrahedralMesh<DIM,DIM>* pElectricsMesh,
00164 QuadraticMesh<DIM>* pMechanicsMesh,
00165 std::vector<unsigned> fixedMechanicsNodes,
00166 AbstractCardiacCellFactory<DIM>* pCellFactory,
00167 double endTime,
00168 unsigned numElecTimeStepsPerMechTimestep,
00169 double nhsOdeTimeStep,
00170 std::string outputDirectory = "")
00171 {
00172
00173 MechanicsEventHandler::Reset();
00174 MechanicsEventHandler::BeginEvent(MechanicsEventHandler::ALL);
00175
00176
00177
00178
00179 HeartEventHandler::Disable();
00180
00181
00182
00183
00184 assert(pCellFactory != NULL);
00185 mpMonodomainProblem = new MonodomainProblem<DIM>(pCellFactory);
00186
00187
00188 assert(endTime > 0);
00189 mEndTime = endTime;
00190 mElectricsTimeStep = 0.01;
00191 assert(numElecTimeStepsPerMechTimestep>0);
00192
00193 mNumElecTimestepsPerMechTimestep = numElecTimeStepsPerMechTimestep;
00194
00195 mMechanicsTimeStep = mElectricsTimeStep*mNumElecTimestepsPerMechTimestep;
00196 assert(nhsOdeTimeStep <= mMechanicsTimeStep+1e-14);
00197 mNhsOdeTimeStep = nhsOdeTimeStep;
00198
00199
00200 mWriteOutput = (outputDirectory!="");
00201 if(mWriteOutput)
00202 {
00203 mOutputDirectory = outputDirectory;
00204 mDeformationOutputDirectory = mOutputDirectory + "/deformation";
00205 HeartConfig::Instance()->SetOutputDirectory(mOutputDirectory + "/electrics");
00206 HeartConfig::Instance()->SetOutputFilenamePrefix("voltage");
00207 }
00208 else
00209 {
00210 mDeformationOutputDirectory = "";
00211 }
00212 mNoElectricsOutput = false;
00213
00214
00215 mpElectricsMesh = pElectricsMesh;
00216 mpMechanicsMesh = pMechanicsMesh;
00217 mFixedNodes = fixedMechanicsNodes;
00218
00219 mpCardiacMechAssembler = NULL;
00220
00221
00222
00223 std::string log_dir = mOutputDirectory;
00224 LogFile::Instance()->Set(2, mOutputDirectory);
00225 LogFile::Instance()->WriteHeader("Electromechanics");
00226 LOG(2, DIM << "d Implicit CardiacElectroMechanics Simulation:");
00227 LOG(2, "End time = " << mEndTime << ", electrics time step = " << mElectricsTimeStep << ", mechanics timestep = " << mMechanicsTimeStep << "\n");
00228 LOG(2, "Nhs ode timestep " << mNhsOdeTimeStep);
00229 LOG(2, "Output is written to " << mOutputDirectory << "/[deformation/electrics]");
00230 #define COVERAGE_IGNORE
00231
00232 if(mpElectricsMesh != NULL)
00233 {
00234 LOG(2, "Electrics mesh has " << mpElectricsMesh->GetNumNodes() << " nodes");
00235 }
00236 if(mpMechanicsMesh != NULL)
00237 {
00238 LOG(2, "Mechanics mesh has " << mpMechanicsMesh->GetNumNodes() << " nodes");
00239 }
00240 #undef COVERAGE_IGNORE
00241 mIsWatchedLocation = false;
00242 mWatchedElectricsNodeIndex = UNSIGNED_UNSET;
00243 mWatchedMechanicsNodeIndex = UNSIGNED_UNSET;
00244 }
00245
00246 template<unsigned DIM>
00247 CardiacElectroMechanicsProblem<DIM>::~CardiacElectroMechanicsProblem()
00248 {
00249
00250
00251
00252
00253 if(mIsWatchedLocation && mpMechanicsMesh)
00254 {
00255 mpWatchedLocationFile->close();
00256 }
00257
00258 delete mpMonodomainProblem;
00259 delete mpCardiacMechAssembler;
00260
00261 LogFile::Close();
00262 }
00263
00264 template<unsigned DIM>
00265 void CardiacElectroMechanicsProblem<DIM>::Initialise()
00266 {
00267 LOG(2, "Initialising meshes and cardiac mechanics assembler..");
00268
00269 assert(mpElectricsMesh!=NULL);
00270 assert(mpMechanicsMesh!=NULL);
00271 assert(mpCardiacMechAssembler==NULL);
00272
00273 if(mIsWatchedLocation)
00274 {
00275 DetermineWatchedNodes();
00276 }
00277
00278
00279 mpMonodomainProblem->SetMesh(mpElectricsMesh);
00280
00281 HeartConfig::Instance()->SetIntracellularConductivities(Create_c_vector(1.75,1.75,1.75));
00282
00283 mpMonodomainProblem->Initialise();
00284
00285
00286 mpCardiacMechAssembler = new ImplicitCardiacMechanicsAssembler<DIM>(mpMechanicsMesh,mDeformationOutputDirectory,mFixedNodes);
00287
00288
00289 mElementAndWeightsForQuadPoints.resize(mpCardiacMechAssembler->GetTotalNumQuadPoints());
00290
00291
00292 QuadraturePointsGroup<DIM> quad_point_posns(*mpMechanicsMesh, *(mpCardiacMechAssembler->GetQuadratureRule()));
00293
00294
00295
00296 for(unsigned i=0; i<quad_point_posns.Size(); i++)
00297 {
00298 ChastePoint<DIM> point;
00299
00300 for(unsigned j=0;j<DIM;j++)
00301 {
00302 point.rGetLocation()[j]=quad_point_posns.Get(i)[j];
00303 }
00304
00305 unsigned elem_index = mpElectricsMesh->GetContainingElementIndex(point);
00306 c_vector<double,DIM+1> weight = mpElectricsMesh->GetElement(elem_index)->CalculateInterpolationWeights(point);
00307
00308 mElementAndWeightsForQuadPoints[i].ElementNum = elem_index;
00309 mElementAndWeightsForQuadPoints[i].Weights = weight;
00310 }
00311
00312 if(mWriteOutput)
00313 {
00314 TrianglesMeshWriter<DIM,DIM> mesh_writer(mOutputDirectory,"electrics_mesh",false);
00315 mesh_writer.WriteFilesUsingMesh(*mpElectricsMesh);
00316 }
00317
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331 }
00332
00333 template<unsigned DIM>
00334 void CardiacElectroMechanicsProblem<DIM>::Solve()
00335 {
00336
00337 if(mpCardiacMechAssembler==NULL)
00338 {
00339 Initialise();
00340 }
00341
00342 BoundaryConditionsContainer<DIM,DIM,1> bcc;
00343 bcc.DefineZeroNeumannOnMeshBoundary(mpElectricsMesh, 0);
00344 mpMonodomainProblem->SetBoundaryConditionsContainer(&bcc);
00345
00346
00347
00348 AbstractDynamicAssemblerMixin<DIM,DIM,1>* p_electrics_assembler
00349 = mpMonodomainProblem->CreateAssembler();
00350
00351
00352 Vec voltage=NULL;
00353 Vec initial_voltage = mpMonodomainProblem->CreateInitialCondition();
00354
00355 unsigned num_quad_points = mpCardiacMechAssembler->GetTotalNumQuadPoints();
00356 std::vector<NhsCellularMechanicsOdeSystem> cellmech_systems;
00357 std::vector<double> intracellular_Ca(num_quad_points, 0.0);
00358
00359
00360 unsigned counter = 0;
00361
00362 TimeStepper stepper(0.0, mEndTime, mMechanicsTimeStep);
00363
00364 unsigned mech_writer_counter = 0;
00365 if (mWriteOutput)
00366 {
00367 mpCardiacMechAssembler->SetWriteOutput();
00368 mpCardiacMechAssembler->WriteOutput(mech_writer_counter);
00369
00370 if(!mNoElectricsOutput)
00371 {
00372 mpMonodomainProblem->InitialiseWriter();
00373 mpMonodomainProblem->WriteOneStep(stepper.GetTime(), initial_voltage);
00374 }
00375
00376 if(mIsWatchedLocation)
00377 {
00378 WriteWatchedLocationData(stepper.GetTime(), initial_voltage);
00379 }
00380 }
00381
00382 while (!stepper.IsTimeAtEnd())
00383 {
00384 LOG(2, "\nCurrent time = " << stepper.GetTime());
00385 std::cout << "\n\n ** Current time = " << stepper.GetTime();
00386
00387 LOG(2, " Solving electrics");
00388 MechanicsEventHandler::BeginEvent(MechanicsEventHandler::NON_MECH);
00389 for(unsigned i=0; i<mNumElecTimestepsPerMechTimestep; i++)
00390 {
00391 double current_time = stepper.GetTime() + i*mElectricsTimeStep;
00392 double next_time = stepper.GetTime() + (i+1)*mElectricsTimeStep;
00393
00394
00395 p_electrics_assembler->SetTimes(current_time, next_time, mElectricsTimeStep);
00396 p_electrics_assembler->SetInitialCondition( initial_voltage );
00397
00398 voltage = p_electrics_assembler->Solve();
00399
00400 PetscReal min_voltage, max_voltage;
00401 VecMax(voltage,PETSC_NULL,&max_voltage);
00402 VecMin(voltage,PETSC_NULL,&min_voltage);
00403 if(i==0)
00404 {
00405 LOG(2, " minimum and maximum voltage is " << min_voltage <<", "<<max_voltage);
00406 }
00407 else if(i==1)
00408 {
00409 LOG(2, " ..");
00410 }
00411
00412 VecDestroy(initial_voltage);
00413 initial_voltage = voltage;
00414 }
00415
00417
00418
00419
00420
00421 LOG(2, " Interpolating Ca_I");
00422 for(unsigned i=0; i<mElementAndWeightsForQuadPoints.size(); i++)
00423 {
00424 double interpolated_Ca_I = 0;
00425
00426 Element<DIM,DIM>& element = *(mpElectricsMesh->GetElement(mElementAndWeightsForQuadPoints[i].ElementNum));
00427 for(unsigned node_index = 0; node_index<element.GetNumNodes(); node_index++)
00428 {
00429 unsigned global_node_index = element.GetNodeGlobalIndex(node_index);
00430 double Ca_I_at_node = mpMonodomainProblem->GetPde()->GetCardiacCell(global_node_index)->GetIntracellularCalciumConcentration();
00431 interpolated_Ca_I += Ca_I_at_node*mElementAndWeightsForQuadPoints[i].Weights(node_index);
00432 }
00433
00434 intracellular_Ca[i] = interpolated_Ca_I;
00435 }
00436
00437 LOG(2, " Setting Ca_I. max value = " << Max(intracellular_Ca));
00438
00439
00440
00441
00442
00443 mpCardiacMechAssembler->SetIntracellularCalciumConcentrations(intracellular_Ca);
00444 MechanicsEventHandler::EndEvent(MechanicsEventHandler::NON_MECH);
00445
00446
00447
00448 LOG(2, " Solving mechanics ");
00449
00450 mpCardiacMechAssembler->SetWriteOutput(false);
00451
00452 MechanicsEventHandler::BeginEvent(MechanicsEventHandler::ALL_MECH);
00453 mpCardiacMechAssembler->Solve(stepper.GetTime(), stepper.GetNextTime(), mNhsOdeTimeStep);
00454 MechanicsEventHandler::EndEvent(MechanicsEventHandler::ALL_MECH);
00455
00456 LOG(2, " Number of newton iterations = " << mpCardiacMechAssembler->GetNumNewtonIterations());
00457
00458
00459 stepper.AdvanceOneTimeStep();
00460 counter++;
00461
00462
00463 MechanicsEventHandler::BeginEvent(MechanicsEventHandler::OUTPUT);
00464 if(mWriteOutput && (counter%WRITE_EVERY_NTH_TIME==0))
00465 {
00466 LOG(2, " Writing output");
00467
00468 mech_writer_counter++;
00469 mpCardiacMechAssembler->SetWriteOutput();
00470 mpCardiacMechAssembler->WriteOutput(mech_writer_counter);
00471
00472 if(!mNoElectricsOutput)
00473 {
00474 mpMonodomainProblem->mpWriter->AdvanceAlongUnlimitedDimension();
00475 mpMonodomainProblem->WriteOneStep(stepper.GetTime(), voltage);
00476 }
00477
00478 if(mIsWatchedLocation)
00479 {
00480 WriteWatchedLocationData(stepper.GetTime(), voltage);
00481 }
00482 }
00483 MechanicsEventHandler::EndEvent(MechanicsEventHandler::OUTPUT);
00484
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497 LogFile::Instance()->WriteElapsedTime(" ");
00498 }
00499
00500 if ((mWriteOutput) && (!mNoElectricsOutput))
00501 {
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518 HeartConfig::Instance()->Reset();
00519 mpMonodomainProblem->mpWriter->Close();
00520 delete mpMonodomainProblem->mpWriter;
00521 }
00522
00523 VecDestroy(voltage);
00524 delete p_electrics_assembler;
00525
00526 MechanicsEventHandler::EndEvent(MechanicsEventHandler::ALL);
00527 }
00528
00529
00530
00531 template<unsigned DIM>
00532 double CardiacElectroMechanicsProblem<DIM>::Max(std::vector<double>& vec)
00533 {
00534 double max = -1e200;
00535 for(unsigned i=0; i<vec.size(); i++)
00536 {
00537 if(vec[i]>max) max=vec[i];
00538 }
00539 return max;
00540 }
00541
00542 template<unsigned DIM>
00543 void CardiacElectroMechanicsProblem<DIM>::SetNoElectricsOutput()
00544 {
00545 mNoElectricsOutput = true;
00546 }
00547
00548 template<unsigned DIM>
00549 void CardiacElectroMechanicsProblem<DIM>::SetWatchedPosition(c_vector<double,DIM> watchedLocation)
00550 {
00551 mIsWatchedLocation = true;
00552 mWatchedLocation = watchedLocation;
00553 }
00554
00555 template<unsigned DIM>
00556 std::vector<c_vector<double,DIM> >& CardiacElectroMechanicsProblem<DIM>::rGetDeformedPosition()
00557 {
00558 return mpCardiacMechAssembler->rGetDeformedPosition();
00559 }
00560
00561
00563
00565
00566
00567 template class CardiacElectroMechanicsProblem<2>;
00568 template class CardiacElectroMechanicsProblem<3>;