Chaste Commit::6e4f5fe395bca70eb7641cf6e0e87f450383ca5a
CardiacElectroMechanicsProblem.cpp
1/*
2
3Copyright (c) 2005-2026, University of Oxford.
4All rights reserved.
5
6University of Oxford means the Chancellor, Masters and Scholars of the
7University of Oxford, having an administrative office at Wellington
8Square, Oxford OX1 2JD, UK.
9
10This file is part of Chaste.
11
12Redistribution and use in source and binary forms, with or without
13modification, are permitted provided that the following conditions are met:
14 * Redistributions of source code must retain the above copyright notice,
15 this list of conditions and the following disclaimer.
16 * Redistributions in binary form must reproduce the above copyright notice,
17 this list of conditions and the following disclaimer in the documentation
18 and/or other materials provided with the distribution.
19 * Neither the name of the University of Oxford nor the names of its
20 contributors may be used to endorse or promote products derived from this
21 software without specific prior written permission.
22
23THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34*/
35
36#include "CardiacElectroMechanicsProblem.hpp"
37
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"
51#include "PetscTools.hpp"
52#include "ImplicitCardiacMechanicsSolver.hpp"
53#include "ExplicitCardiacMechanicsSolver.hpp"
54#include "CmguiDeformedSolutionsWriter.hpp"
55#include "VoltageInterpolaterOntoMechanicsMesh.hpp"
56#include "BidomainProblem.hpp"
57
58
59template<unsigned DIM, unsigned ELEC_PROB_DIM>
61{
62 assert(mIsWatchedLocation);
63
64 // find the nearest electrics mesh node
65 double min_dist = DBL_MAX;
66 unsigned node_index = UNSIGNED_UNSET;
67 for (unsigned i=0; i<mpElectricsMesh->GetNumNodes(); i++)
68 {
69 double dist = norm_2(mWatchedLocation - mpElectricsMesh->GetNode(i)->rGetLocation());
70 if (dist < min_dist)
71 {
72 min_dist = dist;
73 node_index = i;
74 }
75 }
76
77 // set up watched node, if close enough
78 assert(node_index != UNSIGNED_UNSET); // should def have found something
79 c_vector<double,DIM> pos = mpElectricsMesh->GetNode(node_index)->rGetLocation();
80
81 if (min_dist > 1e-8)
82 {
83 // LCOV_EXCL_START
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;;
87
89 //EXCEPTION("Could not find an electrics node very close to requested watched location");
91 // LCOV_EXCL_STOP
92 }
93 else
94 {
95 LOG(2,"Chosen electrics node "<<node_index<<" at location " << pos << " to be watched");
96 mWatchedElectricsNodeIndex = node_index;
97 }
98
99 // find nearest mechanics mesh
100 min_dist = DBL_MAX;
101 node_index = UNSIGNED_UNSET;
102 c_vector<double,DIM> pos_at_min;
103
104 for (unsigned i=0; i<mpMechanicsMesh->GetNumNodes(); i++)
105 {
106 c_vector<double,DIM> position = mpMechanicsMesh->GetNode(i)->rGetLocation();
107
108 double dist = norm_2(position-mWatchedLocation);
109
110 if (dist < min_dist)
111 {
112 min_dist = dist;
113 node_index = i;
114 pos_at_min = position;
115 }
116 }
117
118 // set up watched node, if close enough
119 assert(node_index != UNSIGNED_UNSET); // should def have found something
120
121 if (min_dist > 1e-8)
122 {
123 // LCOV_EXCL_START
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;
127
129 //EXCEPTION("Could not find a mechanics node very close to requested watched location");
131 // LCOV_EXCL_STOP
132 }
133 else
134 {
135 LOG(2,"Chosen mechanics node "<<node_index<<" at location " << pos << " to be watched");
136 mWatchedMechanicsNodeIndex = node_index;
137 }
138
139 OutputFileHandler handler(mOutputDirectory,false);
140 mpWatchedLocationFile = handler.OpenOutputFile("watched.txt");
141}
142
143template<unsigned DIM, unsigned ELEC_PROB_DIM>
145{
146 assert(mIsWatchedLocation);
147
148 std::vector<c_vector<double,DIM> >& deformed_position = mpMechanicsSolver->rGetDeformedPosition();
149
151 ReplicatableVector voltage_replicated(voltage);
152 double V = voltage_replicated[mWatchedElectricsNodeIndex];
153
156 // // Metadata is currently being added to CellML models and then this will be avoided by asking for Calcium.
157 // double Ca = mpElectricsProblem->GetMonodomainTissue()->GetCardiacCell(mWatchedElectricsNodeIndex)->GetIntracellularCalciumConcentration();
158
159 *mpWatchedLocationFile << time << " ";
160 for (unsigned i=0; i<DIM; i++)
161 {
162 *mpWatchedLocationFile << deformed_position[mWatchedMechanicsNodeIndex](i) << " ";
163 }
164 *mpWatchedLocationFile << V << "\n";
165 mpWatchedLocationFile->flush();
166}
167
168template<unsigned DIM, unsigned ELEC_PROB_DIM>
169c_matrix<double,DIM,DIM>& CardiacElectroMechanicsProblem<DIM,ELEC_PROB_DIM>::rCalculateModifiedConductivityTensor(unsigned elementIndex, const c_matrix<double,DIM,DIM>& rOriginalConductivity, unsigned domainIndex)
170{
171
172 // first get the deformation gradient for this electrics element
173 unsigned containing_mechanics_elem = mpMeshPair->rGetCoarseElementsForFineElementCentroids()[elementIndex];
174 c_matrix<double,DIM,DIM>& r_deformation_gradient = mDeformationGradientsForEachMechanicsElement[containing_mechanics_elem];
175
176 // compute sigma_def = F^{-1} sigma_undef F^{-T}
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));
180
181 return mModifiedConductivityTensor;
182}
183
184
188template<unsigned DIM, unsigned PROBLEM_DIM>
190{
191public:
198 static AbstractCardiacProblem<DIM, DIM, PROBLEM_DIM>* Create(ElectricsProblemType problemType,
199 AbstractCardiacCellFactory<DIM>* pCellFactory);
200};
201
205template<unsigned DIM>
207{
208public:
215 static AbstractCardiacProblem<DIM, DIM, 1u>* Create(ElectricsProblemType problemType,
217 {
218 if (problemType == MONODOMAIN)
219 {
220 return new MonodomainProblem<DIM>(pCellFactory);
221 }
222 EXCEPTION("The second template parameter should be 2 when a bidomain problem is chosen");
223 }
224};
225
229template<unsigned DIM>
231{
232public:
239 static AbstractCardiacProblem<DIM, DIM, 2u>* Create(ElectricsProblemType problemType,
241 {
242 if (problemType == BIDOMAIN)
243 {
244 return new BidomainProblem<DIM>(pCellFactory, false);//false-> no bath
245 }
246 if (problemType == BIDOMAIN_WITH_BATH)
247 {
248 return new BidomainProblem<DIM>(pCellFactory, true);// true-> bath
249 }
250 EXCEPTION("The second template parameter should be 1 when a monodomain problem is chosen");
251 }
252};
253
254
255template<unsigned DIM, unsigned ELEC_PROB_DIM>
257 CompressibilityType compressibilityType,
258 ElectricsProblemType electricsProblemType,
259 TetrahedralMesh<DIM,DIM>* pElectricsMesh,
260 QuadraticMesh<DIM>* pMechanicsMesh,
262 ElectroMechanicsProblemDefinition<DIM>* pProblemDefinition,
263 std::string outputDirectory)
264 : mCompressibilityType(compressibilityType),
265 mpCardiacMechSolver(NULL),
266 mpMechanicsSolver(NULL),
267 mpElectricsMesh(pElectricsMesh),
268 mpMechanicsMesh(pMechanicsMesh),
269 mpProblemDefinition(pProblemDefinition),
270 mHasBath(false),
271 mpMeshPair(NULL),
272 mNoElectricsOutput(false),
273 mIsWatchedLocation(false),
274 mWatchedElectricsNodeIndex(UNSIGNED_UNSET),
275 mWatchedMechanicsNodeIndex(UNSIGNED_UNSET),
276 mNumTimestepsToOutputDeformationGradientsAndStress(UNSIGNED_UNSET),
277 mpCardiacVtkWriter(NULL)
278{
279 // Do some initial set up...
280 // However, NOTE, we don't use either the passed in meshes or the problem_definition.
281 // These pointers are allowed to be NULL, in case a child constructor wants to set
282 // them up (eg CardiacElectroMechProbRegularGeom).
283 // The meshes and problem_defn are used for the first time in Initialise().
284
285
286 // Start-up mechanics event handler..
288 MechanicsEventHandler::BeginEvent(MechanicsEventHandler::ALL);
289 // disable the electric event handler, because we use a problem class but
290 // don't call Solve, so we would have to worry about starting and ending any
291 // events in AbstractCardiacProblem::Solve() (esp. calling EndEvent(EVERYTHING))
292 // if we didn't disable it.
294
295 assert(HeartConfig::Instance()->GetSimulationDuration()>0.0);
296 assert(HeartConfig::Instance()->GetPdeTimeStep()>0.0);
297
298 // Create the monodomain problem.
299 // **NOTE** WE ONLY USE THIS TO: set up the cells, get an initial condition
300 // (voltage) vector, and get a solver. We won't ever call Solve on the cardiac problem class
301 assert(pCellFactory != NULL);
302 mpElectricsProblem = CreateElectricsProblem<DIM,ELEC_PROB_DIM>::Create(electricsProblemType, pCellFactory);
303
304 if (electricsProblemType == BIDOMAIN_WITH_BATH)
305 {
306 mHasBath = true;
307 }
308 // check whether output is required
309 mWriteOutput = (outputDirectory!="");
310 if (mWriteOutput)
311 {
312 mOutputDirectory = outputDirectory;
313 // create the directory
318 }
319 else
320 {
322 }
323
324// mpImpactRegion=NULL;
325}
326
327template<unsigned DIM, unsigned ELEC_PROB_DIM>
329{
330 // NOTE if SetWatchedLocation but not Initialise has been called, mpWatchedLocationFile
331 // will be uninitialised and using it will cause a seg fault. Hence the mpMechanicsMesh!=NULL
332 // it is true if Initialise has been called.
333 if (mIsWatchedLocation && mpMechanicsMesh)
334 {
335 mpWatchedLocationFile->close();
336 }
337
338 delete mpElectricsProblem;
339 delete mpCardiacMechSolver;
340 delete mpMeshPair;
341 delete mpCardiacVtkWriter;
342
344}
345
346template<unsigned DIM, unsigned ELEC_PROB_DIM>
348{
349 assert(mpElectricsMesh!=NULL);
350 assert(mpMechanicsMesh!=NULL);
351 assert(mpProblemDefinition!=NULL);
352 assert(mpCardiacMechSolver==NULL);
353
356 mNumElecTimestepsPerMechTimestep = (unsigned) floor((mpProblemDefinition->GetMechanicsSolveTimestep()/HeartConfig::Instance()->GetPdeTimeStep())+0.5);
357 if (fabs(mNumElecTimestepsPerMechTimestep*HeartConfig::Instance()->GetPdeTimeStep() - mpProblemDefinition->GetMechanicsSolveTimestep()) > 1e-6)
358 {
359 EXCEPTION("Electrics PDE timestep does not divide mechanics solve timestep");
360 }
361
362 // Create the Logfile (note we have to do this after the output dir has been
363 // created, else the log file might get cleaned away
364 std::string log_dir = mOutputDirectory; // just the TESTOUTPUT dir if mOutputDir="";
365 LogFile::Instance()->Set(2, mOutputDirectory);
366 LogFile::Instance()->WriteHeader("Electromechanics");
367 LOG(2, DIM << "d Implicit CardiacElectroMechanics Simulation:");
368 LOG(2, "End time = " << HeartConfig::Instance()->GetSimulationDuration() << ", electrics time step = " << HeartConfig::Instance()->GetPdeTimeStep() << ", mechanics timestep = " << mpProblemDefinition->GetMechanicsSolveTimestep() << "\n");
369 LOG(2, "Contraction model ode timestep " << mpProblemDefinition->GetContractionModelOdeTimestep());
370 LOG(2, "Output is written to " << mOutputDirectory << "/[deformation/electrics]");
371
372 LOG(2, "Electrics mesh has " << mpElectricsMesh->GetNumNodes() << " nodes");
373 LOG(2, "Mechanics mesh has " << mpMechanicsMesh->GetNumNodes() << " nodes");
374
375 LOG(2, "Initialising..");
376
377
378 if (mIsWatchedLocation)
379 {
380 DetermineWatchedNodes();
381 }
382
383 // initialise electrics problem
384 mpElectricsProblem->SetMesh(mpElectricsMesh);
385 mpElectricsProblem->Initialise();
386
387 if (mCompressibilityType==INCOMPRESSIBLE)
388 {
389 switch(mpProblemDefinition->GetSolverType())
390 {
391 case EXPLICIT:
393 *mpMechanicsMesh,*mpProblemDefinition,mDeformationOutputDirectory);
394 break;
395 case IMPLICIT:
397 *mpMechanicsMesh,*mpProblemDefinition,mDeformationOutputDirectory);
398 break;
399 default:
401 }
402 }
403 else
404 {
405 // repeat above with Compressible solver rather than incompressible -
406 // not the neatest but avoids having to template this class.
407 switch(mpProblemDefinition->GetSolverType())
408 {
409 case EXPLICIT:
411 *mpMechanicsMesh,*mpProblemDefinition,mDeformationOutputDirectory);
412 break;
413 case IMPLICIT:
415 *mpMechanicsMesh,*mpProblemDefinition,mDeformationOutputDirectory);
416 break;
417 default:
419 }
420 }
421
422
423 mpMechanicsSolver = dynamic_cast<AbstractNonlinearElasticitySolver<DIM>*>(mpCardiacMechSolver);
424 assert(mpMechanicsSolver);
425
426 // set up mesh pair and determine the fine mesh elements and corresponding weights for each
427 // quadrature point in the coarse mesh
428 mpMeshPair = new FineCoarseMeshPair<DIM>(*mpElectricsMesh, *mpMechanicsMesh);
429 mpMeshPair->SetUpBoxesOnFineMesh();
430 mpMeshPair->ComputeFineElementsAndWeightsForCoarseQuadPoints(*(mpCardiacMechSolver->GetQuadratureRule()), false);
431 mpMeshPair->DeleteFineBoxCollection();
432
433 mpCardiacMechSolver->SetFineCoarseMeshPair(mpMeshPair);
434 mpCardiacMechSolver->Initialise();
435
436 unsigned num_quad_points = mpCardiacMechSolver->GetTotalNumQuadPoints();
437 mInterpolatedCalciumConcs.assign(num_quad_points, 0.0);
438 mInterpolatedVoltages.assign(num_quad_points, 0.0);
439
440 if (mpProblemDefinition->ReadFibreSheetDirectionsFromFile())
441 {
442 mpCardiacMechSolver->SetVariableFibreSheetDirections(mpProblemDefinition->GetFibreSheetDirectionsFile(),
443 mpProblemDefinition->GetFibreSheetDirectionsDefinedPerQuadraturePoint());
444 }
445
446
447 if (mpProblemDefinition->GetDeformationAffectsConductivity() || mpProblemDefinition->GetDeformationAffectsCellModels())
448 {
449 mpMeshPair->SetUpBoxesOnCoarseMesh();
450 }
451
452
453 if (mpProblemDefinition->GetDeformationAffectsCellModels() || mpProblemDefinition->GetDeformationAffectsConductivity())
454 {
455 // initialise the stretches saved for each mechanics element
456 mStretchesForEachMechanicsElement.resize(mpMechanicsMesh->GetNumElements(), 1.0);
457
458 // initialise the store of the F in each mechanics element (one constant value of F) in each
459 mDeformationGradientsForEachMechanicsElement.resize(mpMechanicsMesh->GetNumElements(),identity_matrix<double>(DIM));
460 }
461
462
463 if (mpProblemDefinition->GetDeformationAffectsCellModels())
464 {
465 // compute the coarse elements which contain each fine node -- for transferring stretch from
466 // mechanics solve electrics cell models
467 mpMeshPair->ComputeCoarseElementsForFineNodes(false);
468
469 }
470
471 if (mpProblemDefinition->GetDeformationAffectsConductivity())
472 {
473 // compute the coarse elements which contain each fine element centroid -- for transferring F from
474 // mechanics solve to electrics mesh elements
475 mpMeshPair->ComputeCoarseElementsForFineElementCentroids(false);
476
477 // tell the abstract tissue class that the conductivities need to be modified, passing in this class
478 // (which is of type AbstractConductivityModifier)
479 mpElectricsProblem->GetTissue()->SetConductivityModifier(this);
480 }
481
482 if (mWriteOutput)
483 {
484 TrianglesMeshWriter<DIM,DIM> mesh_writer(mOutputDirectory,"electrics_mesh",false);
485 mesh_writer.WriteFilesUsingMesh(*mpElectricsMesh);
486 }
487}
488
489template<unsigned DIM, unsigned ELEC_PROB_DIM>
491{
492 // initialise the meshes and mechanics solver
493 if (mpCardiacMechSolver==NULL)
494 {
495 Initialise();
496 }
497
498 bool verbose_during_solve = ( mpProblemDefinition->GetVerboseDuringSolve()
499 || CommandLineArguments::Instance()->OptionExists("-mech_verbose")
500 || CommandLineArguments::Instance()->OptionExists("-mech_very_verbose"));
501
502
503 mpProblemDefinition->Validate();
504
505 boost::shared_ptr<BoundaryConditionsContainer<DIM,DIM,ELEC_PROB_DIM> > p_bcc(new BoundaryConditionsContainer<DIM,DIM,ELEC_PROB_DIM>);
506 p_bcc->DefineZeroNeumannOnMeshBoundary(mpElectricsMesh, 0);
507 mpElectricsProblem->SetBoundaryConditionsContainer(p_bcc);
508
509 // get an electrics solver from the problem. Note that we don't call
510 // Solve() on the CardiacProblem class, we do the looping here.
511 AbstractDynamicLinearPdeSolver<DIM,DIM,ELEC_PROB_DIM>* p_electrics_solver = mpElectricsProblem->CreateSolver();
512
513 // set up initial voltage etc
514 Vec electrics_solution=NULL; //This will be set and used later
515 Vec calcium_data= mpElectricsMesh->GetDistributedVectorFactory()->CreateVec();
516 Vec initial_voltage = mpElectricsProblem->CreateInitialCondition();
517
518 // write the initial position
519 unsigned counter = 0;
520
521 TimeStepper stepper(0.0, HeartConfig::Instance()->GetSimulationDuration(), mpProblemDefinition->GetMechanicsSolveTimestep());
522
523 CmguiDeformedSolutionsWriter<DIM>* p_cmgui_writer = NULL;
524
525 std::vector<std::string> variable_names;
526
527 if (mWriteOutput)
528 {
529#ifdef CHASTE_VTK
530 if (HeartConfig::Instance()->GetVisualizeWithVtk())
531 {
532 ReplicatableVector ic(initial_voltage);
533 mpCardiacVtkWriter = new CardiacElectroMechanicsVtkHandler<DIM,ELEC_PROB_DIM>(*mpMechanicsSolver,
534 *mpMechanicsMesh,*mpElectricsMesh, ic, mDeformationOutputDirectory);
535 }
536#endif
537 mpMechanicsSolver->SetWriteOutput();
538 mpMechanicsSolver->WriteCurrentSpatialSolution("undeformed","nodes");
539
540 p_cmgui_writer = new CmguiDeformedSolutionsWriter<DIM>(mOutputDirectory+"/deformation/cmgui",
541 "solution",
542 *(this->mpMechanicsMesh),
543 WRITE_QUADRATIC_MESH);
544
545 variable_names.push_back("V");
546 if (ELEC_PROB_DIM==2)
547 {
548 variable_names.push_back("Phi_e");
549 if (mHasBath==true)
550 {
551 std::vector<std::string> regions;
552 regions.push_back("tissue");
553 regions.push_back("bath");
554 p_cmgui_writer->SetRegionNames(regions);
555 }
556 }
557 p_cmgui_writer->SetAdditionalFieldNames(variable_names);
558 p_cmgui_writer->WriteInitialMesh("undeformed");
559 }
560
567
568 LOG(2, "\nSolving for initial deformation");
569 // LCOV_EXCL_START
570 if (verbose_during_solve)
571 {
572 std::cout << "\n\n ** Solving for initial deformation\n";
573 }
574 // LCOV_EXCL_STOP
575
576 mpMechanicsSolver->SetWriteOutput(false);
577
578 mpMechanicsSolver->SetCurrentTime(0.0);
579 MechanicsEventHandler::BeginEvent(MechanicsEventHandler::ALL_MECH);
580
581 mpMechanicsSolver->SetIncludeActiveTension(false);
582 if (mNumTimestepsToOutputDeformationGradientsAndStress!=UNSIGNED_UNSET)
583 {
584 mpMechanicsSolver->SetComputeAverageStressPerElementDuringSolve(true);
585 }
586
587 unsigned total_newton_iters = 0;
588 for (unsigned index=1; index<=mpProblemDefinition->GetNumIncrementsForInitialDeformation(); index++)
589 {
590 // LCOV_EXCL_START
591 if (verbose_during_solve)
592 {
593 std::cout << " Increment " << index << " of " << mpProblemDefinition->GetNumIncrementsForInitialDeformation() << "\n";
594 }
595 // LCOV_EXCL_STOP
596
597 if (mpProblemDefinition->GetTractionBoundaryConditionType()==PRESSURE_ON_DEFORMED)
598 {
599 mpProblemDefinition->SetPressureScaling(((double)index)/mpProblemDefinition->GetNumIncrementsForInitialDeformation());
600 }
601 mpMechanicsSolver->Solve();
602
603 total_newton_iters += mpMechanicsSolver->GetNumNewtonIterations();
604 }
605
606 mpMechanicsSolver->SetIncludeActiveTension(true);
607 MechanicsEventHandler::EndEvent(MechanicsEventHandler::ALL_MECH);
608 LOG(2, " Number of newton iterations = " << total_newton_iters);
609
610
611 unsigned mech_writer_counter = 0;
612
613 if (mWriteOutput)
614 {
615 LOG(2, " Writing output");
616 mpMechanicsSolver->SetWriteOutput();
617 mpMechanicsSolver->WriteCurrentSpatialSolution("solution","nodes",mech_writer_counter);
618 p_cmgui_writer->WriteDeformationPositions(rGetDeformedPosition(), mech_writer_counter);
619
620 if (!mNoElectricsOutput)
621 {
622 // the writer inside monodomain problem uses the printing timestep
623 // inside HeartConfig to estimate total number of timesteps, so make
624 // sure this is set to what we will use.
625 HeartConfig::Instance()->SetPrintingTimeStep(mpProblemDefinition->GetMechanicsSolveTimestep());
626
627 // When we consider archiving these simulations we will need to get a bool back from the following
628 // command to decide whether or not the file is being extended, and hence whether to write the
629 // initial conditions into the .h5 file.
630 mpElectricsProblem->InitialiseWriter();
631 mpElectricsProblem->WriteOneStep(stepper.GetTime(), initial_voltage);
632 }
633
634 if (mIsWatchedLocation)
635 {
636 WriteWatchedLocationData(stepper.GetTime(), initial_voltage);
637 }
638
639 if (mNumTimestepsToOutputDeformationGradientsAndStress!=UNSIGNED_UNSET)
640 {
641 mpMechanicsSolver->WriteCurrentStrains(DEFORMATION_GRADIENT_F,"deformation_gradient",mech_writer_counter);
642 mpMechanicsSolver->WriteCurrentAverageElementStresses("second_PK",mech_writer_counter);
643 }
644 }
645
646
647 PrepareForSolve();
648
652// std::vector<double> current_solution_previous_time_step = mpMechanicsSolver->rGetCurrentSolution();
653// std::vector<double> current_solution_second_last_time_step = mpMechanicsSolver->rGetCurrentSolution();
654// bool first_step = true;
655
656
657 // reset this to false, may be reset again below
658 mpMechanicsSolver->SetComputeAverageStressPerElementDuringSolve(false);
659
660 while (!stepper.IsTimeAtEnd())
661 {
662 LOG(2, "\nCurrent time = " << stepper.GetTime());
663 // LCOV_EXCL_START
664 if (verbose_during_solve)
665 {
666 // also output time to screen as newton solve information will be output
667 std::cout << "\n\n ** Current time = " << stepper.GetTime() << "\n";
668 }
669 // LCOV_EXCL_STOP
670
677 if (mpProblemDefinition->GetDeformationAffectsCellModels() || mpProblemDefinition->GetDeformationAffectsConductivity())
678 {
679 // Determine the stretch and deformation gradient on each element.
680 //
681 // If mpProblemDefinition->GetDeformationAffectsCellModels()==true:
682 // Stretch will be passed to the cell models.
683 //
684 // If mpProblemDefinition->GetDeformationAffectsConductivity()==true:
685 // The deformation gradient needs to be set up but does not need to be passed to the tissue
686 // so that F is used to compute the conductivity. Instead this is
687 // done through the line "mpElectricsProblem->GetMonodomainTissue()->SetConductivityModifier(this);" line above, which means
688 // rCalculateModifiedConductivityTensor() will be called on this class by the tissue, which then uses the F
689
690 mpCardiacMechSolver->ComputeDeformationGradientAndStretchInEachElement(mDeformationGradientsForEachMechanicsElement, mStretchesForEachMechanicsElement);
691 }
692
693 if (mpProblemDefinition->GetDeformationAffectsCellModels())
694 {
695 // Set the stretches on each of the cell models
696 for (unsigned global_index = mpElectricsMesh->GetDistributedVectorFactory()->GetLow();
697 global_index < mpElectricsMesh->GetDistributedVectorFactory()->GetHigh();
698 global_index++)
699 {
700 unsigned containing_elem = mpMeshPair->rGetCoarseElementsForFineNodes()[global_index];
701 double stretch = mStretchesForEachMechanicsElement[containing_elem];
702 mpElectricsProblem->GetTissue()->GetCardiacCell(global_index)->SetStretch(stretch);
703 }
704 }
705
706 p_electrics_solver->SetTimeStep(HeartConfig::Instance()->GetPdeTimeStep());
707
713 LOG(2, " Solving electrics");
714 MechanicsEventHandler::BeginEvent(MechanicsEventHandler::NON_MECH);
715 for (unsigned i=0; i<mNumElecTimestepsPerMechTimestep; i++)
716 {
717 double current_time = stepper.GetTime() + i*HeartConfig::Instance()->GetPdeTimeStep();
718 double next_time = stepper.GetTime() + (i+1)*HeartConfig::Instance()->GetPdeTimeStep();
719
720 // solve the electrics
721 p_electrics_solver->SetTimes(current_time, next_time);
722 p_electrics_solver->SetInitialCondition( initial_voltage );
723
724 electrics_solution = p_electrics_solver->Solve();
725
726 PetscReal min_voltage, max_voltage;
727 VecMax(electrics_solution,CHASTE_PETSC_NULLPTR,&max_voltage); //the second param is where the index would be returned
728 VecMin(electrics_solution,CHASTE_PETSC_NULLPTR,&min_voltage);
729 if (i==0)
730 {
731 LOG(2, " minimum and maximum voltage is " << min_voltage <<", "<<max_voltage);
732 }
733 else if (i==1)
734 {
735 LOG(2, " ..");
736 }
737
738 PetscTools::Destroy(initial_voltage);
739 initial_voltage = electrics_solution;
740 }
741
742 if (mpProblemDefinition->GetDeformationAffectsConductivity())
743 {
744 p_electrics_solver->SetMatrixIsNotAssembled();
745 }
746
747
753
754 // compute Ca_I at each quad point (by interpolation, using the info on which
755 // electrics element the quad point is in. Then set Ca_I on the mechanics solver
756 LOG(2, " Interpolating Ca_I and voltage");
757
758 //Collect the distributed calcium data into one Vec to be later replicated
759 for (unsigned node_index = 0; node_index<mpElectricsMesh->GetNumNodes(); node_index++)
760 {
761 if (mpElectricsMesh->GetDistributedVectorFactory()->IsGlobalIndexLocal(node_index))
762 {
763 double calcium_value = mpElectricsProblem->GetTissue()->GetCardiacCell(node_index)->GetIntracellularCalciumConcentration();
764 VecSetValue(calcium_data, node_index ,calcium_value, INSERT_VALUES);
765 }
766 }
767 PetscTools::Barrier();//not sure this is needed
768
769 //Replicate electrics solution and calcium (replication is inside this constructor of ReplicatableVector)
770 ReplicatableVector electrics_solution_repl(electrics_solution);//size=(number of electrics nodes)*ELEC_PROB_DIM
771 ReplicatableVector calcium_repl(calcium_data);//size = number of electrics nodes
772
773 //interpolate values onto the quad points of the mechanics mesh
774 for (unsigned i=0; i<mpMeshPair->rGetElementsAndWeights().size(); i++)
775 {
776 double interpolated_CaI = 0;
777 double interpolated_voltage = 0;
778
779 Element<DIM,DIM>& element = *(mpElectricsMesh->GetElement(mpMeshPair->rGetElementsAndWeights()[i].ElementNum));
780
781 for (unsigned node_index = 0; node_index<element.GetNumNodes(); node_index++)
782 {
783 unsigned global_index = element.GetNodeGlobalIndex(node_index);
784 double CaI_at_node = calcium_repl[global_index];
785 interpolated_CaI += CaI_at_node*mpMeshPair->rGetElementsAndWeights()[i].Weights(node_index);
786 //the following line assumes interleaved solution for ELEC_PROB_DIM>1 (e.g, [Vm_0, phi_e_0, Vm1, phi_e_1...])
787 interpolated_voltage += electrics_solution_repl[global_index*ELEC_PROB_DIM]*mpMeshPair->rGetElementsAndWeights()[i].Weights(node_index);
788 }
789
790 assert(i<mInterpolatedCalciumConcs.size());
791 assert(i<mInterpolatedVoltages.size());
792 mInterpolatedCalciumConcs[i] = interpolated_CaI;
793 mInterpolatedVoltages[i] = interpolated_voltage;
794 }
795
796 LOG(2, " Setting Ca_I. max value = " << Max(mInterpolatedCalciumConcs));
797
798 // NOTE IF NHS: HERE WE SHOULD PERHAPS CHECK WHETHER THE CELL MODELS HAVE Ca_Trop
799 // AND UPDATE FROM NHS TO CELL_MODEL, BUT NOT SURE HOW TO DO THIS.. (esp for implicit)
800
801 // set [Ca], V, t
802 mpCardiacMechSolver->SetCalciumAndVoltage(mInterpolatedCalciumConcs, mInterpolatedVoltages);
803 MechanicsEventHandler::EndEvent(MechanicsEventHandler::NON_MECH);
804
805
811 LOG(2, " Solving mechanics ");
812 mpMechanicsSolver->SetWriteOutput(false);
813
814 // make sure the mechanics solver knows the current time (in case
815 // the traction say is time-dependent).
816 mpMechanicsSolver->SetCurrentTime(stepper.GetTime());
817
818 // see if we will need to output stresses at the end of this timestep
819 if (mNumTimestepsToOutputDeformationGradientsAndStress!=UNSIGNED_UNSET
820 && (counter+1)%mNumTimestepsToOutputDeformationGradientsAndStress == 0)
821 {
822 mpMechanicsSolver->SetComputeAverageStressPerElementDuringSolve(true);
823 }
824
827// for (unsigned i=0; i<mpMechanicsSolver->rGetCurrentSolution().size(); i++)
828// {
829// double current = mpMechanicsSolver->rGetCurrentSolution()[i];
830// double previous = current_solution_previous_time_step[i];
831// double second_last = current_solution_second_last_time_step[i];
832// //double guess = 2*current - previous;
833// double guess = 3*current - 3*previous + second_last;
834//
835// if (!first_step)
836// {
837// current_solution_second_last_time_step[i] = current_solution_previous_time_step[i];
838// }
839// current_solution_previous_time_step[i] = mpMechanicsSolver->rGetCurrentSolution()[i];
840// mpMechanicsSolver->rGetCurrentSolution()[i] = guess;
841// }
842// first_step = false;
843
844 MechanicsEventHandler::BeginEvent(MechanicsEventHandler::ALL_MECH);
845 mpCardiacMechSolver->Solve(stepper.GetTime(), stepper.GetNextTime(), mpProblemDefinition->GetContractionModelOdeTimestep());
846 MechanicsEventHandler::EndEvent(MechanicsEventHandler::ALL_MECH);
847
848 LOG(2, " Number of newton iterations = " << mpMechanicsSolver->GetNumNewtonIterations());
849
850 // update the current time
851 stepper.AdvanceOneTimeStep();
852 counter++;
853
854
860 MechanicsEventHandler::BeginEvent(MechanicsEventHandler::OUTPUT);
861 if (mWriteOutput && (counter%WRITE_EVERY_NTH_TIME==0))
862 {
863 LOG(2, " Writing output");
864 // write deformed position
865 mech_writer_counter++;
866 mpMechanicsSolver->SetWriteOutput();
867 mpMechanicsSolver->WriteCurrentSpatialSolution("solution","nodes",mech_writer_counter);
868
869 p_cmgui_writer->WriteDeformationPositions(rGetDeformedPosition(), counter);
870
871#ifdef CHASTE_VTK
872 if (HeartConfig::Instance()->GetVisualizeWithVtk())
873 {
874 mpCardiacVtkWriter->WriteSolution(counter,electrics_solution_repl);//writer will pick up mech solution from solver
875 }
876#endif
877 if (!mNoElectricsOutput)
878 {
879 mpElectricsProblem->mpWriter->AdvanceAlongUnlimitedDimension();
880 mpElectricsProblem->WriteOneStep(stepper.GetTime(), electrics_solution);
881 }
882
883 if (mIsWatchedLocation)
884 {
885 WriteWatchedLocationData(stepper.GetTime(), electrics_solution);
886 }
887 OnEndOfTimeStep(counter);
888
889 if (mNumTimestepsToOutputDeformationGradientsAndStress!=UNSIGNED_UNSET && counter%mNumTimestepsToOutputDeformationGradientsAndStress==0)
890 {
891 mpMechanicsSolver->WriteCurrentStrains(DEFORMATION_GRADIENT_F,"deformation_gradient",mech_writer_counter);
892 mpMechanicsSolver->WriteCurrentAverageElementStresses("second_PK",mech_writer_counter);
893 }
894 mpMechanicsSolver->SetComputeAverageStressPerElementDuringSolve(false);
895 }
896 MechanicsEventHandler::EndEvent(MechanicsEventHandler::OUTPUT);
897
898 // write the total elapsed time..
900 } // end of main solve time loop
901
902 if ((mWriteOutput) && (!mNoElectricsOutput))
903 {
905 mpElectricsProblem->mpWriter->Close();
906 delete mpElectricsProblem->mpWriter;
907
908 std::string input_dir = mOutputDirectory+"/electrics";
909
910 // Convert simulation data to meshalyzer format - commented
911 std::string config_directory = HeartConfig::Instance()->GetOutputDirectory();
913
914// Hdf5ToMeshalyzerConverter<DIM,DIM> meshalyzer_converter(FileFinder(input_dir, RelativeTo::ChasteTestOutput),
915// "voltage", mpElectricsMesh,
916// HeartConfig::Instance()->GetOutputUsingOriginalNodeOrdering(),
917// HeartConfig::Instance()->GetVisualizerOutputPrecision() );
918 // Write mesh in a suitable form for meshalyzer
919 //std::string output_directory = mOutputDirectory + "/electrics/output";
920 // Write the mesh
921 //MeshalyzerMeshWriter<DIM,DIM> mesh_writer(output_directory, "mesh", false);
922 //mesh_writer.WriteFilesUsingMesh(*mpElectricsMesh);
923 // Write the parameters out
924 //HeartConfig::Instance()->Write();
925
926 // convert output to CMGUI format
928 "voltage", mpElectricsMesh, mHasBath,
929 HeartConfig::Instance()->GetVisualizerOutputPrecision());
930
931 // interpolate the electrical data onto the mechanics mesh nodes and write CMGUI...
932 // Note: this calculates the data on ALL nodes of the mechanics mesh (incl internal,
933 // non-vertex ones), which won't be used if linear CMGUI visualisation
934 // of the mechanics solution is used.
935 VoltageInterpolaterOntoMechanicsMesh<DIM> converter(*mpElectricsMesh,*mpMechanicsMesh);
936 converter.OutputToCmgui(variable_names,input_dir,"voltage");
937
938 // reset to the default value
939 HeartConfig::Instance()->SetOutputDirectory(config_directory);
940 }
941
942 if (p_cmgui_writer)
943 {
944 if (mNoElectricsOutput)
945 {
946 p_cmgui_writer->WriteCmguiScript("","undeformed");
947 }
948 else
949 {
950 p_cmgui_writer->WriteCmguiScript("../../electrics/cmgui_output/voltage_mechanics_mesh","undeformed");
951 }
952 delete p_cmgui_writer;
953 }
954 PetscTools::Destroy(electrics_solution);
955 PetscTools::Destroy(calcium_data);
956 delete p_electrics_solver;
957
958 MechanicsEventHandler::EndEvent(MechanicsEventHandler::ALL);
959}
960
961template<unsigned DIM, unsigned ELEC_PROB_DIM>
963{
964 double max = -1e200;
965 for (unsigned i=0; i<vec.size(); i++)
966 {
967 if (vec[i]>max) max=vec[i];
968 }
969 return max;
970}
971
972template<unsigned DIM, unsigned ELEC_PROB_DIM>
977
978template<unsigned DIM, unsigned ELEC_PROB_DIM>
980{
981 mIsWatchedLocation = true;
982 mWatchedLocation = watchedLocation;
983}
984
985template<unsigned DIM, unsigned ELEC_PROB_DIM>
987{
988 mNumTimestepsToOutputDeformationGradientsAndStress = (unsigned) floor((timeStep/mpProblemDefinition->GetMechanicsSolveTimestep())+0.5);
989 if (fabs(mNumTimestepsToOutputDeformationGradientsAndStress*mpProblemDefinition->GetMechanicsSolveTimestep() - timeStep) > 1e-6)
990 {
991 EXCEPTION("Timestep provided for SetOutputDeformationGradientsAndStress() is not a multiple of mechanics solve timestep");
992 }
993}
994
995template<unsigned DIM, unsigned ELEC_PROB_DIM>
997{
998 return mpMechanicsSolver->rGetDeformedPosition();
999}
1000
1001// Explicit instantiation
1002
1003//note: 1d incompressible material doesn't make sense
#define EXCEPTION(message)
#define NEVER_REACHED
const unsigned UNSIGNED_UNSET
Definition Exception.hpp:53
#define CHASTE_PETSC_NULLPTR
A macro to define PETSc null pointer based on the PETSc version.
boost::numeric::ublas::c_matrix< T, 1, 1 > Inverse(const boost::numeric::ublas::c_matrix< T, 1, 1 > &rM)
void SetTimes(double tStart, double tEnd)
unsigned GetNumNodes() const
unsigned GetNodeGlobalIndex(unsigned localIndex) const
virtual void WriteFilesUsingMesh(AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > &rMesh, bool keepOriginalElementIndexing=true)
double Max(std::vector< double > &vec)
CardiacElectroMechanicsProblem(CompressibilityType compressibilityType, ElectricsProblemType electricsProblemType, TetrahedralMesh< DIM, DIM > *pElectricsMesh, QuadraticMesh< DIM > *pMechanicsMesh, AbstractCardiacCellFactory< DIM > *pCellFactory, ElectroMechanicsProblemDefinition< DIM > *pProblemDefinition, std::string outputDirectory)
c_matrix< double, DIM, DIM > & rCalculateModifiedConductivityTensor(unsigned elementIndex, const c_matrix< double, DIM, DIM > &rOriginalConductivity, unsigned domainIndex)
void SetWatchedPosition(c_vector< double, DIM > watchedLocation)
std::vector< c_vector< double, DIM > > & rGetDeformedPosition()
void WriteWatchedLocationData(double time, Vec voltage)
AbstractCardiacProblem< DIM, DIM, ELEC_PROB_DIM > * mpElectricsProblem
void WriteCmguiScript(std::string fieldBaseName="", std::string undeformedBaseName="")
void WriteDeformationPositions(std::vector< c_vector< double, DIM > > &rDeformedPositions, unsigned counter)
void WriteInitialMesh(std::string fileName="")
void SetAdditionalFieldNames(std::vector< std::string > &rFieldNames)
void SetRegionNames(std::vector< std::string > &rRegionNames)
bool OptionExists(const std::string &rOption)
static CommandLineArguments * Instance()
static AbstractCardiacProblem< DIM, DIM, 1u > * Create(ElectricsProblemType problemType, AbstractCardiacCellFactory< DIM > *pCellFactory)
static AbstractCardiacProblem< DIM, DIM, 2u > * Create(ElectricsProblemType problemType, AbstractCardiacCellFactory< DIM > *pCellFactory)
static AbstractCardiacProblem< DIM, DIM, PROBLEM_DIM > * Create(ElectricsProblemType problemType, AbstractCardiacCellFactory< DIM > *pCellFactory)
double GetPdeTimeStep() const
void SetPrintingTimeStep(double printingTimeStep)
void SetOutputDirectory(const std::string &rOutputDirectory)
void SetOutputFilenamePrefix(const std::string &rOutputFilenamePrefix)
static void Reset()
std::string GetOutputDirectory() const
static HeartConfig * Instance()
void Set(unsigned level, const std::string &rDirectory, const std::string &rFileName="log.txt")
Definition LogFile.cpp:73
static LogFile * Instance()
Definition LogFile.cpp:52
void WriteElapsedTime(std::string pre="")
Definition LogFile.cpp:116
void WriteHeader(std::string simulationType="")
Definition LogFile.cpp:111
static void Close()
Definition LogFile.cpp:101
out_stream OpenOutputFile(const std::string &rFileName, std::ios_base::openmode mode=std::ios::out|std::ios::trunc) const
static void Destroy(Vec &rVec)
static void Barrier(const std::string callerId="")
bool IsTimeAtEnd() const
double GetTime() const
void AdvanceOneTimeStep()
double GetNextTime() const
void OutputToCmgui(std::vector< std::string > &rVariableNames, std::string directory, std::string inputFileNamePrefix)