CardiacElectroMechanicsProblem.cpp

00001 /*
00002 
00003 Copyright (C) University of Oxford, 2005-2011
00004 
00005 University of Oxford means the Chancellor, Masters and Scholars of the
00006 University of Oxford, having an administrative office at Wellington
00007 Square, Oxford OX1 2JD, UK.
00008 
00009 This file is part of Chaste.
00010 
00011 Chaste is free software: you can redistribute it and/or modify it
00012 under the terms of the GNU Lesser General Public License as published
00013 by the Free Software Foundation, either version 2.1 of the License, or
00014 (at your option) any later version.
00015 
00016 Chaste is distributed in the hope that it will be useful, but WITHOUT
00017 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00018 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
00019 License for more details. The offer of Chaste under the terms of the
00020 License is subject to the License being interpreted in accordance with
00021 English Law and subject to any action against the University of Oxford
00022 being under the jurisdiction of the English Courts.
00023 
00024 You should have received a copy of the GNU Lesser General Public License
00025 along with Chaste. If not, see <http://www.gnu.org/licenses/>.
00026 
00027 */
00028 
00029 #include "CardiacElectroMechanicsProblem.hpp"
00030 
00031 #include "OutputFileHandler.hpp"
00032 #include "ReplicatableVector.hpp"
00033 #include "HeartConfig.hpp"
00034 #include "LogFile.hpp"
00035 #include "ChastePoint.hpp"
00036 #include "Element.hpp"
00037 #include "BoundaryConditionsContainer.hpp"
00038 #include "AbstractDynamicLinearPdeSolver.hpp"
00039 #include "TimeStepper.hpp"
00040 #include "QuadraturePointsGroup.hpp"
00041 #include "TrianglesMeshWriter.hpp"
00042 #include "Hdf5ToMeshalyzerConverter.hpp"
00043 #include "Hdf5ToCmguiConverter.hpp"
00044 #include "MeshalyzerMeshWriter.hpp"
00045 #include "PetscTools.hpp"
00046 
00047 #include "ImplicitCardiacMechanicsSolver.hpp"
00048 #include "ExplicitCardiacMechanicsSolver.hpp"
00049 #include "MooneyRivlinMaterialLaw.hpp"
00050 #include "CmguiDeformedSolutionsWriter.hpp"
00051 #include "VoltageInterpolaterOntoMechanicsMesh.hpp"
00052 
00053 template<unsigned DIM>
00054 void CardiacElectroMechanicsProblem<DIM>::DetermineWatchedNodes()
00055 {
00056     assert(mIsWatchedLocation);
00057 
00058     // find the nearest electrics mesh node
00059     double min_dist = DBL_MAX;
00060     unsigned node_index = UNSIGNED_UNSET;
00061     for(unsigned i=0; i<mpElectricsMesh->GetNumNodes(); i++)
00062     {
00063         double dist = norm_2(mWatchedLocation - mpElectricsMesh->GetNode(i)->rGetLocation());
00064         if(dist < min_dist)
00065         {
00066             min_dist = dist;
00067             node_index = i;
00068         }
00069     }
00070 
00071     // set up watched node, if close enough
00072     assert(node_index != UNSIGNED_UNSET); // should def have found something
00073     c_vector<double,DIM> pos = mpElectricsMesh->GetNode(node_index)->rGetLocation();
00074 
00075     if(min_dist > 1e-8)
00076     {
00077         #define COVERAGE_IGNORE
00078         std::cout << "ERROR: Could not find an electrics node very close to requested watched location - "
00079                   << "min distance was " << min_dist << " for node " << node_index
00080                   << " at location " << pos << std::flush;;
00081 
00083         //EXCEPTION("Could not find an electrics node very close to requested watched location");
00084         NEVER_REACHED;
00085         #undef COVERAGE_IGNORE
00086     }
00087     else
00088     {
00089         LOG(2,"Chosen electrics node "<<node_index<<" at location " << pos << " to be watched");
00090         mWatchedElectricsNodeIndex = node_index;
00091     }
00092 
00093     // find nearest mechanics mesh
00094     min_dist = DBL_MAX;
00095     node_index = UNSIGNED_UNSET;
00096     c_vector<double,DIM> pos_at_min;
00097 
00098     for(unsigned i=0; i<mpMechanicsMesh->GetNumNodes(); i++)
00099     {
00100         c_vector<double,DIM> position = mpMechanicsMesh->GetNode(i)->rGetLocation();
00101 
00102         double dist = norm_2(position-mWatchedLocation);
00103 
00104         if(dist < min_dist)
00105         {
00106             min_dist = dist;
00107             node_index = i;
00108             pos_at_min = position;
00109         }
00110     }
00111 
00112     // set up watched node, if close enough
00113     assert(node_index != UNSIGNED_UNSET); // should def have found something
00114 
00115     if(min_dist > 1e-8)
00116     {
00117         #define COVERAGE_IGNORE
00118         std::cout << "ERROR: Could not find a mechanics node very close to requested watched location - "
00119                   << "min distance was " << min_dist << " for node " << node_index
00120                   << " at location " << pos_at_min;
00121 
00123         //EXCEPTION("Could not find a mechanics node very close to requested watched location");
00124         assert(0);
00125         #undef COVERAGE_IGNORE
00126     }
00127     else
00128     {
00129         LOG(2,"Chosen mechanics node "<<node_index<<" at location " << pos << " to be watched");
00130         mWatchedMechanicsNodeIndex = node_index;
00131     }
00132 
00133     OutputFileHandler handler(mOutputDirectory,false);
00134     mpWatchedLocationFile = handler.OpenOutputFile("watched.txt");
00135 }
00136 
00137 template<unsigned DIM>
00138 void CardiacElectroMechanicsProblem<DIM>::WriteWatchedLocationData(double time, Vec voltage)
00139 {
00140     assert(mIsWatchedLocation);
00141 
00142     std::vector<c_vector<double,DIM> >& deformed_position = mpCardiacMechSolver->rGetDeformedPosition();
00143 
00145     ReplicatableVector voltage_replicated(voltage);
00146     double V = voltage_replicated[mWatchedElectricsNodeIndex];
00147 
00150     //     // Metadata is currently being added to CellML models and then this will be avoided by asking for Calcium.
00151     //    double Ca = mpMonodomainProblem->GetMonodomainTissue()->GetCardiacCell(mWatchedElectricsNodeIndex)->GetIntracellularCalciumConcentration();
00152 
00153     *mpWatchedLocationFile << time << " ";
00154     for(unsigned i=0; i<DIM; i++)
00155     {
00156         *mpWatchedLocationFile << deformed_position[mWatchedMechanicsNodeIndex](i) << " ";
00157     }
00158     *mpWatchedLocationFile << V << "\n";
00159     mpWatchedLocationFile->flush();
00160 }
00161 
00162 template<unsigned DIM>
00163 c_matrix<double,DIM,DIM>& CardiacElectroMechanicsProblem<DIM>::rGetModifiedConductivityTensor(unsigned elementIndex, const c_matrix<double,DIM,DIM>& rOriginalConductivity)
00164 {
00165     if(mLastModifiedConductivity.first==elementIndex)
00166     {
00167         // if already computed on this electrics element, just return conductivity
00168         return mLastModifiedConductivity.second;
00169     }
00170     else
00171     {
00172         // new element, need to compute conductivity
00173 
00174         // first get the deformation gradient for this electrics element
00175         unsigned containing_mechanics_elem = mpMeshPair->rGetCoarseElementsForFineElementCentroids()[elementIndex];
00176         c_matrix<double,DIM,DIM>& r_deformation_gradient = mDeformationGradientsForEachMechanicsElement[containing_mechanics_elem];
00177 
00178         // compute sigma_def = F^{-1} sigma_undef F^{-T}
00179         c_matrix<double,DIM,DIM> inv_F = Inverse(r_deformation_gradient);
00180         mLastModifiedConductivity.second = prod(inv_F, rOriginalConductivity);
00181         mLastModifiedConductivity.second = prod(mLastModifiedConductivity.second, trans(inv_F));
00182 
00183         // save the current element and return the tensor
00184         mLastModifiedConductivity.first = elementIndex;
00185         return mLastModifiedConductivity.second;
00186     }
00187 }
00188 
00189 
00190 //
00191 //
00193 //template<unsigned DIM>
00194 //void CardiacElectroMechanicsProblem<DIM>::SetImpactRegion(std::vector<BoundaryElement<DIM-1,DIM>*>& rImpactRegion)
00195 //{
00196 //    assert(mpImpactRegion == NULL);
00197 //    mpImpactRegion = &rImpactRegion;
00198 //}
00199 //
00201 //template<unsigned DIM>
00202 //void CardiacElectroMechanicsProblem<DIM>::ApplyImpactTractions(double time)
00203 //{
00204 //    if(mpImpactRegion==NULL)
00205 //    {
00206 //        return;
00207 //    }
00208 //
00209 //    double start_time = 10;
00210 //    double end_time = 20;
00211 //    double magnitude = 1.5;
00212 //    unsigned direction = 1;
00213 //
00214 //    c_vector<double,DIM> traction = zero_vector<double>(DIM);
00215 //    if( (time>=start_time) && (time<=end_time) )
00216 //    {
00217 //        traction(direction) = magnitude;
00218 //    }
00219 //    mImpactTractions.clear();
00220 //    mImpactTractions.resize(mpImpactRegion->size(), traction);
00221 //    mpCardiacMechSolver->SetSurfaceTractionBoundaryConditions(*mpImpactRegion, mImpactTractions);
00222 //}
00223 
00224 template<unsigned DIM>
00225 CardiacElectroMechanicsProblem<DIM>::CardiacElectroMechanicsProblem(
00226             ContractionModel contractionModel,
00227             TetrahedralMesh<DIM,DIM>* pElectricsMesh,
00228             QuadraticMesh<DIM>* pMechanicsMesh,
00229             std::vector<unsigned> fixedMechanicsNodes,
00230             AbstractCardiacCellFactory<DIM>* pCellFactory,
00231             double endTime,
00232             double electricsPdeTimeStep,
00233             double mechanicsSolveTimestep,
00234             double contractionModelOdeTimeStep,
00235             std::string outputDirectory = "") :
00236         mpMeshPair(NULL)
00237 {
00238     // Start-up mechanics event handler..
00239     MechanicsEventHandler::Reset();
00240     MechanicsEventHandler::BeginEvent(MechanicsEventHandler::ALL);
00241     // disable the electric event handler, because we use a problem class but
00242     // don't call Solve, so we would have to worry about starting and ending any
00243     // events in AbstractCardiacProblem::Solve() (esp. calling EndEvent(EVERYTHING))
00244     // if we didn't disable it.
00245     HeartEventHandler::Disable();
00246 
00247     mContractionModel = contractionModel;
00248 
00249     // save time infomation
00250     assert(endTime > 0);
00251     mEndTime = endTime;
00252 
00253     // We need HeartConfig to store the end time so we can make an estimate of the HDF5 file size later.
00254     HeartConfig::Instance()->SetSimulationDuration(mEndTime);
00255 
00256     assert(electricsPdeTimeStep>0);
00257     mElectricsTimeStep = electricsPdeTimeStep;
00258 
00259     mNumElecTimestepsPerMechTimestep = (unsigned) floor((mechanicsSolveTimestep/electricsPdeTimeStep)+0.5);
00260     if(fabs(mNumElecTimestepsPerMechTimestep*electricsPdeTimeStep - mechanicsSolveTimestep) > 1e-6)
00261     {
00262         EXCEPTION("Electrics PDE timestep does not divide mechanics solve timestep");
00263     }
00264     mMechanicsTimeStep = mechanicsSolveTimestep;
00265 
00266     // **NOTE** if this assertion trips you may be passing in numElecTimestepsPerMechTimestep as the 8th
00267     // parameter of the constructor--this has now changed so you should pass in the mechanics timestep
00268     assert(mechanicsSolveTimestep <= 10); // no reason why this should ever be this large
00269 
00270     assert(contractionModelOdeTimeStep <= mMechanicsTimeStep+1e-14);
00271     mContractionModelOdeTimeStep = contractionModelOdeTimeStep;
00272 
00273     // create the monodomain problem. Note the we use this to set up the cells,
00274     // get an initial condition (voltage) vector, and get an solver. We won't
00275     // ever call solve on the MonodomainProblem
00276     assert(pCellFactory != NULL);
00277     mpMonodomainProblem = new MonodomainProblem<DIM>(pCellFactory);
00278 
00279     // check whether output is required
00280     mWriteOutput = (outputDirectory!="");
00281     if(mWriteOutput)
00282     {
00283         mOutputDirectory = outputDirectory;
00284         // create the directory
00285         OutputFileHandler handler(mOutputDirectory);
00286         mDeformationOutputDirectory = mOutputDirectory + "/deformation";
00287         HeartConfig::Instance()->SetOutputDirectory(mOutputDirectory + "/electrics");
00288         HeartConfig::Instance()->SetOutputFilenamePrefix("voltage");
00289     }
00290     else
00291     {
00292         mDeformationOutputDirectory = "";
00293     }
00294     mNoElectricsOutput = false;
00295 
00296     // initialise all the pointers
00297     mpElectricsMesh = pElectricsMesh; // note these are allowed to be null, in case a child constructor wants to create them
00298     mpMechanicsMesh = pMechanicsMesh;
00299     mFixedNodes = fixedMechanicsNodes;
00300 
00301     mpCardiacMechSolver = NULL;
00302 
00303     // Create the Logfile (note we have to do this after the output dir has been
00304     // created, else the log file might get cleaned away
00305     std::string log_dir = mOutputDirectory; // just the TESTOUTPUT dir if mOutputDir="";
00306     LogFile::Instance()->Set(2, mOutputDirectory);
00307     LogFile::Instance()->WriteHeader("Electromechanics");
00308     LOG(2, DIM << "d Implicit CardiacElectroMechanics Simulation:");
00309     LOG(2, "End time = " << mEndTime << ", electrics time step = " << mElectricsTimeStep << ", mechanics timestep = " << mMechanicsTimeStep << "\n");
00310     LOG(2, "Contraction model ode timestep " << mContractionModelOdeTimeStep);
00311     LOG(2, "Output is written to " << mOutputDirectory << "/[deformation/electrics]");
00312 
00313     if(mpElectricsMesh != NULL)
00314     {
00315         LOG(2, "Electrics mesh has " << mpElectricsMesh->GetNumNodes() << " nodes");
00316     }
00317     if(mpMechanicsMesh != NULL)
00318     {
00319         LOG(2, "Mechanics mesh has " << mpMechanicsMesh->GetNumNodes() << " nodes");
00320     }
00321 
00322     mIsWatchedLocation = false;
00323     mWatchedElectricsNodeIndex = UNSIGNED_UNSET;
00324     mWatchedMechanicsNodeIndex = UNSIGNED_UNSET;
00325 
00326     mFibreSheetDirectionsFile = "";
00327 
00328     mConductivityAffectedByDeformationMef = false;
00329     mCellModelsAffectedByDeformationMef = false;
00330 
00331     mLastModifiedConductivity.first = UNSIGNED_UNSET; //important
00332 
00333 //    mpImpactRegion=NULL;
00334 }
00335 
00336 template<unsigned DIM>
00337 CardiacElectroMechanicsProblem<DIM>::~CardiacElectroMechanicsProblem()
00338 {   
00344     if(mIsWatchedLocation && mpMechanicsMesh)
00345     {
00346         mpWatchedLocationFile->close();
00347     }
00348 
00349     delete mpMonodomainProblem;
00350 
00351     delete mpCardiacMechSolver;
00352 
00353     delete mpMeshPair;
00354 
00355     LogFile::Close();
00356 }
00357 
00358 template<unsigned DIM>
00359 void CardiacElectroMechanicsProblem<DIM>::Initialise()
00360 {
00361     LOG(2, "Initialising..");
00362 
00363     assert(mpElectricsMesh!=NULL);
00364     assert(mpMechanicsMesh!=NULL);
00365     assert(mpCardiacMechSolver==NULL);
00366 
00367     if(mIsWatchedLocation)
00368     {
00369         DetermineWatchedNodes();
00370     }
00371 
00372 
00373     // initialise monodomain problem
00374     mpMonodomainProblem->SetMesh(mpElectricsMesh);
00375     HeartConfig::Instance()->SetIntracellularConductivities(Create_c_vector(1.75,1.75,1.75));
00376     mpMonodomainProblem->Initialise();
00377 
00378     // Construct mechanics solver
00379     // Here we pick the best solver for each particular contraction model. Commented out versions are
00380     // for experimentation.
00381     switch(mContractionModel)
00382     {
00383         case NASH2004:
00384             // stretch and stretch-rate independent, so should use explicit
00385             mpCardiacMechSolver = new ExplicitCardiacMechanicsSolver<DIM>(mContractionModel,mpMechanicsMesh,mDeformationOutputDirectory,mFixedNodes);
00386             break;
00387         case KERCHOFFS2003:
00388             // stretch independent, so should use implicit (explicit may be unstable)
00389             mpCardiacMechSolver = new ImplicitCardiacMechanicsSolver<DIM>(mContractionModel,mpMechanicsMesh,mDeformationOutputDirectory,mFixedNodes);
00390             break;
00391         case NHS:
00392             // stretch and stretch-rate independent, so should definitely use implicit
00393             mpCardiacMechSolver = new ImplicitCardiacMechanicsSolver<DIM>(mContractionModel,mpMechanicsMesh,mDeformationOutputDirectory,mFixedNodes);
00394             break;
00395         default:
00396             EXCEPTION("Invalid contraction model, options are: KERCHOFFS2003 or NHS");
00397     }
00398 
00399     if(mFibreSheetDirectionsFile!="")
00400     {
00401        mpCardiacMechSolver->SetVariableFibreSheetDirections(mFibreSheetDirectionsFile, mFibreSheetDirectionsDefinedPerQuadraturePoint);
00402     }
00403 
00404     // set up mesh pair and determine the fine mesh elements and corresponding weights for each
00405     // quadrature point in the coarse mesh
00406     mpMeshPair = new FineCoarseMeshPair<DIM>(*mpElectricsMesh, *mpMechanicsMesh);
00407     mpMeshPair->SetUpBoxesOnFineMesh();
00408     mpMeshPair->ComputeFineElementsAndWeightsForCoarseQuadPoints(*(mpCardiacMechSolver->GetQuadratureRule()), false);
00409     mpMeshPair->DeleteFineBoxCollection();
00410 
00411 
00412 
00413 
00414     if(mConductivityAffectedByDeformationMef || mCellModelsAffectedByDeformationMef)
00415     {
00416         mpMeshPair->SetUpBoxesOnCoarseMesh();
00417     }
00418 
00419     if(mCellModelsAffectedByDeformationMef)
00420     {
00421         // compute the coarse elements which contain each fine node -- for transferring stretch from
00422         // mechanics solve electrics cell models
00423         mpMeshPair->ComputeCoarseElementsForFineNodes(false);
00424 
00425         // initialise the stretches saved for each mechanics element
00426         mStretchesForEachMechanicsElement.resize(mpMechanicsMesh->GetNumElements(),1.0);
00427     }
00428 
00429     if(mConductivityAffectedByDeformationMef)
00430     {
00431         // compute the coarse elements which contain each fine element centroid -- for transferring F from
00432         // mechanics solve to electrics mesh elements
00433         mpMeshPair->ComputeCoarseElementsForFineElementCentroids(false);
00434 
00435         // initialise the store of the F in each mechanics element (one constant value of F) in each
00436         mDeformationGradientsForEachMechanicsElement.resize(mpMechanicsMesh->GetNumElements(),identity_matrix<double>(DIM));
00437 
00438         // tell the abstract tissue class that the conductivities need to be modified, passing in this class
00439         // (which is of type AbstractConductivityModifier)
00440         mpMonodomainProblem->GetMonodomainTissue()->SetConductivityModifier(this);
00441     }
00442 
00443     if(mWriteOutput)
00444     {
00445         TrianglesMeshWriter<DIM,DIM> mesh_writer(mOutputDirectory,"electrics_mesh",false);
00446         mesh_writer.WriteFilesUsingMesh(*mpElectricsMesh);
00447     }
00448 }
00449 
00450 template<unsigned DIM>
00451 void CardiacElectroMechanicsProblem<DIM>::Solve()
00452 {
00453 
00454     // initialise the meshes and mechanics solver
00455     if(mpCardiacMechSolver==NULL)
00456     {
00457         Initialise();
00458     }
00459 
00460     boost::shared_ptr<BoundaryConditionsContainer<DIM,DIM,1> > p_bcc(new BoundaryConditionsContainer<DIM,DIM,1>);
00461     p_bcc->DefineZeroNeumannOnMeshBoundary(mpElectricsMesh, 0);
00462     mpMonodomainProblem->SetBoundaryConditionsContainer(p_bcc);
00463 
00464     // get an electrics solver from the problem. Note that we don't call
00465     // Solve() on the CardiacProblem class, we do the looping here.
00466     AbstractDynamicLinearPdeSolver<DIM,DIM,1>* p_electrics_solver
00467        = mpMonodomainProblem->CreateSolver();
00468 
00469     // set up initial voltage etc
00470     Vec voltage=NULL; //This will be set and used later
00471     Vec initial_voltage = mpMonodomainProblem->CreateInitialCondition();
00472 
00473     unsigned num_quad_points = mpCardiacMechSolver->GetTotalNumQuadPoints();
00474     std::vector<double> interpolated_calcium_concs(num_quad_points, 0.0);
00475     std::vector<double> interpolated_voltages(num_quad_points, 0.0);
00476 
00477     // write the initial position
00478     unsigned counter = 0;
00479 
00480     TimeStepper stepper(0.0, mEndTime, mMechanicsTimeStep);
00481 
00482     CmguiDeformedSolutionsWriter<DIM>* p_cmgui_writer = NULL;
00483 
00484     unsigned mech_writer_counter = 0;
00485     if (mWriteOutput)
00486     {
00487         mpCardiacMechSolver->SetWriteOutput();
00488         mpCardiacMechSolver->WriteCurrentDeformation("solution",mech_writer_counter);
00489 
00490         p_cmgui_writer = new CmguiDeformedSolutionsWriter<DIM>(mOutputDirectory+"/deformation/cmgui",
00491                                                                "solution",
00492                                                                *(this->mpMechanicsMesh),
00493                                                                WRITE_QUADRATIC_MESH);
00494         std::vector<std::string> fields;
00495         fields.push_back("V");
00496         p_cmgui_writer->SetAdditionalFieldNames(fields);
00497         p_cmgui_writer->WriteInitialMesh();
00498 
00499 
00500         if(!mNoElectricsOutput)
00501         {
00502             // the writer inside monodomain problem uses the printing timestep
00503             // inside HeartConfig to estimate total number of timesteps, so make
00504             // sure this is set to what we will use.
00505             HeartConfig::Instance()->SetPrintingTimeStep(mMechanicsTimeStep);
00506             mpMonodomainProblem->InitialiseWriter();
00507             mpMonodomainProblem->WriteOneStep(stepper.GetTime(), initial_voltage);
00508         }
00509 
00510         if(mIsWatchedLocation)
00511         {
00512             WriteWatchedLocationData(stepper.GetTime(), initial_voltage);
00513         }
00514     }
00515 
00516     PrepareForSolve();
00517 
00518     while (!stepper.IsTimeAtEnd())
00519     {
00520         LOG(2, "\nCurrent time = " << stepper.GetTime());
00521         #ifdef MECH_VERBOSE // defined in AbstractNonlinearElasticitySolver
00522         // also output time to screen as newton solve information will be output
00523         std::cout << "\n\n ** Current time = " << stepper.GetTime() << "\n";
00524         #endif
00525 
00532         if(mCellModelsAffectedByDeformationMef)
00533         {
00534             //  Determine the stretch in each mechanics element (later: determine stretch, and
00535             //  deformation gradient)
00536             mpCardiacMechSolver->ComputeDeformationGradientAndStretchInEachElement(mDeformationGradientsForEachMechanicsElement, mStretchesForEachMechanicsElement);
00537 
00538             //  Set the stretches on each of the cell models
00539             for(unsigned global_index = mpElectricsMesh->GetDistributedVectorFactory()->GetLow();
00540                          global_index < mpElectricsMesh->GetDistributedVectorFactory()->GetHigh();
00541                          global_index++)
00542             {
00543                 unsigned containing_elem = mpMeshPair->rGetCoarseElementsForFineNodes()[global_index];
00544                 double stretch = mStretchesForEachMechanicsElement[containing_elem];
00545                 mpMonodomainProblem->GetTissue()->GetCardiacCell(global_index)->SetStretch(stretch);
00546             }
00547 
00548             // the deformation gradient does not need to be passed to the tissue so that F is used to compute the conductivity, instead this is
00549             // done through the "mpMonodomainProblem->GetMonodomainTissue()->SetConductivityModifier(this);" line above, which means
00550             // rGetModifiedConductivityTensor() will be called on this class by the tissue, which then uses the F
00551         }
00552 
00553         p_electrics_solver->SetTimeStep(mElectricsTimeStep);
00554 
00560         LOG(2, "  Solving electrics");
00561         MechanicsEventHandler::BeginEvent(MechanicsEventHandler::NON_MECH);
00562         for(unsigned i=0; i<mNumElecTimestepsPerMechTimestep; i++)
00563         {
00564             double current_time = stepper.GetTime() + i*mElectricsTimeStep;
00565             double next_time = stepper.GetTime() + (i+1)*mElectricsTimeStep;
00566 
00567             // solve the electrics
00568             p_electrics_solver->SetTimes(current_time, next_time);
00569             p_electrics_solver->SetInitialCondition( initial_voltage );
00570 
00571             voltage = p_electrics_solver->Solve();
00572 
00573             PetscReal min_voltage, max_voltage;
00574             VecMax(voltage,PETSC_NULL,&max_voltage); //the second param is where the index would be returned
00575             VecMin(voltage,PETSC_NULL,&min_voltage);
00576             if(i==0)
00577             {
00578                 LOG(2, "  minimum and maximum voltage is " << min_voltage <<", "<<max_voltage);
00579             }
00580             else if(i==1)
00581             {
00582                 LOG(2, "  ..");
00583             }
00584 
00585             VecDestroy(initial_voltage);
00586             initial_voltage = voltage;
00587         }
00588 
00589         if(mConductivityAffectedByDeformationMef)
00590         {
00591             p_electrics_solver->SetMatrixIsNotAssembled();
00592         }
00593 
00594 
00600 
00601         // compute Ca_I at each quad point (by interpolation, using the info on which
00602         // electrics element the quad point is in. Then set Ca_I on the mechanics solver
00603         LOG(2, "  Interpolating Ca_I and voltage");
00604 
00605         ReplicatableVector voltage_repl(voltage);
00606 
00607         // collect all the calcium concentrations (from the cells, which are
00608         // distributed) in one (replicated) vector
00609         ReplicatableVector calcium_repl(mpElectricsMesh->GetNumNodes());
00610         PetscInt lo, hi;
00611         VecGetOwnershipRange(voltage, &lo, &hi);
00612         for(int index=lo; index<hi; index++)
00613         {
00614             calcium_repl[index] = mpMonodomainProblem->GetTissue()->GetCardiacCell(index)->GetIntracellularCalciumConcentration();
00615         }
00616         PetscTools::Barrier();
00617         calcium_repl.Replicate(lo,hi);
00618 
00619 
00620         for(unsigned i=0; i<mpMeshPair->rGetElementsAndWeights().size(); i++)
00621         {
00622             double interpolated_CaI = 0;
00623             double interpolated_voltage = 0;
00624 
00625             Element<DIM,DIM>& element = *(mpElectricsMesh->GetElement(mpMeshPair->rGetElementsAndWeights()[i].ElementNum));
00626             for(unsigned node_index = 0; node_index<element.GetNumNodes(); node_index++)
00627             {
00628                 unsigned global_node_index = element.GetNodeGlobalIndex(node_index);
00629                 double CaI_at_node =  calcium_repl[global_node_index];
00630                 interpolated_CaI += CaI_at_node*mpMeshPair->rGetElementsAndWeights()[i].Weights(node_index);
00631                 interpolated_voltage += voltage_repl[global_node_index]*mpMeshPair->rGetElementsAndWeights()[i].Weights(node_index);
00632             }
00633 
00634             interpolated_calcium_concs[i] = interpolated_CaI;
00635             interpolated_voltages[i] = interpolated_voltage;
00636         }
00637 
00638         LOG(2, "  Setting Ca_I. max value = " << Max(interpolated_calcium_concs));
00639 
00640         // NOTE IF NHS: HERE WE SHOULD PERHAPS CHECK WHETHER THE CELL MODELS HAVE Ca_Trop
00641         // AND UPDATE FROM NHS TO CELL_MODEL, BUT NOT SURE HOW TO DO THIS.. (esp for implicit)
00642 
00643         // set [Ca], V, t
00644         mpCardiacMechSolver->SetCalciumAndVoltage(interpolated_calcium_concs, interpolated_voltages);
00645         MechanicsEventHandler::EndEvent(MechanicsEventHandler::NON_MECH);
00646 
00647 
00648 
00654         LOG(2, "  Solving mechanics ");
00655         mpCardiacMechSolver->SetWriteOutput(false);
00656 
00657         // make sure the mechanics solver knows the current time (in case
00658         // the traction say is time-dependent).
00659         mpCardiacMechSolver->SetCurrentTime(stepper.GetTime());
00660 
00662 //        ApplyImpactTractions(stepper.GetTime());
00663 
00664         MechanicsEventHandler::BeginEvent(MechanicsEventHandler::ALL_MECH);
00665         mpCardiacMechSolver->Solve(stepper.GetTime(), stepper.GetNextTime(), mContractionModelOdeTimeStep);
00666         MechanicsEventHandler::EndEvent(MechanicsEventHandler::ALL_MECH);
00667 
00668         LOG(2, "    Number of newton iterations = " << mpCardiacMechSolver->GetNumNewtonIterations());
00669 
00670          // update the current time
00671         stepper.AdvanceOneTimeStep();
00672         counter++;
00673 
00674 
00680         MechanicsEventHandler::BeginEvent(MechanicsEventHandler::OUTPUT);
00681         if(mWriteOutput && (counter%WRITE_EVERY_NTH_TIME==0))
00682         {
00683             LOG(2, "  Writing output");
00684             // write deformed position
00685             mech_writer_counter++;
00686             mpCardiacMechSolver->SetWriteOutput();
00687             mpCardiacMechSolver->WriteCurrentDeformation("solution",mech_writer_counter);
00688 
00689             p_cmgui_writer->WriteDeformationPositions(rGetDeformedPosition(), counter);
00690 
00691             if(!mNoElectricsOutput)
00692             {
00693                 mpMonodomainProblem->mpWriter->AdvanceAlongUnlimitedDimension();
00694                 mpMonodomainProblem->WriteOneStep(stepper.GetTime(), voltage);
00695             }
00696 
00697             if(mIsWatchedLocation)
00698             {
00699                 WriteWatchedLocationData(stepper.GetTime(), voltage);
00700             }
00701             OnEndOfTimeStep(counter);
00702         }
00703         MechanicsEventHandler::EndEvent(MechanicsEventHandler::OUTPUT);
00704 
00705         // write the total elapsed time..
00706         LogFile::Instance()->WriteElapsedTime("  ");
00707     }
00708 
00709 
00710 
00711     if ((mWriteOutput) && (!mNoElectricsOutput))
00712     {
00713         HeartConfig::Instance()->Reset();
00714         mpMonodomainProblem->mpWriter->Close();
00715         delete mpMonodomainProblem->mpWriter;
00716 
00717         std::string input_dir = mOutputDirectory+"/electrics";
00718 
00719         // Convert simulation data to meshalyzer format - commented
00720         std::string config_directory = HeartConfig::Instance()->GetOutputDirectory();
00721         HeartConfig::Instance()->SetOutputDirectory(input_dir);
00722 
00723         //Hdf5ToMeshalyzerConverter<DIM,DIM> meshalyzer_converter(input_dir, "voltage", mpElectricsMesh);
00724 
00725         // convert output to CMGUI format
00726         Hdf5ToCmguiConverter<DIM,DIM> cmgui_converter(input_dir,"voltage",mpElectricsMesh);
00727 
00728         // Write mesh in a suitable form for meshalyzer
00729         //std::string output_directory =  mOutputDirectory + "/electrics/output";
00730         // Write the mesh
00731         //MeshalyzerMeshWriter<DIM,DIM> mesh_writer(output_directory, "mesh", false);
00732         //mesh_writer.WriteFilesUsingMesh(*mpElectricsMesh);
00733         // Write the parameters out
00734         //HeartConfig::Instance()->Write();
00735 
00736         // interpolate the electrical data onto the mechanics mesh nodes and write CMGUI...
00737         // Note: this calculates the data on ALL nodes of the mechanics mesh (incl internal,
00738         // non-vertex ones), which won't be used if linear CMGUI visualisation
00739         // of the mechanics solution is used.
00740         VoltageInterpolaterOntoMechanicsMesh<DIM> cnverter(*mpElectricsMesh,*mpMechanicsMesh,input_dir,"voltage");
00741 
00742         // reset to the default value
00743         HeartConfig::Instance()->SetOutputDirectory(config_directory);
00744     }
00745 
00746     if(p_cmgui_writer)
00747     {
00748         if(mNoElectricsOutput)
00749         {
00750             p_cmgui_writer->WriteCmguiScript();
00751         }
00752         else
00753         {
00754             p_cmgui_writer->WriteCmguiScript("../../electrics/cmgui_output/voltage_mechanics_mesh");
00755         }
00756         delete p_cmgui_writer;
00757     }
00758     VecDestroy(voltage);
00759     delete p_electrics_solver;
00760 
00761     MechanicsEventHandler::EndEvent(MechanicsEventHandler::ALL);
00762 }
00763 
00764 
00765 
00766 template<unsigned DIM>
00767 double CardiacElectroMechanicsProblem<DIM>::Max(std::vector<double>& vec)
00768 {
00769     double max = -1e200;
00770     for(unsigned i=0; i<vec.size(); i++)
00771     {
00772         if(vec[i]>max) max=vec[i];
00773     }
00774     return max;
00775 }
00776 
00777 template<unsigned DIM>
00778 void CardiacElectroMechanicsProblem<DIM>::SetNoElectricsOutput()
00779 {
00780     mNoElectricsOutput = true;
00781 }
00782 
00783 template<unsigned DIM>
00784 void CardiacElectroMechanicsProblem<DIM>::SetWatchedPosition(c_vector<double,DIM> watchedLocation)
00785 {
00786     mIsWatchedLocation = true;
00787     mWatchedLocation = watchedLocation;
00788 }
00789 
00790 template<unsigned DIM>
00791 void CardiacElectroMechanicsProblem<DIM>::SetVariableFibreSheetDirectionsFile(std::string fibreSheetDirectionsFile, bool definedPerQuadraturePoint)
00792 {
00793     mFibreSheetDirectionsFile = fibreSheetDirectionsFile;
00794     mFibreSheetDirectionsDefinedPerQuadraturePoint = definedPerQuadraturePoint;
00795 }
00796 
00797 template<unsigned DIM>
00798 std::vector<c_vector<double,DIM> >& CardiacElectroMechanicsProblem<DIM>::rGetDeformedPosition()
00799 {
00800     return mpCardiacMechSolver->rGetDeformedPosition();
00801 }
00802 
00803 
00804 
00805 
00807 // Explicit instantiation
00809 
00810 //note: 1d incompressible material doesn't make sense
00811 template class CardiacElectroMechanicsProblem<2>;
00812 template class CardiacElectroMechanicsProblem<3>;

Generated on Mon Apr 18 11:35:28 2011 for Chaste by  doxygen 1.5.5