CardiacElectroMechanicsProblem.cpp

00001 /*
00002 
00003 Copyright (c) 2005-2015, 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 "CardiacElectroMechanicsProblem.hpp"
00037 
00038 #include "OutputFileHandler.hpp"
00039 #include "ReplicatableVector.hpp"
00040 #include "HeartConfig.hpp"
00041 #include "LogFile.hpp"
00042 #include "ChastePoint.hpp"
00043 #include "Element.hpp"
00044 #include "BoundaryConditionsContainer.hpp"
00045 #include "AbstractDynamicLinearPdeSolver.hpp"
00046 #include "TimeStepper.hpp"
00047 #include "TrianglesMeshWriter.hpp"
00048 #include "Hdf5ToMeshalyzerConverter.hpp"
00049 #include "Hdf5ToCmguiConverter.hpp"
00050 #include "MeshalyzerMeshWriter.hpp"
00051 #include "PetscTools.hpp"
00052 #include "ImplicitCardiacMechanicsSolver.hpp"
00053 #include "ExplicitCardiacMechanicsSolver.hpp"
00054 #include "CmguiDeformedSolutionsWriter.hpp"
00055 #include "VoltageInterpolaterOntoMechanicsMesh.hpp"
00056 #include "BidomainProblem.hpp"
00057 
00058 
00059 template<unsigned DIM, unsigned ELEC_PROB_DIM>
00060 void CardiacElectroMechanicsProblem<DIM,ELEC_PROB_DIM>::DetermineWatchedNodes()
00061 {
00062     assert(mIsWatchedLocation);
00063 
00064     // find the nearest electrics mesh node
00065     double min_dist = DBL_MAX;
00066     unsigned node_index = UNSIGNED_UNSET;
00067     for(unsigned i=0; i<mpElectricsMesh->GetNumNodes(); i++)
00068     {
00069         double dist = norm_2(mWatchedLocation - mpElectricsMesh->GetNode(i)->rGetLocation());
00070         if(dist < min_dist)
00071         {
00072             min_dist = dist;
00073             node_index = i;
00074         }
00075     }
00076 
00077     // set up watched node, if close enough
00078     assert(node_index != UNSIGNED_UNSET); // should def have found something
00079     c_vector<double,DIM> pos = mpElectricsMesh->GetNode(node_index)->rGetLocation();
00080 
00081     if(min_dist > 1e-8)
00082     {
00083         #define COVERAGE_IGNORE
00084         std::cout << "ERROR: Could not find an electrics node very close to requested watched location - "
00085                   << "min distance was " << min_dist << " for node " << node_index
00086                   << " at location " << pos << std::flush;;
00087 
00089         //EXCEPTION("Could not find an electrics node very close to requested watched location");
00090         NEVER_REACHED;
00091         #undef COVERAGE_IGNORE
00092     }
00093     else
00094     {
00095         LOG(2,"Chosen electrics node "<<node_index<<" at location " << pos << " to be watched");
00096         mWatchedElectricsNodeIndex = node_index;
00097     }
00098 
00099     // find nearest mechanics mesh
00100     min_dist = DBL_MAX;
00101     node_index = UNSIGNED_UNSET;
00102     c_vector<double,DIM> pos_at_min;
00103 
00104     for(unsigned i=0; i<mpMechanicsMesh->GetNumNodes(); i++)
00105     {
00106         c_vector<double,DIM> position = mpMechanicsMesh->GetNode(i)->rGetLocation();
00107 
00108         double dist = norm_2(position-mWatchedLocation);
00109 
00110         if(dist < min_dist)
00111         {
00112             min_dist = dist;
00113             node_index = i;
00114             pos_at_min = position;
00115         }
00116     }
00117 
00118     // set up watched node, if close enough
00119     assert(node_index != UNSIGNED_UNSET); // should def have found something
00120 
00121     if(min_dist > 1e-8)
00122     {
00123         #define COVERAGE_IGNORE
00124         std::cout << "ERROR: Could not find a mechanics node very close to requested watched location - "
00125                   << "min distance was " << min_dist << " for node " << node_index
00126                   << " at location " << pos_at_min;
00127 
00129         //EXCEPTION("Could not find a mechanics node very close to requested watched location");
00130         NEVER_REACHED;
00131         #undef COVERAGE_IGNORE
00132     }
00133     else
00134     {
00135         LOG(2,"Chosen mechanics node "<<node_index<<" at location " << pos << " to be watched");
00136         mWatchedMechanicsNodeIndex = node_index;
00137     }
00138 
00139     OutputFileHandler handler(mOutputDirectory,false);
00140     mpWatchedLocationFile = handler.OpenOutputFile("watched.txt");
00141 }
00142 
00143 template<unsigned DIM, unsigned ELEC_PROB_DIM>
00144 void CardiacElectroMechanicsProblem<DIM,ELEC_PROB_DIM>::WriteWatchedLocationData(double time, Vec voltage)
00145 {
00146     assert(mIsWatchedLocation);
00147 
00148     std::vector<c_vector<double,DIM> >& deformed_position = mpMechanicsSolver->rGetDeformedPosition();
00149 
00151     ReplicatableVector voltage_replicated(voltage);
00152     double V = voltage_replicated[mWatchedElectricsNodeIndex];
00153 
00156     //     // Metadata is currently being added to CellML models and then this will be avoided by asking for Calcium.
00157     //    double Ca = mpElectricsProblem->GetMonodomainTissue()->GetCardiacCell(mWatchedElectricsNodeIndex)->GetIntracellularCalciumConcentration();
00158 
00159     *mpWatchedLocationFile << time << " ";
00160     for(unsigned i=0; i<DIM; i++)
00161     {
00162         *mpWatchedLocationFile << deformed_position[mWatchedMechanicsNodeIndex](i) << " ";
00163     }
00164     *mpWatchedLocationFile << V << "\n";
00165     mpWatchedLocationFile->flush();
00166 }
00167 
00168 template<unsigned DIM, unsigned ELEC_PROB_DIM>
00169 c_matrix<double,DIM,DIM>& CardiacElectroMechanicsProblem<DIM,ELEC_PROB_DIM>::rCalculateModifiedConductivityTensor(unsigned elementIndex, const c_matrix<double,DIM,DIM>& rOriginalConductivity, unsigned domainIndex)
00170 {
00171 
00172     // first get the deformation gradient for this electrics element
00173     unsigned containing_mechanics_elem = mpMeshPair->rGetCoarseElementsForFineElementCentroids()[elementIndex];
00174     c_matrix<double,DIM,DIM>& r_deformation_gradient = mDeformationGradientsForEachMechanicsElement[containing_mechanics_elem];
00175 
00176     // compute sigma_def = F^{-1} sigma_undef F^{-T}
00177     c_matrix<double,DIM,DIM> inv_F = Inverse(r_deformation_gradient);
00178     mModifiedConductivityTensor = prod(inv_F, rOriginalConductivity);
00179     mModifiedConductivityTensor = prod(mModifiedConductivityTensor, trans(inv_F));
00180 
00181     return mModifiedConductivityTensor;
00182 }
00183 
00184 
00188 template<unsigned DIM, unsigned PROBLEM_DIM>
00189 class CreateElectricsProblem
00190 {
00191 public:
00198     static AbstractCardiacProblem<DIM, DIM, PROBLEM_DIM>* Create(ElectricsProblemType problemType,
00199                                                                  AbstractCardiacCellFactory<DIM>* pCellFactory);
00200 };
00201 
00205 template<unsigned DIM>
00206 class CreateElectricsProblem<DIM, 1u>
00207 {
00208 public:
00215     static AbstractCardiacProblem<DIM, DIM, 1u>* Create(ElectricsProblemType problemType,
00216                                                         AbstractCardiacCellFactory<DIM>* pCellFactory)
00217     {
00218         if (problemType == MONODOMAIN)
00219         {
00220             return new MonodomainProblem<DIM>(pCellFactory);
00221         }
00222         EXCEPTION("The second template parameter should be 2 when a bidomain problem is chosen");
00223     }
00224 };
00225 
00229 template<unsigned DIM>
00230 class CreateElectricsProblem<DIM, 2u>
00231 {
00232 public:
00239     static AbstractCardiacProblem<DIM, DIM, 2u>* Create(ElectricsProblemType problemType,
00240                                                         AbstractCardiacCellFactory<DIM>* pCellFactory)
00241     {
00242         if (problemType == BIDOMAIN)
00243         {
00244             return new BidomainProblem<DIM>(pCellFactory, false);//false-> no bath
00245         }
00246         if (problemType == BIDOMAIN_WITH_BATH)
00247         {
00248             return new BidomainProblem<DIM>(pCellFactory, true);// true-> bath
00249         }
00250         EXCEPTION("The second template parameter should be 1 when a monodomain problem is chosen");
00251     }
00252 };
00253 
00254 
00255 template<unsigned DIM, unsigned ELEC_PROB_DIM>
00256 CardiacElectroMechanicsProblem<DIM,ELEC_PROB_DIM>::CardiacElectroMechanicsProblem(
00257             CompressibilityType compressibilityType,
00258             ElectricsProblemType electricsProblemType,
00259             TetrahedralMesh<DIM,DIM>* pElectricsMesh,
00260             QuadraticMesh<DIM>* pMechanicsMesh,
00261             AbstractCardiacCellFactory<DIM>* pCellFactory,
00262             ElectroMechanicsProblemDefinition<DIM>* pProblemDefinition,
00263             std::string outputDirectory)
00264       : mCompressibilityType(compressibilityType),
00265         mpCardiacMechSolver(NULL),
00266         mpMechanicsSolver(NULL),
00267         mpElectricsMesh(pElectricsMesh),
00268         mpMechanicsMesh(pMechanicsMesh),
00269         mpProblemDefinition(pProblemDefinition),
00270         mHasBath(false),
00271         mpMeshPair(NULL),
00272         mNoElectricsOutput(false),
00273         mIsWatchedLocation(false),
00274         mWatchedElectricsNodeIndex(UNSIGNED_UNSET),
00275         mWatchedMechanicsNodeIndex(UNSIGNED_UNSET),
00276         mNumTimestepsToOutputDeformationGradientsAndStress(UNSIGNED_UNSET)
00277 {
00278     // Do some initial set up...
00279     // However, NOTE, we don't use either the passed in meshes or the problem_definition.
00280     // These pointers are allowed to be NULL, in case a child constructor wants to set
00281     // them up (eg CardiacElectroMechProbRegularGeom).
00282     // The meshes and problem_defn are used for the first time in Initialise().
00283 
00284 
00285     // Start-up mechanics event handler..
00286     MechanicsEventHandler::Reset();
00287     MechanicsEventHandler::BeginEvent(MechanicsEventHandler::ALL);
00288     // disable the electric event handler, because we use a problem class but
00289     // don't call Solve, so we would have to worry about starting and ending any
00290     // events in AbstractCardiacProblem::Solve() (esp. calling EndEvent(EVERYTHING))
00291     // if we didn't disable it.
00292     HeartEventHandler::Disable();
00293 
00294     assert(HeartConfig::Instance()->GetSimulationDuration()>0.0);
00295     assert(HeartConfig::Instance()->GetPdeTimeStep()>0.0);
00296 
00297     // Create the monodomain problem.
00298     // **NOTE** WE ONLY USE THIS TO: set up the cells, get an initial condition
00299     // (voltage) vector, and get a solver. We won't ever call Solve on the cardiac problem class
00300     assert(pCellFactory != NULL);
00301     mpElectricsProblem = CreateElectricsProblem<DIM,ELEC_PROB_DIM>::Create(electricsProblemType, pCellFactory);
00302 
00303     if (electricsProblemType == BIDOMAIN_WITH_BATH)
00304     {
00305         mHasBath = true;
00306     }
00307     // check whether output is required
00308     mWriteOutput = (outputDirectory!="");
00309     if(mWriteOutput)
00310     {
00311         mOutputDirectory = outputDirectory;
00312         // create the directory
00313         OutputFileHandler handler(mOutputDirectory);
00314         mDeformationOutputDirectory = mOutputDirectory + "/deformation";
00315         HeartConfig::Instance()->SetOutputDirectory(mOutputDirectory + "/electrics");
00316         HeartConfig::Instance()->SetOutputFilenamePrefix("voltage");
00317     }
00318     else
00319     {
00320         mDeformationOutputDirectory = "";
00321     }
00322 
00323 //    mpImpactRegion=NULL;
00324 }
00325 
00326 template<unsigned DIM, unsigned ELEC_PROB_DIM>
00327 CardiacElectroMechanicsProblem<DIM,ELEC_PROB_DIM>::~CardiacElectroMechanicsProblem()
00328 {
00329     // NOTE if SetWatchedLocation but not Initialise has been called, mpWatchedLocationFile
00330     // will be uninitialised and using it will cause a seg fault. Hence the mpMechanicsMesh!=NULL
00331     // it is true if Initialise has been called.
00332     if(mIsWatchedLocation && mpMechanicsMesh)
00333     {
00334         mpWatchedLocationFile->close();
00335     }
00336 
00337     delete mpElectricsProblem;
00338     delete mpCardiacMechSolver;
00339     delete mpMeshPair;
00340 
00341     LogFile::Close();
00342 }
00343 
00344 template<unsigned DIM, unsigned ELEC_PROB_DIM>
00345 void CardiacElectroMechanicsProblem<DIM,ELEC_PROB_DIM>::Initialise()
00346 {
00347     assert(mpElectricsMesh!=NULL);
00348     assert(mpMechanicsMesh!=NULL);
00349     assert(mpProblemDefinition!=NULL);
00350     assert(mpCardiacMechSolver==NULL);
00351 
00354     mNumElecTimestepsPerMechTimestep = (unsigned) floor((mpProblemDefinition->GetMechanicsSolveTimestep()/HeartConfig::Instance()->GetPdeTimeStep())+0.5);
00355     if(fabs(mNumElecTimestepsPerMechTimestep*HeartConfig::Instance()->GetPdeTimeStep() - mpProblemDefinition->GetMechanicsSolveTimestep()) > 1e-6)
00356     {
00357         EXCEPTION("Electrics PDE timestep does not divide mechanics solve timestep");
00358     }
00359 
00360     // Create the Logfile (note we have to do this after the output dir has been
00361     // created, else the log file might get cleaned away
00362     std::string log_dir = mOutputDirectory; // just the TESTOUTPUT dir if mOutputDir="";
00363     LogFile::Instance()->Set(2, mOutputDirectory);
00364     LogFile::Instance()->WriteHeader("Electromechanics");
00365     LOG(2, DIM << "d Implicit CardiacElectroMechanics Simulation:");
00366     LOG(2, "End time = " << HeartConfig::Instance()->GetSimulationDuration() << ", electrics time step = " << HeartConfig::Instance()->GetPdeTimeStep() << ", mechanics timestep = " << mpProblemDefinition->GetMechanicsSolveTimestep() << "\n");
00367     LOG(2, "Contraction model ode timestep " << mpProblemDefinition->GetContractionModelOdeTimestep());
00368     LOG(2, "Output is written to " << mOutputDirectory << "/[deformation/electrics]");
00369 
00370     LOG(2, "Electrics mesh has " << mpElectricsMesh->GetNumNodes() << " nodes");
00371     LOG(2, "Mechanics mesh has " << mpMechanicsMesh->GetNumNodes() << " nodes");
00372 
00373     LOG(2, "Initialising..");
00374 
00375 
00376     if(mIsWatchedLocation)
00377     {
00378         DetermineWatchedNodes();
00379     }
00380 
00381     // initialise electrics problem
00382     mpElectricsProblem->SetMesh(mpElectricsMesh);
00383     mpElectricsProblem->Initialise();
00384 
00385     if(mCompressibilityType==INCOMPRESSIBLE)
00386     {
00387         switch(mpProblemDefinition->GetSolverType())
00388         {
00389             case EXPLICIT:
00390                 mpCardiacMechSolver = new ExplicitCardiacMechanicsSolver<IncompressibleNonlinearElasticitySolver<DIM>,DIM>(
00391                                         *mpMechanicsMesh,*mpProblemDefinition,mDeformationOutputDirectory);
00392                 break;
00393             case IMPLICIT:
00394                 mpCardiacMechSolver = new ImplicitCardiacMechanicsSolver<IncompressibleNonlinearElasticitySolver<DIM>,DIM>(
00395                                         *mpMechanicsMesh,*mpProblemDefinition,mDeformationOutputDirectory);
00396                 break;
00397             default:
00398                 NEVER_REACHED;
00399         }
00400     }
00401     else
00402     {
00403         // repeat above with Compressible solver rather than incompressible -
00404         // not the neatest but avoids having to template this class.
00405         switch(mpProblemDefinition->GetSolverType())
00406         {
00407             case EXPLICIT:
00408                 mpCardiacMechSolver = new ExplicitCardiacMechanicsSolver<CompressibleNonlinearElasticitySolver<DIM>,DIM>(
00409                                         *mpMechanicsMesh,*mpProblemDefinition,mDeformationOutputDirectory);
00410                 break;
00411             case IMPLICIT:
00412                 mpCardiacMechSolver = new ImplicitCardiacMechanicsSolver<CompressibleNonlinearElasticitySolver<DIM>,DIM>(
00413                                         *mpMechanicsMesh,*mpProblemDefinition,mDeformationOutputDirectory);
00414                 break;
00415             default:
00416                 NEVER_REACHED;
00417         }
00418     }
00419 
00420 
00421     mpMechanicsSolver = dynamic_cast<AbstractNonlinearElasticitySolver<DIM>*>(mpCardiacMechSolver);
00422     assert(mpMechanicsSolver);
00423 
00424     // set up mesh pair and determine the fine mesh elements and corresponding weights for each
00425     // quadrature point in the coarse mesh
00426     mpMeshPair = new FineCoarseMeshPair<DIM>(*mpElectricsMesh, *mpMechanicsMesh);
00427     mpMeshPair->SetUpBoxesOnFineMesh();
00428     mpMeshPair->ComputeFineElementsAndWeightsForCoarseQuadPoints(*(mpCardiacMechSolver->GetQuadratureRule()), false);
00429     mpMeshPair->DeleteFineBoxCollection();
00430 
00431     mpCardiacMechSolver->SetFineCoarseMeshPair(mpMeshPair);
00432     mpCardiacMechSolver->Initialise();
00433 
00434     unsigned num_quad_points = mpCardiacMechSolver->GetTotalNumQuadPoints();
00435     mInterpolatedCalciumConcs.assign(num_quad_points, 0.0);
00436     mInterpolatedVoltages.assign(num_quad_points, 0.0);
00437 
00438     if(mpProblemDefinition->ReadFibreSheetDirectionsFromFile())
00439     {
00440        mpCardiacMechSolver->SetVariableFibreSheetDirections(mpProblemDefinition->GetFibreSheetDirectionsFile(),
00441                                                             mpProblemDefinition->GetFibreSheetDirectionsDefinedPerQuadraturePoint());
00442     }
00443 
00444 
00445     if(mpProblemDefinition->GetDeformationAffectsConductivity() || mpProblemDefinition->GetDeformationAffectsCellModels())
00446     {
00447         mpMeshPair->SetUpBoxesOnCoarseMesh();
00448     }
00449 
00450 
00451     if(mpProblemDefinition->GetDeformationAffectsCellModels() || mpProblemDefinition->GetDeformationAffectsConductivity())
00452     {
00453         // initialise the stretches saved for each mechanics element
00454         mStretchesForEachMechanicsElement.resize(mpMechanicsMesh->GetNumElements(), 1.0);
00455 
00456         // initialise the store of the F in each mechanics element (one constant value of F) in each
00457         mDeformationGradientsForEachMechanicsElement.resize(mpMechanicsMesh->GetNumElements(),identity_matrix<double>(DIM));
00458     }
00459 
00460 
00461     if(mpProblemDefinition->GetDeformationAffectsCellModels())
00462     {
00463         // compute the coarse elements which contain each fine node -- for transferring stretch from
00464         // mechanics solve electrics cell models
00465         mpMeshPair->ComputeCoarseElementsForFineNodes(false);
00466 
00467     }
00468 
00469     if(mpProblemDefinition->GetDeformationAffectsConductivity())
00470     {
00471         // compute the coarse elements which contain each fine element centroid -- for transferring F from
00472         // mechanics solve to electrics mesh elements
00473         mpMeshPair->ComputeCoarseElementsForFineElementCentroids(false);
00474 
00475         // tell the abstract tissue class that the conductivities need to be modified, passing in this class
00476         // (which is of type AbstractConductivityModifier)
00477         mpElectricsProblem->GetTissue()->SetConductivityModifier(this);
00478     }
00479 
00480     if(mWriteOutput)
00481     {
00482         TrianglesMeshWriter<DIM,DIM> mesh_writer(mOutputDirectory,"electrics_mesh",false);
00483         mesh_writer.WriteFilesUsingMesh(*mpElectricsMesh);
00484     }
00485 }
00486 
00487 template<unsigned DIM, unsigned ELEC_PROB_DIM>
00488 void CardiacElectroMechanicsProblem<DIM,ELEC_PROB_DIM>::Solve()
00489 {
00490     // initialise the meshes and mechanics solver
00491     if(mpCardiacMechSolver==NULL)
00492     {
00493         Initialise();
00494     }
00495 
00496     bool verbose_during_solve = (    mpProblemDefinition->GetVerboseDuringSolve()
00497                                   || CommandLineArguments::Instance()->OptionExists("-mech_verbose")
00498                                   || CommandLineArguments::Instance()->OptionExists("-mech_very_verbose"));
00499 
00500 
00501     mpProblemDefinition->Validate();
00502 
00503     boost::shared_ptr<BoundaryConditionsContainer<DIM,DIM,ELEC_PROB_DIM> > p_bcc(new BoundaryConditionsContainer<DIM,DIM,ELEC_PROB_DIM>);
00504     p_bcc->DefineZeroNeumannOnMeshBoundary(mpElectricsMesh, 0);
00505     mpElectricsProblem->SetBoundaryConditionsContainer(p_bcc);
00506 
00507     // get an electrics solver from the problem. Note that we don't call
00508     // Solve() on the CardiacProblem class, we do the looping here.
00509     AbstractDynamicLinearPdeSolver<DIM,DIM,ELEC_PROB_DIM>* p_electrics_solver
00510        = mpElectricsProblem->CreateSolver();
00511 
00512     // set up initial voltage etc
00513     Vec electrics_solution=NULL; //This will be set and used later
00514     Vec calcium_data= mpElectricsMesh->GetDistributedVectorFactory()->CreateVec();
00515     Vec initial_voltage = mpElectricsProblem->CreateInitialCondition();
00516 
00517     // write the initial position
00518     unsigned counter = 0;
00519 
00520     TimeStepper stepper(0.0, HeartConfig::Instance()->GetSimulationDuration(), mpProblemDefinition->GetMechanicsSolveTimestep());
00521 
00522     CmguiDeformedSolutionsWriter<DIM>* p_cmgui_writer = NULL;
00523 
00524     std::vector<std::string> variable_names;
00525 
00526     if (mWriteOutput)
00527     {
00528         mpMechanicsSolver->SetWriteOutput();
00529         mpMechanicsSolver->WriteCurrentSpatialSolution("undeformed","nodes");
00530 
00531         p_cmgui_writer = new CmguiDeformedSolutionsWriter<DIM>(mOutputDirectory+"/deformation/cmgui",
00532                                                                "solution",
00533                                                                *(this->mpMechanicsMesh),
00534                                                                WRITE_QUADRATIC_MESH);
00535         variable_names.push_back("V");
00536         if(ELEC_PROB_DIM==2)
00537         {
00538             variable_names.push_back("Phi_e");
00539             if (mHasBath==true)
00540             {
00541                 std::vector<std::string> regions;
00542                 regions.push_back("tissue");
00543                 regions.push_back("bath");
00544                 p_cmgui_writer->SetRegionNames(regions);
00545             }
00546         }
00547         p_cmgui_writer->SetAdditionalFieldNames(variable_names);
00548         p_cmgui_writer->WriteInitialMesh("undeformed");
00549     }
00550 
00557 
00558     LOG(2, "\nSolving for initial deformation");
00559     #define COVERAGE_IGNORE
00560     if(verbose_during_solve)
00561     {
00562         std::cout << "\n\n ** Solving for initial deformation\n";
00563     }
00564     #undef COVERAGE_IGNORE
00565 
00566     mpMechanicsSolver->SetWriteOutput(false);
00567 
00568     mpMechanicsSolver->SetCurrentTime(0.0);
00569     MechanicsEventHandler::BeginEvent(MechanicsEventHandler::ALL_MECH);
00570 
00571     mpMechanicsSolver->SetIncludeActiveTension(false);
00572     if(mNumTimestepsToOutputDeformationGradientsAndStress!=UNSIGNED_UNSET)
00573     {
00574         mpMechanicsSolver->SetComputeAverageStressPerElementDuringSolve(true);
00575     }
00576 
00577     unsigned total_newton_iters = 0;
00578     for(unsigned index=1; index<=mpProblemDefinition->GetNumIncrementsForInitialDeformation(); index++)
00579     {
00580         #define COVERAGE_IGNORE
00581         if(verbose_during_solve)
00582         {
00583             std::cout << "    Increment " << index << " of " << mpProblemDefinition->GetNumIncrementsForInitialDeformation() << "\n";
00584         }
00585         #undef COVERAGE_IGNORE
00586 
00587         if(mpProblemDefinition->GetTractionBoundaryConditionType()==PRESSURE_ON_DEFORMED)
00588         {
00589             mpProblemDefinition->SetPressureScaling(((double)index)/mpProblemDefinition->GetNumIncrementsForInitialDeformation());
00590         }
00591         mpMechanicsSolver->Solve();
00592 
00593         total_newton_iters += mpMechanicsSolver->GetNumNewtonIterations();
00594     }
00595 
00596     mpMechanicsSolver->SetIncludeActiveTension(true);
00597     MechanicsEventHandler::EndEvent(MechanicsEventHandler::ALL_MECH);
00598     LOG(2, "    Number of newton iterations = " << total_newton_iters);
00599 
00600 
00601     unsigned mech_writer_counter = 0;
00602 
00603     if (mWriteOutput)
00604     {
00605         LOG(2, "  Writing output");
00606         mpMechanicsSolver->SetWriteOutput();
00607         mpMechanicsSolver->WriteCurrentSpatialSolution("solution","nodes",mech_writer_counter);
00608         p_cmgui_writer->WriteDeformationPositions(rGetDeformedPosition(), mech_writer_counter);
00609 
00610         if(!mNoElectricsOutput)
00611         {
00612             // the writer inside monodomain problem uses the printing timestep
00613             // inside HeartConfig to estimate total number of timesteps, so make
00614             // sure this is set to what we will use.
00615             HeartConfig::Instance()->SetPrintingTimeStep(mpProblemDefinition->GetMechanicsSolveTimestep());
00616 
00617             // When we consider archiving these simulations we will need to get a bool back from the following
00618             // command to decide whether or not the file is being extended, and hence whether to write the
00619             // initial conditions into the .h5 file.
00620             mpElectricsProblem->InitialiseWriter();
00621             mpElectricsProblem->WriteOneStep(stepper.GetTime(), initial_voltage);
00622         }
00623 
00624         if(mIsWatchedLocation)
00625         {
00626             WriteWatchedLocationData(stepper.GetTime(), initial_voltage);
00627         }
00628 
00629         if(mNumTimestepsToOutputDeformationGradientsAndStress!=UNSIGNED_UNSET)
00630         {
00631             mpMechanicsSolver->WriteCurrentStrains(DEFORMATION_GRADIENT_F,"deformation_gradient",mech_writer_counter);
00632             mpMechanicsSolver->WriteCurrentAverageElementStresses("second_PK",mech_writer_counter);
00633         }
00634     }
00635 
00636 
00637     PrepareForSolve();
00638 
00642 //    std::vector<double> current_solution_previous_time_step = mpMechanicsSolver->rGetCurrentSolution();
00643 //    std::vector<double> current_solution_second_last_time_step = mpMechanicsSolver->rGetCurrentSolution();
00644 //    bool first_step = true;
00645 
00646 
00647     // reset this to false, may be reset again below
00648     mpMechanicsSolver->SetComputeAverageStressPerElementDuringSolve(false);
00649 
00650     while (!stepper.IsTimeAtEnd())
00651     {
00652         LOG(2, "\nCurrent time = " << stepper.GetTime());
00653         #define COVERAGE_IGNORE
00654         if(verbose_during_solve)
00655         {
00656             // also output time to screen as newton solve information will be output
00657             std::cout << "\n\n ** Current time = " << stepper.GetTime() << "\n";
00658         }
00659         #undef COVERAGE_IGNORE
00660 
00667         if(mpProblemDefinition->GetDeformationAffectsCellModels() || mpProblemDefinition->GetDeformationAffectsConductivity())
00668         {
00669             //  Determine the stretch and deformation gradient on each element.
00670             //
00671             //  If mpProblemDefinition->GetDeformationAffectsCellModels()==true:
00672             //  Stretch will be passed to the cell models.
00673             //
00674             //  If mpProblemDefinition->GetDeformationAffectsConductivity()==true:
00675             //  The deformation gradient needs to be set up but does not need to be passed to the tissue
00676             //  so that F is used to compute the conductivity. Instead this is
00677             //  done through the line "mpElectricsProblem->GetMonodomainTissue()->SetConductivityModifier(this);" line above, which means
00678             //  rCalculateModifiedConductivityTensor() will be called on this class by the tissue, which then uses the F
00679 
00680             mpCardiacMechSolver->ComputeDeformationGradientAndStretchInEachElement(mDeformationGradientsForEachMechanicsElement, mStretchesForEachMechanicsElement);
00681         }
00682 
00683         if( mpProblemDefinition->GetDeformationAffectsCellModels() )
00684         {
00685             //  Set the stretches on each of the cell models
00686             for(unsigned global_index = mpElectricsMesh->GetDistributedVectorFactory()->GetLow();
00687                          global_index < mpElectricsMesh->GetDistributedVectorFactory()->GetHigh();
00688                          global_index++)
00689             {
00690                 unsigned containing_elem = mpMeshPair->rGetCoarseElementsForFineNodes()[global_index];
00691                 double stretch = mStretchesForEachMechanicsElement[containing_elem];
00692                 mpElectricsProblem->GetTissue()->GetCardiacCell(global_index)->SetStretch(stretch);
00693             }
00694         }
00695 
00696         p_electrics_solver->SetTimeStep(HeartConfig::Instance()->GetPdeTimeStep());
00697 
00703         LOG(2, "  Solving electrics");
00704         MechanicsEventHandler::BeginEvent(MechanicsEventHandler::NON_MECH);
00705         for(unsigned i=0; i<mNumElecTimestepsPerMechTimestep; i++)
00706         {
00707             double current_time = stepper.GetTime() + i*HeartConfig::Instance()->GetPdeTimeStep();
00708             double next_time = stepper.GetTime() + (i+1)*HeartConfig::Instance()->GetPdeTimeStep();
00709 
00710             // solve the electrics
00711             p_electrics_solver->SetTimes(current_time, next_time);
00712             p_electrics_solver->SetInitialCondition( initial_voltage );
00713 
00714             electrics_solution = p_electrics_solver->Solve();
00715 
00716             PetscReal min_voltage, max_voltage;
00717             VecMax(electrics_solution,PETSC_NULL,&max_voltage); //the second param is where the index would be returned
00718             VecMin(electrics_solution,PETSC_NULL,&min_voltage);
00719             if(i==0)
00720             {
00721                 LOG(2, "  minimum and maximum voltage is " << min_voltage <<", "<<max_voltage);
00722             }
00723             else if(i==1)
00724             {
00725                 LOG(2, "  ..");
00726             }
00727 
00728             PetscTools::Destroy(initial_voltage);
00729             initial_voltage = electrics_solution;
00730         }
00731 
00732         if(mpProblemDefinition->GetDeformationAffectsConductivity())
00733         {
00734             p_electrics_solver->SetMatrixIsNotAssembled();
00735         }
00736 
00737 
00743 
00744         // compute Ca_I at each quad point (by interpolation, using the info on which
00745         // electrics element the quad point is in. Then set Ca_I on the mechanics solver
00746         LOG(2, "  Interpolating Ca_I and voltage");
00747 
00748         //Collect the distributed calcium data into one Vec to be later replicated
00749         for(unsigned node_index = 0; node_index<mpElectricsMesh->GetNumNodes(); node_index++)
00750         {
00751             if (mpElectricsMesh->GetDistributedVectorFactory()->IsGlobalIndexLocal(node_index))
00752             {
00753                 double calcium_value = mpElectricsProblem->GetTissue()->GetCardiacCell(node_index)->GetIntracellularCalciumConcentration();
00754                 VecSetValue(calcium_data, node_index ,calcium_value, INSERT_VALUES);
00755             }
00756         }
00757         PetscTools::Barrier();//not sure this is needed
00758 
00759         //Replicate electrics solution and calcium (replication is inside this constructor of ReplicatableVector)
00760         ReplicatableVector electrics_solution_repl(electrics_solution);//size=(number of electrics nodes)*ELEC_PROB_DIM
00761         ReplicatableVector calcium_repl(calcium_data);//size = number of electrics nodes
00762 
00763         //interpolate values onto mechanics mesh
00764         for(unsigned i=0; i<mpMeshPair->rGetElementsAndWeights().size(); i++)
00765         {
00766             double interpolated_CaI = 0;
00767             double interpolated_voltage = 0;
00768 
00769             Element<DIM,DIM>& element = *(mpElectricsMesh->GetElement(mpMeshPair->rGetElementsAndWeights()[i].ElementNum));
00770 
00771             for(unsigned node_index = 0; node_index<element.GetNumNodes(); node_index++)
00772             {
00773                 unsigned global_index = element.GetNodeGlobalIndex(node_index);
00774                 double CaI_at_node =  calcium_repl[global_index];
00775                 interpolated_CaI += CaI_at_node*mpMeshPair->rGetElementsAndWeights()[i].Weights(node_index);
00776                 //the following line assumes interleaved solution for ELEC_PROB_DIM>1 (e.g, [Vm_0, phi_e_0, Vm1, phi_e_1...])
00777                 interpolated_voltage += electrics_solution_repl[global_index*ELEC_PROB_DIM]*mpMeshPair->rGetElementsAndWeights()[i].Weights(node_index);
00778             }
00779 
00780             assert(i<mInterpolatedCalciumConcs.size());
00781             assert(i<mInterpolatedVoltages.size());
00782             mInterpolatedCalciumConcs[i] = interpolated_CaI;
00783             mInterpolatedVoltages[i] = interpolated_voltage;
00784         }
00785 
00786         LOG(2, "  Setting Ca_I. max value = " << Max(mInterpolatedCalciumConcs));
00787 
00788         // NOTE IF NHS: HERE WE SHOULD PERHAPS CHECK WHETHER THE CELL MODELS HAVE Ca_Trop
00789         // AND UPDATE FROM NHS TO CELL_MODEL, BUT NOT SURE HOW TO DO THIS.. (esp for implicit)
00790 
00791         // set [Ca], V, t
00792         mpCardiacMechSolver->SetCalciumAndVoltage(mInterpolatedCalciumConcs, mInterpolatedVoltages);
00793         MechanicsEventHandler::EndEvent(MechanicsEventHandler::NON_MECH);
00794 
00795 
00801         LOG(2, "  Solving mechanics ");
00802         mpMechanicsSolver->SetWriteOutput(false);
00803 
00804         // make sure the mechanics solver knows the current time (in case
00805         // the traction say is time-dependent).
00806         mpMechanicsSolver->SetCurrentTime(stepper.GetTime());
00807 
00808         // see if we will need to output stresses at the end of this timestep
00809         if(    mNumTimestepsToOutputDeformationGradientsAndStress!=UNSIGNED_UNSET
00810             && (counter+1)%mNumTimestepsToOutputDeformationGradientsAndStress == 0 )
00811         {
00812             mpMechanicsSolver->SetComputeAverageStressPerElementDuringSolve(true);
00813         }
00814 
00817 //        for(unsigned i=0; i<mpMechanicsSolver->rGetCurrentSolution().size(); i++)
00818 //        {
00819 //            double current = mpMechanicsSolver->rGetCurrentSolution()[i];
00820 //            double previous = current_solution_previous_time_step[i];
00821 //            double second_last = current_solution_second_last_time_step[i];
00822 //            //double guess = 2*current - previous;
00823 //            double guess = 3*current - 3*previous + second_last;
00824 //
00825 //            if(!first_step)
00826 //            {
00827 //                current_solution_second_last_time_step[i] = current_solution_previous_time_step[i];
00828 //            }
00829 //            current_solution_previous_time_step[i] = mpMechanicsSolver->rGetCurrentSolution()[i];
00830 //            mpMechanicsSolver->rGetCurrentSolution()[i] = guess;
00831 //        }
00832 //        first_step = false;
00833 
00834         MechanicsEventHandler::BeginEvent(MechanicsEventHandler::ALL_MECH);
00835         mpCardiacMechSolver->Solve(stepper.GetTime(), stepper.GetNextTime(), mpProblemDefinition->GetContractionModelOdeTimestep());
00836         MechanicsEventHandler::EndEvent(MechanicsEventHandler::ALL_MECH);
00837 
00838         LOG(2, "    Number of newton iterations = " << mpMechanicsSolver->GetNumNewtonIterations());
00839 
00840          // update the current time
00841         stepper.AdvanceOneTimeStep();
00842         counter++;
00843 
00844 
00850         MechanicsEventHandler::BeginEvent(MechanicsEventHandler::OUTPUT);
00851         if(mWriteOutput && (counter%WRITE_EVERY_NTH_TIME==0))
00852         {
00853             LOG(2, "  Writing output");
00854             // write deformed position
00855             mech_writer_counter++;
00856             mpMechanicsSolver->SetWriteOutput();
00857             mpMechanicsSolver->WriteCurrentSpatialSolution("solution","nodes",mech_writer_counter);
00858 
00859             p_cmgui_writer->WriteDeformationPositions(rGetDeformedPosition(), counter);
00860 
00861             if(!mNoElectricsOutput)
00862             {
00863                 mpElectricsProblem->mpWriter->AdvanceAlongUnlimitedDimension();
00864                 mpElectricsProblem->WriteOneStep(stepper.GetTime(), electrics_solution);
00865             }
00866 
00867             if(mIsWatchedLocation)
00868             {
00869                 WriteWatchedLocationData(stepper.GetTime(), electrics_solution);
00870             }
00871             OnEndOfTimeStep(counter);
00872 
00873             if(mNumTimestepsToOutputDeformationGradientsAndStress!=UNSIGNED_UNSET && counter%mNumTimestepsToOutputDeformationGradientsAndStress==0)
00874             {
00875                 mpMechanicsSolver->WriteCurrentStrains(DEFORMATION_GRADIENT_F,"deformation_gradient",mech_writer_counter);
00876                 mpMechanicsSolver->WriteCurrentAverageElementStresses("second_PK",mech_writer_counter);
00877             }
00878             mpMechanicsSolver->SetComputeAverageStressPerElementDuringSolve(false);
00879         }
00880         MechanicsEventHandler::EndEvent(MechanicsEventHandler::OUTPUT);
00881 
00882         // write the total elapsed time..
00883         LogFile::Instance()->WriteElapsedTime("  ");
00884     }
00885 
00886 
00887 
00888     if ((mWriteOutput) && (!mNoElectricsOutput))
00889     {
00890         HeartConfig::Instance()->Reset();
00891         mpElectricsProblem->mpWriter->Close();
00892         delete mpElectricsProblem->mpWriter;
00893 
00894         std::string input_dir = mOutputDirectory+"/electrics";
00895 
00896         // Convert simulation data to meshalyzer format - commented
00897         std::string config_directory = HeartConfig::Instance()->GetOutputDirectory();
00898         HeartConfig::Instance()->SetOutputDirectory(input_dir);
00899 
00900 //      Hdf5ToMeshalyzerConverter<DIM,DIM> meshalyzer_converter(FileFinder(input_dir, RelativeTo::ChasteTestOutput),
00901 //                                                              "voltage", mpElectricsMesh,
00902 //                                                                HeartConfig::Instance()->GetOutputUsingOriginalNodeOrdering(),
00903 //                                                                HeartConfig::Instance()->GetVisualizerOutputPrecision() );
00904 
00905         // convert output to CMGUI format
00906         Hdf5ToCmguiConverter<DIM,DIM> cmgui_converter(FileFinder(input_dir, RelativeTo::ChasteTestOutput),
00907                                                       "voltage", mpElectricsMesh, mHasBath,
00908                                                       HeartConfig::Instance()->GetVisualizerOutputPrecision());
00909 
00910         // Write mesh in a suitable form for meshalyzer
00911         //std::string output_directory =  mOutputDirectory + "/electrics/output";
00912         // Write the mesh
00913         //MeshalyzerMeshWriter<DIM,DIM> mesh_writer(output_directory, "mesh", false);
00914         //mesh_writer.WriteFilesUsingMesh(*mpElectricsMesh);
00915         // Write the parameters out
00916         //HeartConfig::Instance()->Write();
00917 
00918         // interpolate the electrical data onto the mechanics mesh nodes and write CMGUI...
00919         // Note: this calculates the data on ALL nodes of the mechanics mesh (incl internal,
00920         // non-vertex ones), which won't be used if linear CMGUI visualisation
00921         // of the mechanics solution is used.
00922         VoltageInterpolaterOntoMechanicsMesh<DIM> converter(*mpElectricsMesh,*mpMechanicsMesh,variable_names,input_dir,"voltage");
00923 
00924         // reset to the default value
00925         HeartConfig::Instance()->SetOutputDirectory(config_directory);
00926     }
00927 
00928     if(p_cmgui_writer)
00929     {
00930         if(mNoElectricsOutput)
00931         {
00932             p_cmgui_writer->WriteCmguiScript("","undeformed");
00933         }
00934         else
00935         {
00936             p_cmgui_writer->WriteCmguiScript("../../electrics/cmgui_output/voltage_mechanics_mesh","undeformed");
00937         }
00938         delete p_cmgui_writer;
00939     }
00940     PetscTools::Destroy(electrics_solution);
00941     PetscTools::Destroy(calcium_data);
00942     delete p_electrics_solver;
00943 
00944     MechanicsEventHandler::EndEvent(MechanicsEventHandler::ALL);
00945 }
00946 
00947 
00948 
00949 template<unsigned DIM, unsigned ELEC_PROB_DIM>
00950 double CardiacElectroMechanicsProblem<DIM,ELEC_PROB_DIM>::Max(std::vector<double>& vec)
00951 {
00952     double max = -1e200;
00953     for(unsigned i=0; i<vec.size(); i++)
00954     {
00955         if(vec[i]>max) max=vec[i];
00956     }
00957     return max;
00958 }
00959 
00960 template<unsigned DIM, unsigned ELEC_PROB_DIM>
00961 void CardiacElectroMechanicsProblem<DIM,ELEC_PROB_DIM>::SetNoElectricsOutput()
00962 {
00963     mNoElectricsOutput = true;
00964 }
00965 
00966 template<unsigned DIM, unsigned ELEC_PROB_DIM>
00967 void CardiacElectroMechanicsProblem<DIM,ELEC_PROB_DIM>::SetWatchedPosition(c_vector<double,DIM> watchedLocation)
00968 {
00969     mIsWatchedLocation = true;
00970     mWatchedLocation = watchedLocation;
00971 }
00972 
00973 template<unsigned DIM, unsigned ELEC_PROB_DIM>
00974 void CardiacElectroMechanicsProblem<DIM,ELEC_PROB_DIM>::SetOutputDeformationGradientsAndStress(double timeStep)
00975 {
00976     mNumTimestepsToOutputDeformationGradientsAndStress = (unsigned) floor((timeStep/mpProblemDefinition->GetMechanicsSolveTimestep())+0.5);
00977     if(fabs(mNumTimestepsToOutputDeformationGradientsAndStress*mpProblemDefinition->GetMechanicsSolveTimestep() - timeStep) > 1e-6)
00978     {
00979         EXCEPTION("Timestep provided for SetOutputDeformationGradientsAndStress() is not a multiple of mechanics solve timestep");
00980     }
00981 }
00982 
00983 
00984 template<unsigned DIM, unsigned ELEC_PROB_DIM>
00985 std::vector<c_vector<double,DIM> >& CardiacElectroMechanicsProblem<DIM,ELEC_PROB_DIM>::rGetDeformedPosition()
00986 {
00987     return mpMechanicsSolver->rGetDeformedPosition();
00988 }
00989 
00990 
00991 
00992 
00994 // Explicit instantiation
00996 
00997 //note: 1d incompressible material doesn't make sense
00998 template class CardiacElectroMechanicsProblem<2,1>;
00999 template class CardiacElectroMechanicsProblem<3,1>;
01000 template class CardiacElectroMechanicsProblem<2,2>;
01001 template class CardiacElectroMechanicsProblem<3,2>;

Generated by  doxygen 1.6.2