CardiacElectroMechanicsProblem.cpp

00001 /*
00002 
00003 Copyright (C) University of Oxford, 2005-2009
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 "AbstractDynamicAssemblerMixin.hpp"
00039 #include "TimeStepper.hpp"
00040 #include "QuadraturePointsGroup.hpp"
00041 #include "TrianglesMeshWriter.hpp"
00042 
00043 // Meshalyzer output not working yet
00044 //#include "Hdf5ToMeshalyzerConverter.hpp"
00045 //#include "MeshalyzerMeshWriter.hpp"
00046 //#include "PetscTools.hpp"
00047 
00048 // if including Cinv in monobidomain equations
00049 //#include "NodewiseData.hpp"
00050 
00051      
00052 template<unsigned DIM>
00053 void CardiacElectroMechanicsProblem<DIM>::DetermineWatchedNodes()
00054 {
00055     assert(mIsWatchedLocation);
00056 
00057     // find the nearest electrics mesh node
00058     double min_dist = DBL_MAX;
00059     unsigned node_index = UNSIGNED_UNSET;
00060     for(unsigned i=0; i<mpElectricsMesh->GetNumNodes(); i++)
00061     {
00062         double dist = norm_2(mWatchedLocation - mpElectricsMesh->GetNode(i)->rGetLocation());
00063         if(dist < min_dist)
00064         {
00065             min_dist = dist;
00066             node_index = i;
00067         }
00068     }
00069 
00070     // set up watched node, if close enough
00071     assert(node_index != UNSIGNED_UNSET); // should def have found something
00072     c_vector<double,DIM> pos = mpElectricsMesh->GetNode(node_index)->rGetLocation();
00073 
00074     if(min_dist > 1e-8)
00075     {
00076         #define COVERAGE_IGNORE
00077         std::cout << "ERROR: Could not find an electrics node very close to requested watched location - "
00078                   << "min distance was " << min_dist << " for node " << node_index
00079                   << " at location " << pos << std::flush;;
00080 
00082         //EXCEPTION("Could not find an electrics node very close to requested watched location");
00083         assert(0);
00084         #undef COVERAGE_IGNORE
00085     }
00086     else
00087     {
00088         LOG_AND_COUT(2,"Chosen electrics node "<<node_index<<" at location " << pos << " to be watched");
00089         mWatchedElectricsNodeIndex = node_index;
00090     }
00091 
00092     // find nearest mechanics mesh
00093     min_dist = DBL_MAX;
00094     node_index = UNSIGNED_UNSET;
00095     c_vector<double,DIM> pos_at_min;
00096 
00097     for(unsigned i=0; i<mpMechanicsMesh->GetNumNodes(); i++)
00098     {
00099         c_vector<double,DIM> position = mpMechanicsMesh->GetNode(i)->rGetLocation();
00100 
00101         double dist = norm_2(position-mWatchedLocation);
00102 
00103         if(dist < min_dist)
00104         {
00105             min_dist = dist;
00106             node_index = i;
00107             pos_at_min = position;
00108         }
00109     }
00110 
00111     // set up watched node, if close enough
00112     assert(node_index != UNSIGNED_UNSET); // should def have found something
00113 
00114     if(min_dist > 1e-8)
00115     {
00116         #define COVERAGE_IGNORE
00117         std::cout << "ERROR: Could not find a mechanics node very close to requested watched location - "
00118                   << "min distance was " << min_dist << " for node " << node_index
00119                   << " at location " << pos_at_min;
00120 
00122         //EXCEPTION("Could not find a mechanics node very close to requested watched location");
00123         assert(0);
00124         #undef COVERAGE_IGNORE
00125     }
00126     else
00127     {
00128         LOG_AND_COUT(2,"Chosen electrics node "<<node_index<<" at location " << pos << " to be watched");
00129         mWatchedMechanicsNodeIndex = node_index;
00130     }
00131 
00132     OutputFileHandler handler(mOutputDirectory);
00133     mpWatchedLocationFile = handler.OpenOutputFile("watched.txt");
00134 }
00135 
00136 template<unsigned DIM>
00137 void CardiacElectroMechanicsProblem<DIM>::WriteWatchedLocationData(double time, Vec voltage)
00138 {
00139     assert(mIsWatchedLocation);
00140 
00141     std::vector<c_vector<double,DIM> >& deformed_position = mpCardiacMechAssembler->rGetDeformedPosition();
00142 
00144     ReplicatableVector voltage_replicated(voltage);
00145     double V=voltage_replicated[mWatchedElectricsNodeIndex];
00146 
00148 // NOTE!!! HARDCODED state variable index - assumes Lr91. Hierarchy not set up yet.
00149     double Ca = mpMonodomainProblem->GetMonodomainPde()->GetCardiacCell(mWatchedElectricsNodeIndex)->rGetStateVariables()[3];
00150 
00151     *mpWatchedLocationFile << time << " ";
00152     for(unsigned i=0; i<DIM; i++)
00153     {
00154         *mpWatchedLocationFile << deformed_position[mWatchedMechanicsNodeIndex](i) << " ";
00155     }
00156     *mpWatchedLocationFile << V <<  " " << Ca << "\n";
00157     mpWatchedLocationFile->flush();
00158 }
00159 
00160 
00161 template<unsigned DIM>
00162 CardiacElectroMechanicsProblem<DIM>::CardiacElectroMechanicsProblem(
00163             TetrahedralMesh<DIM,DIM>* pElectricsMesh,
00164             QuadraticMesh<DIM>* pMechanicsMesh,
00165             std::vector<unsigned> fixedMechanicsNodes,
00166             AbstractCardiacCellFactory<DIM>* pCellFactory,
00167             double endTime,
00168             unsigned numElecTimeStepsPerMechTimestep,
00169             double nhsOdeTimeStep,
00170             std::string outputDirectory = "")
00171 {
00172     // Start-up mechanics event handler..
00173     MechanicsEventHandler::Reset();
00174     MechanicsEventHandler::BeginEvent(MechanicsEventHandler::ALL);
00175     // disable the electric event handler, because we use a problem class but
00176     // don't call Solve, so we would have to worry about starting and ending any
00177     // events in AbstractCardiacProblem::Solve() (esp. calling EndEvent(EVERYTHING))
00178     // if we didn't disable it.
00179     HeartEventHandler::Disable();
00180     
00181     // create the monodomain problem. Note the we use this to set up the cells,
00182     // get an initial condition (voltage) vector, and get an assembler. We won't
00183     // ever call solve on the MonodomainProblem
00184     assert(pCellFactory != NULL);
00185     mpMonodomainProblem = new MonodomainProblem<DIM>(pCellFactory);
00186 
00187     // save time infomation
00188     assert(endTime > 0);
00189     mEndTime = endTime;
00190     mElectricsTimeStep = 0.01;
00191     assert(numElecTimeStepsPerMechTimestep>0);
00192 
00193     mNumElecTimestepsPerMechTimestep = numElecTimeStepsPerMechTimestep;
00194 
00195     mMechanicsTimeStep = mElectricsTimeStep*mNumElecTimestepsPerMechTimestep;
00196     assert(nhsOdeTimeStep <= mMechanicsTimeStep+1e-14);
00197     mNhsOdeTimeStep = nhsOdeTimeStep;
00198 
00199     // check whether output is required
00200     mWriteOutput = (outputDirectory!="");
00201     if(mWriteOutput)
00202     {
00203         mOutputDirectory = outputDirectory;
00204         mDeformationOutputDirectory = mOutputDirectory + "/deformation";
00205         HeartConfig::Instance()->SetOutputDirectory(mOutputDirectory + "/electrics");
00206         HeartConfig::Instance()->SetOutputFilenamePrefix("voltage");
00207     }
00208     else
00209     {
00210         mDeformationOutputDirectory = "";
00211     }
00212     mNoElectricsOutput = false;
00213 
00214     // initialise all the pointers
00215     mpElectricsMesh = pElectricsMesh; // note these are allowed to be null, in case a child constructor wants to create them
00216     mpMechanicsMesh = pMechanicsMesh;
00217     mFixedNodes = fixedMechanicsNodes;
00218 
00219     mpCardiacMechAssembler = NULL;
00220 
00221     // Create the Logfile (note we have to do this after the output dir has been
00222     // created, else the log file might get cleaned away
00223     std::string log_dir = mOutputDirectory; // just the TESTOUTPUT dir if mOutputDir="";
00224     LogFile::Instance()->Set(2, mOutputDirectory);
00225     LogFile::Instance()->WriteHeader("Electromechanics");
00226     LOG(2, DIM << "d Implicit CardiacElectroMechanics Simulation:");
00227     LOG(2, "End time = " << mEndTime << ", electrics time step = " << mElectricsTimeStep << ", mechanics timestep = " << mMechanicsTimeStep << "\n");
00228     LOG(2, "Nhs ode timestep " << mNhsOdeTimeStep);
00229     LOG(2, "Output is written to " << mOutputDirectory << "/[deformation/electrics]");
00230 #define COVERAGE_IGNORE
00231 //todo: Coverage
00232     if(mpElectricsMesh != NULL)
00233     {
00234         LOG(2, "Electrics mesh has " << mpElectricsMesh->GetNumNodes() << " nodes");
00235     }
00236     if(mpMechanicsMesh != NULL)
00237     {
00238         LOG(2, "Mechanics mesh has " << mpMechanicsMesh->GetNumNodes() << " nodes");
00239     }
00240 #undef COVERAGE_IGNORE
00241     mIsWatchedLocation = false;
00242     mWatchedElectricsNodeIndex = UNSIGNED_UNSET;
00243     mWatchedMechanicsNodeIndex = UNSIGNED_UNSET;
00244 }
00245 
00246 template<unsigned DIM>
00247 CardiacElectroMechanicsProblem<DIM>::~CardiacElectroMechanicsProblem()
00248 {
00249     // NOTE if SetWatchedLocation but not Initialise has been
00250     // called, mpWatchedLocationFile will be uninitialised and
00251     // using it will cause a seg fault. Hence the mpMechanicsMesh!=NULL
00252     // it is true if Initialise has been called.
00253     if(mIsWatchedLocation && mpMechanicsMesh)
00254     {
00255         mpWatchedLocationFile->close();
00256     }
00257 
00258     delete mpMonodomainProblem;
00259     delete mpCardiacMechAssembler;
00260 
00261     LogFile::Close();
00262 }
00263 
00264 template<unsigned DIM>
00265 void CardiacElectroMechanicsProblem<DIM>::Initialise()
00266 {
00267     LOG(2, "Initialising meshes and cardiac mechanics assembler..");
00268 
00269     assert(mpElectricsMesh!=NULL);
00270     assert(mpMechanicsMesh!=NULL);
00271     assert(mpCardiacMechAssembler==NULL);
00272 
00273     if(mIsWatchedLocation)
00274     {
00275         DetermineWatchedNodes();
00276     }
00277 
00278     // initialise monodomain problem
00279     mpMonodomainProblem->SetMesh(mpElectricsMesh);
00280     
00281     HeartConfig::Instance()->SetIntracellularConductivities(Create_c_vector(1.75,1.75,1.75));
00282 
00283     mpMonodomainProblem->Initialise();
00284 
00285     // construct mechanics assembler
00286     mpCardiacMechAssembler = new ImplicitCardiacMechanicsAssembler<DIM>(mpMechanicsMesh,mDeformationOutputDirectory,mFixedNodes);
00287 
00288     // find the element nums and weights for each gauss point in the mechanics mesh
00289     mElementAndWeightsForQuadPoints.resize(mpCardiacMechAssembler->GetTotalNumQuadPoints());
00290 
00291     // get the quad point positions in the mechanics assembler
00292     QuadraturePointsGroup<DIM> quad_point_posns(*mpMechanicsMesh, *(mpCardiacMechAssembler->GetQuadratureRule()));
00293 
00294     // find the electrics element and weight for each quad point in the mechanics mesh,
00295     // and store
00296     for(unsigned i=0; i<quad_point_posns.Size(); i++)
00297     {
00298         ChastePoint<DIM> point;
00299 
00300         for(unsigned j=0;j<DIM;j++)
00301         {
00302             point.rGetLocation()[j]=quad_point_posns.Get(i)[j];
00303         }
00304 
00305         unsigned elem_index = mpElectricsMesh->GetContainingElementIndex(point);
00306         c_vector<double,DIM+1> weight = mpElectricsMesh->GetElement(elem_index)->CalculateInterpolationWeights(point);
00307 
00308         mElementAndWeightsForQuadPoints[i].ElementNum = elem_index;
00309         mElementAndWeightsForQuadPoints[i].Weights = weight;
00310     }
00311 
00312     if(mWriteOutput)
00313     {
00314         TrianglesMeshWriter<DIM,DIM> mesh_writer(mOutputDirectory,"electrics_mesh",false);
00315         mesh_writer.WriteFilesUsingMesh(*mpElectricsMesh);
00316     }
00317 
00319 //    // get the assembler to compute which electrics nodes are in each mechanics mesh
00320 //    mpCardiacMechAssembler->ComputeElementsContainingNodes(mpElectricsMesh);
00321 //    assert(DIM==2);
00322 //
00323 //    NodewiseData<DIM>::Instance()->AllocateMemory(mpElectricsMesh->GetNumNodes(), 3);
00324 //    std::vector<std::vector<double> >& r_c_inverse = NodewiseData<DIM>::Instance()->rGetData();
00325 //    for(unsigned i=0; i<r_c_inverse.size(); i++)
00326 //    {
00327 //        r_c_inverse[i][0] = 1.0;
00328 //        r_c_inverse[i][1] = 0.0;
00329 //        r_c_inverse[i][2] = 1.0;
00330 //    }
00331 }
00332 
00333 template<unsigned DIM>
00334 void CardiacElectroMechanicsProblem<DIM>::Solve()
00335 {
00336     // initialise the meshes and mechanics assembler
00337     if(mpCardiacMechAssembler==NULL)
00338     {
00339         Initialise();
00340     }
00341 
00342     BoundaryConditionsContainer<DIM,DIM,1> bcc;
00343     bcc.DefineZeroNeumannOnMeshBoundary(mpElectricsMesh, 0);
00344     mpMonodomainProblem->SetBoundaryConditionsContainer(&bcc);
00345 
00346     // get an electrics assembler from the problem. Note that we don't call
00347     // Solve() on the CardiacProblem class, we do the looping here.
00348     AbstractDynamicAssemblerMixin<DIM,DIM,1>* p_electrics_assembler
00349        = mpMonodomainProblem->CreateAssembler();
00350 
00351     // set up initial voltage etc
00352     Vec voltage=NULL; //This will be set and used later
00353     Vec initial_voltage = mpMonodomainProblem->CreateInitialCondition();
00354 
00355     unsigned num_quad_points = mpCardiacMechAssembler->GetTotalNumQuadPoints();
00356     std::vector<NhsCellularMechanicsOdeSystem> cellmech_systems;
00357     std::vector<double> intracellular_Ca(num_quad_points, 0.0);
00358 
00359     // write the initial position
00360     unsigned counter = 0;
00361 
00362     TimeStepper stepper(0.0, mEndTime, mMechanicsTimeStep);
00363 
00364     unsigned mech_writer_counter = 0;
00365     if (mWriteOutput)
00366     {
00367         mpCardiacMechAssembler->SetWriteOutput();
00368         mpCardiacMechAssembler->WriteOutput(mech_writer_counter);
00369 
00370         if(!mNoElectricsOutput)
00371         {
00372             mpMonodomainProblem->InitialiseWriter();
00373             mpMonodomainProblem->WriteOneStep(stepper.GetTime(), initial_voltage);
00374         }
00375 
00376         if(mIsWatchedLocation)
00377         {
00378             WriteWatchedLocationData(stepper.GetTime(), initial_voltage);
00379         }
00380     }
00381 
00382     while (!stepper.IsTimeAtEnd())
00383     {
00384         LOG(2, "\nCurrent time = " << stepper.GetTime());
00385         std::cout << "\n\n ** Current time = " << stepper.GetTime();
00386 
00387         LOG(2, "  Solving electrics");
00388         MechanicsEventHandler::BeginEvent(MechanicsEventHandler::NON_MECH);
00389         for(unsigned i=0; i<mNumElecTimestepsPerMechTimestep; i++)
00390         {
00391             double current_time = stepper.GetTime() + i*mElectricsTimeStep;
00392             double next_time = stepper.GetTime() + (i+1)*mElectricsTimeStep;
00393 
00394             // solve the electrics
00395             p_electrics_assembler->SetTimes(current_time, next_time, mElectricsTimeStep);
00396             p_electrics_assembler->SetInitialCondition( initial_voltage );
00397 
00398             voltage = p_electrics_assembler->Solve();
00399 
00400             PetscReal min_voltage, max_voltage;
00401             VecMax(voltage,PETSC_NULL,&max_voltage); //the second param is where the index would be returned
00402             VecMin(voltage,PETSC_NULL,&min_voltage);
00403             if(i==0)
00404             {
00405                 LOG(2, "  minimum and maximum voltage is " << min_voltage <<", "<<max_voltage);
00406             }
00407             else if(i==1)
00408             {
00409                 LOG(2, "  ..");
00410             }
00411 
00412             VecDestroy(initial_voltage);
00413             initial_voltage = voltage;
00414         }
00415 
00417 //        p_electrics_assembler->SetMatrixIsNotAssembled();
00418 
00419         // compute Ca_I at each quad point (by interpolation, using the info on which
00420         // electrics element the quad point is in. Then set Ca_I on the mechanics solver
00421         LOG(2, "  Interpolating Ca_I");
00422         for(unsigned i=0; i<mElementAndWeightsForQuadPoints.size(); i++)
00423         {
00424             double interpolated_Ca_I = 0;
00425 
00426             Element<DIM,DIM>& element = *(mpElectricsMesh->GetElement(mElementAndWeightsForQuadPoints[i].ElementNum));
00427             for(unsigned node_index = 0; node_index<element.GetNumNodes(); node_index++)
00428             {
00429                 unsigned global_node_index = element.GetNodeGlobalIndex(node_index);
00430                 double Ca_I_at_node = mpMonodomainProblem->GetPde()->GetCardiacCell(global_node_index)->GetIntracellularCalciumConcentration();
00431                 interpolated_Ca_I += Ca_I_at_node*mElementAndWeightsForQuadPoints[i].Weights(node_index);
00432             }
00433 
00434             intracellular_Ca[i] = interpolated_Ca_I;
00435         }
00436 
00437         LOG(2, "  Setting Ca_I. max value = " << Max(intracellular_Ca));
00438 
00439         // NOTE: HERE WE SHOULD REALLY CHECK WHETHER THE CELL MODELS HAVE Ca_Trop
00440         // AND UPDATE FROM NHS TO CELL_MODEL, BUT NOT SURE HOW TO DO THIS.. (esp for implicit)
00441         
00442         // set [Ca]
00443         mpCardiacMechAssembler->SetIntracellularCalciumConcentrations(intracellular_Ca);
00444         MechanicsEventHandler::EndEvent(MechanicsEventHandler::NON_MECH);
00445 
00446 
00447         // solve the mechanics
00448         LOG(2, "  Solving mechanics ");
00449         //double timestep = std::min(0.01, stepper.GetNextTime()-stepper.GetTime());
00450         mpCardiacMechAssembler->SetWriteOutput(false);
00451 
00452         MechanicsEventHandler::BeginEvent(MechanicsEventHandler::ALL_MECH);
00453         mpCardiacMechAssembler->Solve(stepper.GetTime(), stepper.GetNextTime(), mNhsOdeTimeStep);
00454         MechanicsEventHandler::EndEvent(MechanicsEventHandler::ALL_MECH);
00455 
00456         LOG(2, "    Number of newton iterations = " << mpCardiacMechAssembler->GetNumNewtonIterations());
00457 
00458         // update the current time
00459         stepper.AdvanceOneTimeStep();
00460         counter++;
00461 
00462         // output the results
00463         MechanicsEventHandler::BeginEvent(MechanicsEventHandler::OUTPUT);
00464         if(mWriteOutput && (counter%WRITE_EVERY_NTH_TIME==0))
00465         {
00466             LOG(2, "  Writing output");
00467             // write deformed position
00468             mech_writer_counter++;
00469             mpCardiacMechAssembler->SetWriteOutput();
00470             mpCardiacMechAssembler->WriteOutput(mech_writer_counter);
00471 
00472             if(!mNoElectricsOutput)
00473             {
00474                 mpMonodomainProblem->mpWriter->AdvanceAlongUnlimitedDimension();
00475                 mpMonodomainProblem->WriteOneStep(stepper.GetTime(), voltage);
00476             }
00477 
00478             if(mIsWatchedLocation)
00479             {
00480                 WriteWatchedLocationData(stepper.GetTime(), voltage);
00481             }
00482         }
00483         MechanicsEventHandler::EndEvent(MechanicsEventHandler::OUTPUT);
00484 
00486 //        // setup the Cinverse data;
00487 //        std::vector<std::vector<double> >& r_c_inverse = NodewiseData<DIM>::Instance()->rGetData();
00488 //        mpCardiacMechAssembler->CalculateCinverseAtNodes(mpElectricsMesh, r_c_inverse);
00489 //
00490 //        // write lambda
00491 //        std::stringstream file_name;
00492 //        file_name << "lambda_" << mech_writer_counter << ".dat";
00493 //        mpCardiacMechAssembler->WriteLambda(mOutputDirectory,file_name.str());
00494 
00495 
00496         // write the total elapsed time..
00497         LogFile::Instance()->WriteElapsedTime("  ");
00498     }
00499 
00500     if ((mWriteOutput) && (!mNoElectricsOutput))
00501     {
00502 // Not sure why this doesn't work.
00503 //        //Convert simulation data to Meshalyzer format
00504 //        std::string output_directory =  mOutputDirectory + "/electrics/output";
00505 //        Hdf5ToMeshalyzerConverter converter(mOutputDirectory+"/electrics", output_directory, "voltage");
00506 //        
00507 //        //Write mesh in a suitable form for meshalyzer
00508 //        if (PetscTools::AmMaster())
00509 //        {
00510 //            //Write the mesh
00511 //            MeshalyzerMeshWriter<DIM,DIM> mesh_writer(output_directory, "mesh", false);
00512 //            mesh_writer.WriteFilesUsingMesh(*mpElectricsMesh);
00513 //            
00514 //            //Write the parameters out
00515 //            HeartConfig::Instance()->Write(output_directory, "parameters.xml");
00516 //        }
00517 
00518         HeartConfig::Instance()->Reset();
00519         mpMonodomainProblem->mpWriter->Close();
00520         delete mpMonodomainProblem->mpWriter;
00521     }
00522 
00523     VecDestroy(voltage);
00524     delete p_electrics_assembler;
00525 
00526     MechanicsEventHandler::EndEvent(MechanicsEventHandler::ALL);
00527 }
00528 
00529 
00530 
00531 template<unsigned DIM>
00532 double CardiacElectroMechanicsProblem<DIM>::Max(std::vector<double>& vec)
00533 {
00534     double max = -1e200;
00535     for(unsigned i=0; i<vec.size(); i++)
00536     {
00537         if(vec[i]>max) max=vec[i];
00538     }
00539     return max;
00540 }
00541 
00542 template<unsigned DIM>
00543 void CardiacElectroMechanicsProblem<DIM>::SetNoElectricsOutput()
00544 {
00545     mNoElectricsOutput = true;
00546 }
00547 
00548 template<unsigned DIM>
00549 void CardiacElectroMechanicsProblem<DIM>::SetWatchedPosition(c_vector<double,DIM> watchedLocation)
00550 {
00551     mIsWatchedLocation = true;
00552     mWatchedLocation = watchedLocation;
00553 }
00554 
00555 template<unsigned DIM>
00556 std::vector<c_vector<double,DIM> >& CardiacElectroMechanicsProblem<DIM>::rGetDeformedPosition()
00557 {
00558     return mpCardiacMechAssembler->rGetDeformedPosition();
00559 }
00560 
00561 
00563 // Explicit instantiation
00565 
00566 //note: 1d incompressible material doesn't make sense
00567 template class CardiacElectroMechanicsProblem<2>;
00568 template class CardiacElectroMechanicsProblem<3>;

Generated on Wed Mar 18 12:51:52 2009 for Chaste by  doxygen 1.5.5