36 #include "CardiacElectroMechanicsProblem.hpp"
38 #include "OutputFileHandler.hpp"
39 #include "ReplicatableVector.hpp"
40 #include "HeartConfig.hpp"
41 #include "LogFile.hpp"
42 #include "ChastePoint.hpp"
43 #include "Element.hpp"
44 #include "BoundaryConditionsContainer.hpp"
45 #include "AbstractDynamicLinearPdeSolver.hpp"
46 #include "TimeStepper.hpp"
47 #include "TrianglesMeshWriter.hpp"
48 #include "Hdf5ToMeshalyzerConverter.hpp"
49 #include "Hdf5ToCmguiConverter.hpp"
50 #include "MeshalyzerMeshWriter.hpp"
52 #include "ImplicitCardiacMechanicsSolver.hpp"
53 #include "ExplicitCardiacMechanicsSolver.hpp"
54 #include "CmguiDeformedSolutionsWriter.hpp"
55 #include "VoltageInterpolaterOntoMechanicsMesh.hpp"
56 #include "BidomainProblem.hpp"
59 template<
unsigned DIM,
unsigned ELEC_PROB_DIM>
62 assert(mIsWatchedLocation);
65 double min_dist = DBL_MAX;
67 for (
unsigned i=0; i<mpElectricsMesh->GetNumNodes(); i++)
69 double dist = norm_2(mWatchedLocation - mpElectricsMesh->GetNode(i)->rGetLocation());
79 c_vector<double,DIM> pos = mpElectricsMesh->GetNode(node_index)->rGetLocation();
84 std::cout <<
"ERROR: Could not find an electrics node very close to requested watched location - "
85 <<
"min distance was " << min_dist <<
" for node " << node_index
86 <<
" at location " << pos << std::flush;;
95 LOG(2,
"Chosen electrics node "<<node_index<<
" at location " << pos <<
" to be watched");
96 mWatchedElectricsNodeIndex = node_index;
102 c_vector<double,DIM> pos_at_min;
104 for (
unsigned i=0; i<mpMechanicsMesh->GetNumNodes(); i++)
106 c_vector<double,DIM> position = mpMechanicsMesh->GetNode(i)->rGetLocation();
108 double dist = norm_2(position-mWatchedLocation);
114 pos_at_min = position;
124 std::cout <<
"ERROR: Could not find a mechanics node very close to requested watched location - "
125 <<
"min distance was " << min_dist <<
" for node " << node_index
126 <<
" at location " << pos_at_min;
135 LOG(2,
"Chosen mechanics node "<<node_index<<
" at location " << pos <<
" to be watched");
136 mWatchedMechanicsNodeIndex = node_index;
143 template<
unsigned DIM,
unsigned ELEC_PROB_DIM>
146 assert(mIsWatchedLocation);
148 std::vector<c_vector<double,DIM> >& deformed_position = mpMechanicsSolver->rGetDeformedPosition();
152 double V = voltage_replicated[mWatchedElectricsNodeIndex];
159 *mpWatchedLocationFile << time <<
" ";
160 for (
unsigned i=0; i<DIM; i++)
162 *mpWatchedLocationFile << deformed_position[mWatchedMechanicsNodeIndex](i) <<
" ";
164 *mpWatchedLocationFile << V <<
"\n";
165 mpWatchedLocationFile->flush();
168 template<
unsigned DIM,
unsigned ELEC_PROB_DIM>
173 unsigned containing_mechanics_elem = mpMeshPair->rGetCoarseElementsForFineElementCentroids()[elementIndex];
174 c_matrix<double,DIM,DIM>& r_deformation_gradient = mDeformationGradientsForEachMechanicsElement[containing_mechanics_elem];
177 c_matrix<double,DIM,DIM> inv_F =
Inverse(r_deformation_gradient);
178 mModifiedConductivityTensor = prod(inv_F, rOriginalConductivity);
179 mModifiedConductivityTensor = prod(mModifiedConductivityTensor, trans(inv_F));
181 return mModifiedConductivityTensor;
188 template<
unsigned DIM,
unsigned PROBLEM_DIM>
205 template<
unsigned DIM>
218 if (problemType == MONODOMAIN)
222 EXCEPTION(
"The second template parameter should be 2 when a bidomain problem is chosen");
229 template<
unsigned DIM>
242 if (problemType == BIDOMAIN)
246 if (problemType == BIDOMAIN_WITH_BATH)
250 EXCEPTION(
"The second template parameter should be 1 when a monodomain problem is chosen");
255 template<
unsigned DIM,
unsigned ELEC_PROB_DIM>
257 CompressibilityType compressibilityType,
258 ElectricsProblemType electricsProblemType,
263 std::string outputDirectory)
264 : mCompressibilityType(compressibilityType),
265 mpCardiacMechSolver(NULL),
266 mpMechanicsSolver(NULL),
267 mpElectricsMesh(pElectricsMesh),
268 mpMechanicsMesh(pMechanicsMesh),
269 mpProblemDefinition(pProblemDefinition),
272 mNoElectricsOutput(false),
273 mIsWatchedLocation(false),
276 mNumTimestepsToOutputDeformationGradientsAndStress(
UNSIGNED_UNSET)
300 assert(pCellFactory != NULL);
303 if (electricsProblemType == BIDOMAIN_WITH_BATH)
326 template<
unsigned DIM,
unsigned ELEC_PROB_DIM>
332 if (mIsWatchedLocation && mpMechanicsMesh)
334 mpWatchedLocationFile->close();
337 delete mpElectricsProblem;
338 delete mpCardiacMechSolver;
344 template<
unsigned DIM,
unsigned ELEC_PROB_DIM>
347 assert(mpElectricsMesh!=NULL);
348 assert(mpMechanicsMesh!=NULL);
349 assert(mpProblemDefinition!=NULL);
350 assert(mpCardiacMechSolver==NULL);
357 EXCEPTION(
"Electrics PDE timestep does not divide mechanics solve timestep");
362 std::string log_dir = mOutputDirectory;
365 LOG(2, DIM <<
"d Implicit CardiacElectroMechanics Simulation:");
366 LOG(2,
"End time = " <<
HeartConfig::Instance()->GetSimulationDuration() <<
", electrics time step = " <<
HeartConfig::Instance()->GetPdeTimeStep() <<
", mechanics timestep = " << mpProblemDefinition->GetMechanicsSolveTimestep() <<
"\n");
367 LOG(2,
"Contraction model ode timestep " << mpProblemDefinition->GetContractionModelOdeTimestep());
368 LOG(2,
"Output is written to " << mOutputDirectory <<
"/[deformation/electrics]");
370 LOG(2,
"Electrics mesh has " << mpElectricsMesh->GetNumNodes() <<
" nodes");
371 LOG(2,
"Mechanics mesh has " << mpMechanicsMesh->GetNumNodes() <<
" nodes");
373 LOG(2,
"Initialising..");
376 if (mIsWatchedLocation)
378 DetermineWatchedNodes();
382 mpElectricsProblem->SetMesh(mpElectricsMesh);
383 mpElectricsProblem->Initialise();
385 if (mCompressibilityType==INCOMPRESSIBLE)
387 switch(mpProblemDefinition->GetSolverType())
391 *mpMechanicsMesh,*mpProblemDefinition,mDeformationOutputDirectory);
395 *mpMechanicsMesh,*mpProblemDefinition,mDeformationOutputDirectory);
405 switch(mpProblemDefinition->GetSolverType())
409 *mpMechanicsMesh,*mpProblemDefinition,mDeformationOutputDirectory);
413 *mpMechanicsMesh,*mpProblemDefinition,mDeformationOutputDirectory);
422 assert(mpMechanicsSolver);
428 mpMeshPair->ComputeFineElementsAndWeightsForCoarseQuadPoints(*(mpCardiacMechSolver->GetQuadratureRule()),
false);
429 mpMeshPair->DeleteFineBoxCollection();
431 mpCardiacMechSolver->SetFineCoarseMeshPair(mpMeshPair);
432 mpCardiacMechSolver->Initialise();
434 unsigned num_quad_points = mpCardiacMechSolver->GetTotalNumQuadPoints();
435 mInterpolatedCalciumConcs.assign(num_quad_points, 0.0);
436 mInterpolatedVoltages.assign(num_quad_points, 0.0);
438 if (mpProblemDefinition->ReadFibreSheetDirectionsFromFile())
440 mpCardiacMechSolver->SetVariableFibreSheetDirections(mpProblemDefinition->GetFibreSheetDirectionsFile(),
441 mpProblemDefinition->GetFibreSheetDirectionsDefinedPerQuadraturePoint());
445 if (mpProblemDefinition->GetDeformationAffectsConductivity() || mpProblemDefinition->GetDeformationAffectsCellModels())
447 mpMeshPair->SetUpBoxesOnCoarseMesh();
451 if (mpProblemDefinition->GetDeformationAffectsCellModels() || mpProblemDefinition->GetDeformationAffectsConductivity())
454 mStretchesForEachMechanicsElement.resize(mpMechanicsMesh->GetNumElements(), 1.0);
457 mDeformationGradientsForEachMechanicsElement.resize(mpMechanicsMesh->GetNumElements(),identity_matrix<double>(DIM));
461 if (mpProblemDefinition->GetDeformationAffectsCellModels())
465 mpMeshPair->ComputeCoarseElementsForFineNodes(
false);
469 if (mpProblemDefinition->GetDeformationAffectsConductivity())
473 mpMeshPair->ComputeCoarseElementsForFineElementCentroids(
false);
477 mpElectricsProblem->GetTissue()->SetConductivityModifier(
this);
487 template<
unsigned DIM,
unsigned ELEC_PROB_DIM>
491 if (mpCardiacMechSolver==NULL)
496 bool verbose_during_solve = ( mpProblemDefinition->GetVerboseDuringSolve()
501 mpProblemDefinition->Validate();
504 p_bcc->DefineZeroNeumannOnMeshBoundary(mpElectricsMesh, 0);
505 mpElectricsProblem->SetBoundaryConditionsContainer(p_bcc);
510 = mpElectricsProblem->CreateSolver();
513 Vec electrics_solution=NULL;
514 Vec calcium_data= mpElectricsMesh->GetDistributedVectorFactory()->CreateVec();
515 Vec initial_voltage = mpElectricsProblem->CreateInitialCondition();
518 unsigned counter = 0;
524 std::vector<std::string> variable_names;
528 mpMechanicsSolver->SetWriteOutput();
529 mpMechanicsSolver->WriteCurrentSpatialSolution(
"undeformed",
"nodes");
533 *(this->mpMechanicsMesh),
534 WRITE_QUADRATIC_MESH);
535 variable_names.push_back(
"V");
536 if (ELEC_PROB_DIM==2)
538 variable_names.push_back(
"Phi_e");
541 std::vector<std::string> regions;
542 regions.push_back(
"tissue");
543 regions.push_back(
"bath");
544 p_cmgui_writer->SetRegionNames(regions);
547 p_cmgui_writer->SetAdditionalFieldNames(variable_names);
548 p_cmgui_writer->WriteInitialMesh(
"undeformed");
558 LOG(2,
"\nSolving for initial deformation");
560 if (verbose_during_solve)
562 std::cout <<
"\n\n ** Solving for initial deformation\n";
566 mpMechanicsSolver->SetWriteOutput(
false);
568 mpMechanicsSolver->SetCurrentTime(0.0);
571 mpMechanicsSolver->SetIncludeActiveTension(
false);
572 if (mNumTimestepsToOutputDeformationGradientsAndStress!=
UNSIGNED_UNSET)
574 mpMechanicsSolver->SetComputeAverageStressPerElementDuringSolve(
true);
577 unsigned total_newton_iters = 0;
578 for (
unsigned index=1; index<=mpProblemDefinition->GetNumIncrementsForInitialDeformation(); index++)
581 if (verbose_during_solve)
583 std::cout <<
" Increment " << index <<
" of " << mpProblemDefinition->GetNumIncrementsForInitialDeformation() <<
"\n";
587 if (mpProblemDefinition->GetTractionBoundaryConditionType()==PRESSURE_ON_DEFORMED)
589 mpProblemDefinition->SetPressureScaling(((
double)index)/mpProblemDefinition->GetNumIncrementsForInitialDeformation());
591 mpMechanicsSolver->Solve();
593 total_newton_iters += mpMechanicsSolver->GetNumNewtonIterations();
596 mpMechanicsSolver->SetIncludeActiveTension(
true);
598 LOG(2,
" Number of newton iterations = " << total_newton_iters);
601 unsigned mech_writer_counter = 0;
605 LOG(2,
" Writing output");
606 mpMechanicsSolver->SetWriteOutput();
607 mpMechanicsSolver->WriteCurrentSpatialSolution(
"solution",
"nodes",mech_writer_counter);
608 p_cmgui_writer->WriteDeformationPositions(rGetDeformedPosition(), mech_writer_counter);
610 if (!mNoElectricsOutput)
620 mpElectricsProblem->InitialiseWriter();
621 mpElectricsProblem->WriteOneStep(stepper.GetTime(), initial_voltage);
624 if (mIsWatchedLocation)
626 WriteWatchedLocationData(stepper.GetTime(), initial_voltage);
629 if (mNumTimestepsToOutputDeformationGradientsAndStress!=
UNSIGNED_UNSET)
631 mpMechanicsSolver->WriteCurrentStrains(DEFORMATION_GRADIENT_F,
"deformation_gradient",mech_writer_counter);
632 mpMechanicsSolver->WriteCurrentAverageElementStresses(
"second_PK",mech_writer_counter);
648 mpMechanicsSolver->SetComputeAverageStressPerElementDuringSolve(
false);
650 while (!stepper.IsTimeAtEnd())
652 LOG(2,
"\nCurrent time = " << stepper.GetTime());
654 if (verbose_during_solve)
657 std::cout <<
"\n\n ** Current time = " << stepper.GetTime() <<
"\n";
667 if (mpProblemDefinition->GetDeformationAffectsCellModels() || mpProblemDefinition->GetDeformationAffectsConductivity())
680 mpCardiacMechSolver->ComputeDeformationGradientAndStretchInEachElement(mDeformationGradientsForEachMechanicsElement, mStretchesForEachMechanicsElement);
683 if (mpProblemDefinition->GetDeformationAffectsCellModels())
686 for (
unsigned global_index = mpElectricsMesh->GetDistributedVectorFactory()->GetLow();
687 global_index < mpElectricsMesh->GetDistributedVectorFactory()->GetHigh();
690 unsigned containing_elem = mpMeshPair->rGetCoarseElementsForFineNodes()[global_index];
691 double stretch = mStretchesForEachMechanicsElement[containing_elem];
692 mpElectricsProblem->GetTissue()->GetCardiacCell(global_index)->SetStretch(stretch);
703 LOG(2,
" Solving electrics");
705 for (
unsigned i=0; i<mNumElecTimestepsPerMechTimestep; i++)
711 p_electrics_solver->
SetTimes(current_time, next_time);
714 electrics_solution = p_electrics_solver->
Solve();
716 PetscReal min_voltage, max_voltage;
717 VecMax(electrics_solution,PETSC_NULL,&max_voltage);
718 VecMin(electrics_solution,PETSC_NULL,&min_voltage);
721 LOG(2,
" minimum and maximum voltage is " << min_voltage <<
", "<<max_voltage);
729 initial_voltage = electrics_solution;
732 if (mpProblemDefinition->GetDeformationAffectsConductivity())
746 LOG(2,
" Interpolating Ca_I and voltage");
749 for (
unsigned node_index = 0; node_index<mpElectricsMesh->GetNumNodes(); node_index++)
751 if (mpElectricsMesh->GetDistributedVectorFactory()->IsGlobalIndexLocal(node_index))
753 double calcium_value = mpElectricsProblem->GetTissue()->GetCardiacCell(node_index)->GetIntracellularCalciumConcentration();
754 VecSetValue(calcium_data, node_index ,calcium_value, INSERT_VALUES);
764 for (
unsigned i=0; i<mpMeshPair->rGetElementsAndWeights().size(); i++)
766 double interpolated_CaI = 0;
767 double interpolated_voltage = 0;
769 Element<DIM,DIM>& element = *(mpElectricsMesh->GetElement(mpMeshPair->rGetElementsAndWeights()[i].ElementNum));
771 for (
unsigned node_index = 0; node_index<element.
GetNumNodes(); node_index++)
774 double CaI_at_node = calcium_repl[global_index];
775 interpolated_CaI += CaI_at_node*mpMeshPair->rGetElementsAndWeights()[i].Weights(node_index);
777 interpolated_voltage += electrics_solution_repl[global_index*ELEC_PROB_DIM]*mpMeshPair->rGetElementsAndWeights()[i].Weights(node_index);
780 assert(i<mInterpolatedCalciumConcs.size());
781 assert(i<mInterpolatedVoltages.size());
782 mInterpolatedCalciumConcs[i] = interpolated_CaI;
783 mInterpolatedVoltages[i] = interpolated_voltage;
786 LOG(2,
" Setting Ca_I. max value = " << Max(mInterpolatedCalciumConcs));
792 mpCardiacMechSolver->SetCalciumAndVoltage(mInterpolatedCalciumConcs, mInterpolatedVoltages);
801 LOG(2,
" Solving mechanics ");
802 mpMechanicsSolver->SetWriteOutput(
false);
806 mpMechanicsSolver->SetCurrentTime(stepper.GetTime());
809 if (mNumTimestepsToOutputDeformationGradientsAndStress!=
UNSIGNED_UNSET
810 && (counter+1)%mNumTimestepsToOutputDeformationGradientsAndStress == 0)
812 mpMechanicsSolver->SetComputeAverageStressPerElementDuringSolve(
true);
835 mpCardiacMechSolver->Solve(stepper.GetTime(), stepper.GetNextTime(), mpProblemDefinition->GetContractionModelOdeTimestep());
838 LOG(2,
" Number of newton iterations = " << mpMechanicsSolver->GetNumNewtonIterations());
841 stepper.AdvanceOneTimeStep();
851 if (mWriteOutput && (counter%WRITE_EVERY_NTH_TIME==0))
853 LOG(2,
" Writing output");
855 mech_writer_counter++;
856 mpMechanicsSolver->SetWriteOutput();
857 mpMechanicsSolver->WriteCurrentSpatialSolution(
"solution",
"nodes",mech_writer_counter);
859 p_cmgui_writer->WriteDeformationPositions(rGetDeformedPosition(), counter);
861 if (!mNoElectricsOutput)
863 mpElectricsProblem->mpWriter->AdvanceAlongUnlimitedDimension();
864 mpElectricsProblem->WriteOneStep(stepper.GetTime(), electrics_solution);
867 if (mIsWatchedLocation)
869 WriteWatchedLocationData(stepper.GetTime(), electrics_solution);
871 OnEndOfTimeStep(counter);
873 if (mNumTimestepsToOutputDeformationGradientsAndStress!=
UNSIGNED_UNSET && counter%mNumTimestepsToOutputDeformationGradientsAndStress==0)
875 mpMechanicsSolver->WriteCurrentStrains(DEFORMATION_GRADIENT_F,
"deformation_gradient",mech_writer_counter);
876 mpMechanicsSolver->WriteCurrentAverageElementStresses(
"second_PK",mech_writer_counter);
878 mpMechanicsSolver->SetComputeAverageStressPerElementDuringSolve(
false);
886 if ((mWriteOutput) && (!mNoElectricsOutput))
889 mpElectricsProblem->mpWriter->Close();
890 delete mpElectricsProblem->mpWriter;
892 std::string input_dir = mOutputDirectory+
"/electrics";
905 "voltage", mpElectricsMesh, mHasBath,
928 if (mNoElectricsOutput)
930 p_cmgui_writer->WriteCmguiScript(
"",
"undeformed");
934 p_cmgui_writer->WriteCmguiScript(
"../../electrics/cmgui_output/voltage_mechanics_mesh",
"undeformed");
936 delete p_cmgui_writer;
940 delete p_electrics_solver;
945 template<
unsigned DIM,
unsigned ELEC_PROB_DIM>
949 for (
unsigned i=0; i<vec.size(); i++)
951 if (vec[i]>max) max=vec[i];
956 template<
unsigned DIM,
unsigned ELEC_PROB_DIM>
959 mNoElectricsOutput =
true;
962 template<
unsigned DIM,
unsigned ELEC_PROB_DIM>
965 mIsWatchedLocation =
true;
966 mWatchedLocation = watchedLocation;
969 template<
unsigned DIM,
unsigned ELEC_PROB_DIM>
972 mNumTimestepsToOutputDeformationGradientsAndStress = (
unsigned) floor((timeStep/mpProblemDefinition->GetMechanicsSolveTimestep())+0.5);
973 if (fabs(mNumTimestepsToOutputDeformationGradientsAndStress*mpProblemDefinition->GetMechanicsSolveTimestep() - timeStep) > 1e-6)
975 EXCEPTION(
"Timestep provided for SetOutputDeformationGradientsAndStress() is not a multiple of mechanics solve timestep");
979 template<
unsigned DIM,
unsigned ELEC_PROB_DIM>
982 return mpMechanicsSolver->rGetDeformedPosition();
void Set(unsigned level, const std::string &rDirectory, const std::string &rFileName="log.txt")
std::string mDeformationOutputDirectory
void SetOutputDirectory(const std::string &rOutputDirectory)
void WriteWatchedLocationData(double time, Vec voltage)
boost::numeric::ublas::c_matrix< T, 1, 1 > Inverse(const boost::numeric::ublas::c_matrix< T, 1, 1 > &rM)
unsigned GetNodeGlobalIndex(unsigned localIndex) const
#define EXCEPTION(message)
double Max(std::vector< double > &vec)
void SetTimeStep(double dt)
static void BeginEvent(unsigned event)
void SetInitialCondition(Vec initialCondition)
static AbstractCardiacProblem< DIM, DIM, 2u > * Create(ElectricsProblemType problemType, AbstractCardiacCellFactory< DIM > *pCellFactory)
double GetPdeTimeStep() const
bool OptionExists(const std::string &rOption)
void SetTimes(double tStart, double tEnd)
std::vector< c_vector< double, DIM > > & rGetDeformedPosition()
out_stream OpenOutputFile(const std::string &rFileName, std::ios_base::openmode mode=std::ios::out|std::ios::trunc) const
CardiacElectroMechanicsProblem(CompressibilityType compressibilityType, ElectricsProblemType electricsProblemType, TetrahedralMesh< DIM, DIM > *pElectricsMesh, QuadraticMesh< DIM > *pMechanicsMesh, AbstractCardiacCellFactory< DIM > *pCellFactory, ElectroMechanicsProblemDefinition< DIM > *pProblemDefinition, std::string outputDirectory)
void SetNoElectricsOutput()
std::string mOutputDirectory
void DetermineWatchedNodes()
unsigned GetNumNodes() const
const unsigned UNSIGNED_UNSET
void SetOutputDeformationGradientsAndStress(double timestep)
static AbstractCardiacProblem< DIM, DIM, 1u > * Create(ElectricsProblemType problemType, AbstractCardiacCellFactory< DIM > *pCellFactory)
c_matrix< double, DIM, DIM > & rCalculateModifiedConductivityTensor(unsigned elementIndex, const c_matrix< double, DIM, DIM > &rOriginalConductivity, unsigned domainIndex)
void WriteHeader(std::string simulationType="")
virtual ~CardiacElectroMechanicsProblem()
AbstractCardiacProblem< DIM, DIM, ELEC_PROB_DIM > * mpElectricsProblem
void WriteElapsedTime(std::string pre="")
static CommandLineArguments * Instance()
void SetUpBoxesOnFineMesh(double boxWidth=-1)
std::string GetOutputDirectory() const
static LogFile * Instance()
static void EndEvent(unsigned event)
virtual void WriteFilesUsingMesh(AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > &rMesh, bool keepOriginalElementIndexing=true)
void SetMatrixIsNotAssembled()
void SetPrintingTimeStep(double printingTimeStep)
void SetOutputFilenamePrefix(const std::string &rOutputFilenamePrefix)
static HeartConfig * Instance()
static AbstractCardiacProblem< DIM, DIM, PROBLEM_DIM > * Create(ElectricsProblemType problemType, AbstractCardiacCellFactory< DIM > *pCellFactory)
void SetWatchedPosition(c_vector< double, DIM > watchedLocation)