AbstractCardiacMechanicsSolver.hpp

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 #ifndef ABSTRACTCARDIACMECHANICSSOLVER_HPP_
00030 #define ABSTRACTCARDIACMECHANICSSOLVER_HPP_
00031 
00032 #include "IncompressibleNonlinearElasticitySolver.hpp"
00033 #include "CompressibleNonlinearElasticitySolver.hpp"
00034 #include "QuadraticBasisFunction.hpp"
00035 #include "LinearBasisFunction.hpp"
00036 #include "AbstractContractionModel.hpp"
00037 #include "FibreReader.hpp"
00038 
00039 #include "AbstractCardiacMechanicsSolverInterface.hpp"
00040 
00053 template<class ELASTICITY_SOLVER, unsigned DIM>
00054 class AbstractCardiacMechanicsSolver : public ELASTICITY_SOLVER, public AbstractCardiacMechanicsSolverInterface<DIM>
00055 {
00056 protected:
00057     static const unsigned NUM_VERTICES_PER_ELEMENT = ELASTICITY_SOLVER::NUM_VERTICES_PER_ELEMENT; 
00064     std::vector<AbstractContractionModel*> mContractionModelSystems;
00065 
00071     std::vector<double> mStretches;
00072 
00074     unsigned mTotalQuadPoints;
00075 
00077     double mCurrentTime;
00079     double mNextTime;
00081     double mOdeTimestep;
00082 
00084     c_matrix<double,DIM,DIM> mConstantFibreSheetDirections;
00085 
00090     std::vector<c_matrix<double,DIM,DIM> >* mpVariableFibreSheetDirections;
00091 
00096     bool mFibreSheetDirectionsDefinedByQuadraturePoint;
00097 
00099     c_matrix<double,DIM,DIM>* mpCurrentElementFibreSheetMatrix;
00101     c_vector<double,DIM> mCurrentElementFibreDirection;
00102 
00107     virtual bool IsImplicitSolver()=0;
00108 
00127     void ComputeStressAndStressDerivative(AbstractMaterialLaw<DIM>* pMaterialLaw,
00128                                           c_matrix<double,DIM,DIM>& rC,
00129                                           c_matrix<double,DIM,DIM>& rInvC,
00130                                           double pressure,
00131                                           unsigned elementIndex,
00132                                           unsigned currentQuadPointGlobalIndex,
00133                                           c_matrix<double,DIM,DIM>& rT,
00134                                           FourthOrderTensor<DIM,DIM,DIM,DIM>& rDTdE,
00135                                           bool computeDTdE);
00136 
00137 
00138 
00139 
00152     virtual void GetActiveTensionAndTensionDerivs(double currentFibreStretch,
00153                                                   unsigned currentQuadPointGlobalIndex,
00154                                                   bool assembleJacobian,
00155                                                   double& rActiveTension,
00156                                                   double& rDerivActiveTensionWrtLambda,
00157                                                   double& rDerivActiveTensionWrtDLambdaDt)=0;
00158 public:
00166     AbstractCardiacMechanicsSolver(QuadraticMesh<DIM>& rQuadMesh,
00167                                    SolidMechanicsProblemDefinition<DIM>& rProblemDefinition,
00168                                    std::string outputDirectory);
00169 
00173     ~AbstractCardiacMechanicsSolver();
00174 
00175 
00177     unsigned GetTotalNumQuadPoints()
00178     {
00179         return mTotalQuadPoints;
00180     }
00181 
00183     virtual GaussianQuadratureRule<DIM>* GetQuadratureRule()
00184     {
00185         return this->mpQuadratureRule;
00186     }
00187 
00188 
00195     void SetConstantFibreSheetDirections(const c_matrix<double,DIM,DIM>& rFibreSheetMatrix);
00196 
00208     void SetVariableFibreSheetDirections(std::string orthoFile, bool definedPerQuadraturePoint);
00209 
00210 
00211 
00224     void SetCalciumAndVoltage(std::vector<double>& rCalciumConcentrations,
00225                               std::vector<double>& rVoltages);
00226 
00234     virtual void Solve(double time, double nextTime, double odeTimestep)=0;
00235 
00236 
00237 
00257     void ComputeDeformationGradientAndStretchInEachElement(std::vector<c_matrix<double,DIM,DIM> >& rDeformationGradients,
00258                                                            std::vector<double>& rStretches);
00259 };
00260 
00261 
00262 template<class ELASTICITY_SOLVER,unsigned DIM>
00263 AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::AbstractCardiacMechanicsSolver(QuadraticMesh<DIM>& rQuadMesh,
00264                                                                                       SolidMechanicsProblemDefinition<DIM>& rProblemDefinition,
00265                                                                                       std::string outputDirectory)
00266    : ELASTICITY_SOLVER(rQuadMesh,
00267                        rProblemDefinition,
00268                        outputDirectory),
00269      mCurrentTime(DBL_MAX),
00270      mNextTime(DBL_MAX),
00271      mOdeTimestep(DBL_MAX)
00272 {
00273     // compute total num quad points
00274     mTotalQuadPoints = rQuadMesh.GetNumElements()*this->mpQuadratureRule->GetNumQuadPoints();
00275 
00276     // initialise the store of fibre stretches
00277     mStretches.resize(mTotalQuadPoints, 1.0);
00278 
00279     // initialise fibre/sheet direction matrix to be the identity, fibres in X-direction, and sheet in XY-plane
00280     mConstantFibreSheetDirections = zero_matrix<double>(DIM,DIM);
00281     for(unsigned i=0; i<DIM; i++)
00282     {
00283         mConstantFibreSheetDirections(i,i) = 1.0;
00284     }
00285 
00286     mpVariableFibreSheetDirections = NULL;
00287 }
00288 
00289 
00290 template<class ELASTICITY_SOLVER,unsigned DIM>
00291 AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::~AbstractCardiacMechanicsSolver()
00292 {
00293     if(mpVariableFibreSheetDirections)
00294     {
00295         delete mpVariableFibreSheetDirections;
00296     }
00297 }
00298 
00299 
00300 
00301 template<class ELASTICITY_SOLVER,unsigned DIM>
00302 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::SetCalciumAndVoltage(std::vector<double>& rCalciumConcentrations,
00303                                                                                  std::vector<double>& rVoltages)
00304 
00305 {
00306     assert(rCalciumConcentrations.size() == this->mTotalQuadPoints);
00307     assert(rVoltages.size() == this->mTotalQuadPoints);
00308 
00309     ContractionModelInputParameters input_parameters;
00310 
00311     for(unsigned i=0; i<rCalciumConcentrations.size(); i++)
00312     {
00313         input_parameters.intracellularCalciumConcentration = rCalciumConcentrations[i];
00314         input_parameters.voltage = rVoltages[i];
00315 
00316         mContractionModelSystems[i]->SetInputParameters(input_parameters);
00317     }
00318 }
00319 
00320 template<class ELASTICITY_SOLVER,unsigned DIM>
00321 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::ComputeStressAndStressDerivative(AbstractMaterialLaw<DIM>* pMaterialLaw,
00322                                                                                              c_matrix<double,DIM,DIM>& rC,
00323                                                                                              c_matrix<double,DIM,DIM>& rInvC,
00324                                                                                              double pressure,
00325                                                                                              unsigned elementIndex,
00326                                                                                              unsigned currentQuadPointGlobalIndex,
00327                                                                                              c_matrix<double,DIM,DIM>& rT,
00328                                                                                              FourthOrderTensor<DIM,DIM,DIM,DIM>& rDTdE,
00329                                                                                              bool assembleJacobian)
00330 {
00331     if(!mpVariableFibreSheetDirections) // constant fibre directions
00332     {
00333         mpCurrentElementFibreSheetMatrix = &mConstantFibreSheetDirections;
00334     }
00335     else if(!mFibreSheetDirectionsDefinedByQuadraturePoint) // fibre directions defined for each mechanics mesh element
00336     {
00337         mpCurrentElementFibreSheetMatrix = &(*mpVariableFibreSheetDirections)[elementIndex];
00338     }
00339     else // fibre directions defined for each mechanics mesh quadrature point
00340     {
00341         mpCurrentElementFibreSheetMatrix = &(*mpVariableFibreSheetDirections)[currentQuadPointGlobalIndex];
00342     }
00343 
00344     for(unsigned i=0; i<DIM; i++)
00345     {
00346         mCurrentElementFibreDirection(i) = (*mpCurrentElementFibreSheetMatrix)(i,0);
00347     }
00348 
00349     // 1. Compute T and dTdE for the PASSIVE part of the strain energy.
00350     pMaterialLaw->SetChangeOfBasisMatrix(*mpCurrentElementFibreSheetMatrix);
00351     pMaterialLaw->ComputeStressAndStressDerivative(rC,rInvC,pressure,rT,rDTdE,assembleJacobian);
00352 
00353     // 2. Compute the active tension and add to the stress and stress-derivative
00354     double I4 = inner_prod(mCurrentElementFibreDirection, prod(rC, mCurrentElementFibreDirection));
00355     double lambda = sqrt(I4);
00356 
00357     double active_tension = 0;
00358     double d_act_tension_dlam = 0.0;     // Set and used if assembleJacobian==true
00359     double d_act_tension_d_dlamdt = 0.0; // Set and used if assembleJacobian==true
00360 
00361     GetActiveTensionAndTensionDerivs(lambda, currentQuadPointGlobalIndex, assembleJacobian,
00362                                      active_tension, d_act_tension_dlam, d_act_tension_d_dlamdt);
00363 
00364     // amend the stress and dTdE using the active tension
00365     double dTdE_coeff = -2*active_tension/(I4*I4); // note: I4*I4 = lam^4
00366     if(IsImplicitSolver())
00367     {
00368         double dt = mNextTime-mCurrentTime;
00369         dTdE_coeff += (d_act_tension_dlam + d_act_tension_d_dlamdt/dt)/(lambda*I4); // note: I4*lam = lam^3
00370     }
00371 
00372     rT += (active_tension/I4)*outer_prod(mCurrentElementFibreDirection,mCurrentElementFibreDirection);
00373 
00374     if(assembleJacobian)
00375     {
00376         for (unsigned M=0; M<DIM; M++)
00377         {
00378             for (unsigned N=0; N<DIM; N++)
00379             {
00380                 for (unsigned P=0; P<DIM; P++)
00381                 {
00382                     for (unsigned Q=0; Q<DIM; Q++)
00383                     {
00384                         rDTdE(M,N,P,Q) +=  dTdE_coeff * mCurrentElementFibreDirection(M)
00385                                                       * mCurrentElementFibreDirection(N)
00386                                                       * mCurrentElementFibreDirection(P)
00387                                                       * mCurrentElementFibreDirection(Q);
00388                     }
00389                 }
00390             }
00391         }
00392     }
00393 }
00394 
00395 
00396 
00397 
00398 template<class ELASTICITY_SOLVER,unsigned DIM>
00399 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::ComputeDeformationGradientAndStretchInEachElement(
00400     std::vector<c_matrix<double,DIM,DIM> >& rDeformationGradients,
00401     std::vector<double>& rStretches)
00402 {
00403     assert(rStretches.size()==this->mrQuadMesh.GetNumElements());
00404 
00405     // this will only work currently if the coarse mesh fibre info is defined per element, not per quad point
00406     assert(!mpVariableFibreSheetDirections || !mFibreSheetDirectionsDefinedByQuadraturePoint);
00407 
00408     static c_matrix<double,DIM,NUM_VERTICES_PER_ELEMENT> element_current_displacements;
00409     static c_matrix<double,DIM,NUM_VERTICES_PER_ELEMENT> grad_lin_phi;
00410     static c_matrix<double,DIM,DIM> F;      // the deformation gradient, F = dx/dX, F_{iM} = dx_i/dX_M
00411 
00412     static c_matrix<double,DIM,DIM> jacobian;
00413     static c_matrix<double,DIM,DIM> inverse_jacobian;
00414     double jacobian_determinant;
00415     ChastePoint<DIM> quadrature_point; // not needed, but has to be passed in
00416 
00417     // loop over all the elements
00418     for(unsigned elem_index=0; elem_index<this->mrQuadMesh.GetNumElements(); elem_index++)
00419     {
00420         Element<DIM,DIM>* p_elem = this->mrQuadMesh.GetElement(elem_index);
00421 
00422         // get the fibre direction for this element
00423         mpCurrentElementFibreSheetMatrix = mpVariableFibreSheetDirections ? &(*mpVariableFibreSheetDirections)[elem_index] : &mConstantFibreSheetDirections;
00424         for(unsigned i=0; i<DIM; i++)
00425         {
00426             mCurrentElementFibreDirection(i) = (*mpCurrentElementFibreSheetMatrix)(i,0);
00427         }
00428 
00429         // get the displacement at this element
00430         for (unsigned II=0; II<NUM_VERTICES_PER_ELEMENT; II++)
00431         {
00432             for (unsigned JJ=0; JJ<DIM; JJ++)
00433             {
00434                 element_current_displacements(JJ,II) = this->mCurrentSolution[DIM*p_elem->GetNodeGlobalIndex(II) + JJ];
00435             }
00436         }
00437 
00438         // set up the linear basis functions
00439         this->mrQuadMesh.GetInverseJacobianForElement(elem_index, jacobian, jacobian_determinant, inverse_jacobian);
00440         LinearBasisFunction<DIM>::ComputeTransformedBasisFunctionDerivatives(quadrature_point, inverse_jacobian, grad_lin_phi);
00441 
00442         F = identity_matrix<double>(DIM,DIM);
00443 
00444         // loop over the vertices and interpolate F, the deformation gradient
00445         // (note: could use matrix-mult if this becomes inefficient
00446         for (unsigned node_index=0; node_index<NUM_VERTICES_PER_ELEMENT; node_index++)
00447         {
00448             for (unsigned i=0; i<DIM; i++)
00449             {
00450                 for (unsigned M=0; M<DIM; M++)
00451                 {
00452                     F(i,M) += grad_lin_phi(M,node_index)*element_current_displacements(i,node_index);
00453                 }
00454             }
00455         }
00456 
00457         rDeformationGradients[elem_index] = F;
00458 
00459         // compute and save the stretch: m^T C m = ||Fm||
00460         c_vector<double,DIM> deformed_fibre = prod(F, mCurrentElementFibreDirection);
00461         rStretches[elem_index] = norm_2(deformed_fibre);
00462     }
00463 }
00464 
00465 
00466 
00467 
00468 template<class ELASTICITY_SOLVER,unsigned DIM>
00469 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::SetVariableFibreSheetDirections(std::string orthoFile, bool definedPerQuadraturePoint)
00470 {
00471     mFibreSheetDirectionsDefinedByQuadraturePoint = definedPerQuadraturePoint;
00472 
00473     FileFinder finder(orthoFile, RelativeTo::ChasteSourceRoot);
00474     FibreReader<DIM> reader(finder, ORTHO);
00475 
00476     unsigned num_entries = reader.GetNumLinesOfData();
00477 
00478     if(!mFibreSheetDirectionsDefinedByQuadraturePoint && (num_entries!=this->mrQuadMesh.GetNumElements()) )
00479     {
00480         EXCEPTION("Number of entries defined at top of file " << orthoFile << " does not match number of elements in the mesh, "
00481            << "found " <<  num_entries << ", expected " << this->mrQuadMesh.GetNumElements());
00482     }
00483 
00484     if(mFibreSheetDirectionsDefinedByQuadraturePoint && (num_entries!=mTotalQuadPoints) )
00485     {
00486         EXCEPTION("Number of entries defined at top of file " << orthoFile << " does not match number of quadrature points defined, "
00487            << "found " <<  num_entries << ", expected " << mTotalQuadPoints);
00488     }
00489 
00490     mpVariableFibreSheetDirections = new std::vector<c_matrix<double,DIM,DIM> >(num_entries, zero_matrix<double>(DIM,DIM));
00491     for(unsigned index=0; index<num_entries; index++)
00492     {
00493         reader.GetNextFibreSheetAndNormalMatrix( (*mpVariableFibreSheetDirections)[index] );
00494     }
00495 }
00496 
00497 
00498 
00499 template<class ELASTICITY_SOLVER,unsigned DIM>
00500 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::SetConstantFibreSheetDirections(const c_matrix<double,DIM,DIM>& rFibreSheetMatrix)
00501 {
00502     mConstantFibreSheetDirections = rFibreSheetMatrix;
00503     // check orthogonality
00504     c_matrix<double,DIM,DIM>  temp = prod(trans(rFibreSheetMatrix),rFibreSheetMatrix);
00505     for(unsigned i=0; i<DIM; i++)
00506     {
00507         for(unsigned j=0; j<DIM; j++)
00508         {
00509             double val = (i==j ? 1.0 : 0.0);
00510             if(fabs(temp(i,j)-val)>1e-4)
00511             {
00512                 EXCEPTION("The given fibre-sheet matrix, " << rFibreSheetMatrix << ", is not orthogonal");
00513             }
00514         }
00515     }
00516 }
00517 
00518 
00519 #endif /*ABSTRACTCARDIACMECHANICSSOLVER_HPP_*/
Generated on Thu Dec 22 13:00:06 2011 for Chaste by  doxygen 1.6.3