Chaste Release::3.1
AbstractCardiacMechanicsSolver.hpp
00001 /*
00002 
00003 Copyright (c) 2005-2012, 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 #ifndef ABSTRACTCARDIACMECHANICSSOLVER_HPP_
00037 #define ABSTRACTCARDIACMECHANICSSOLVER_HPP_
00038 
00039 #include <map>
00040 #include "IncompressibleNonlinearElasticitySolver.hpp"
00041 #include "CompressibleNonlinearElasticitySolver.hpp"
00042 #include "QuadraticBasisFunction.hpp"
00043 #include "LinearBasisFunction.hpp"
00044 #include "AbstractContractionModel.hpp"
00045 #include "FibreReader.hpp"
00046 #include "FineCoarseMeshPair.hpp"
00047 #include "AbstractCardiacMechanicsSolverInterface.hpp"
00048 #include "HeartRegionCodes.hpp"
00049 #include "ElectroMechanicsProblemDefinition.hpp"
00050 
00051 
00058 typedef struct DataAtQuadraturePoint_
00059 {
00060     AbstractContractionModel* ContractionModel; 
00061     bool Active;
00062     double Stretch; 
00063     double StretchLastTimeStep; 
00065 } DataAtQuadraturePoint;
00066 
00067 
00080 template<class ELASTICITY_SOLVER, unsigned DIM>
00081 class AbstractCardiacMechanicsSolver : public ELASTICITY_SOLVER, public AbstractCardiacMechanicsSolverInterface<DIM>
00082 {
00083 protected:
00084     static const unsigned NUM_VERTICES_PER_ELEMENT = ELASTICITY_SOLVER::NUM_VERTICES_PER_ELEMENT; 
00097     std::map<unsigned,DataAtQuadraturePoint> mQuadPointToDataAtQuadPointMap;
00098 
00102     std::map<unsigned,DataAtQuadraturePoint>::iterator mMapIterator;
00103 
00105     ContractionModelName mContractionModelName;
00106 
00108     FineCoarseMeshPair<DIM>* mpMeshPair;
00109 
00111     unsigned mTotalQuadPoints;
00112 
00114     double mCurrentTime;
00116     double mNextTime;
00118     double mOdeTimestep;
00119 
00121     c_matrix<double,DIM,DIM> mConstantFibreSheetDirections;
00122 
00127     std::vector<c_matrix<double,DIM,DIM> >* mpVariableFibreSheetDirections;
00128 
00133     bool mFibreSheetDirectionsDefinedByQuadraturePoint;
00134 
00136     c_vector<double,DIM> mCurrentElementFibreDirection;
00137 
00139     c_vector<double,DIM> mCurrentElementSheetDirection;
00140 
00142     c_vector<double,DIM> mCurrentElementSheetNormalDirection;
00143 
00144 
00148     ElectroMechanicsProblemDefinition<DIM>& mrElectroMechanicsProblemDefinition;
00149 
00154     virtual bool IsImplicitSolver()=0;
00155 
00156 
00171     void AddActiveStressAndStressDerivative(c_matrix<double,DIM,DIM>& rC,
00172                                             unsigned elementIndex,
00173                                             unsigned currentQuadPointGlobalIndex,
00174                                             c_matrix<double,DIM,DIM>& rT,
00175                                             FourthOrderTensor<DIM,DIM,DIM,DIM>& rDTdE,
00176                                             bool addToDTdE);
00177 
00178 
00186     void SetupChangeOfBasisMatrix(unsigned elementIndex, unsigned currentQuadPointGlobalIndex);
00187 
00193     void Initialise();
00194 
00200     virtual void InitialiseContractionModels(ContractionModelName contractionModelName) = 0;
00201 
00202 
00215     virtual void GetActiveTensionAndTensionDerivs(double currentFibreStretch,
00216                                                   unsigned currentQuadPointGlobalIndex,
00217                                                   bool assembleJacobian,
00218                                                   double& rActiveTension,
00219                                                   double& rDerivActiveTensionWrtLambda,
00220                                                   double& rDerivActiveTensionWrtDLambdaDt)=0;
00221 public:
00230     AbstractCardiacMechanicsSolver(QuadraticMesh<DIM>& rQuadMesh,
00231                                    ContractionModelName contractionModelName,
00232                                    ElectroMechanicsProblemDefinition<DIM>& rProblemDefinition,
00233                                    std::string outputDirectory);
00234 
00238     ~AbstractCardiacMechanicsSolver();
00239 
00247     void SetFineCoarseMeshPair(FineCoarseMeshPair<DIM>* pMeshPair);
00248 
00250     unsigned GetTotalNumQuadPoints()
00251     {
00252         return mTotalQuadPoints;
00253     }
00254 
00256     virtual GaussianQuadratureRule<DIM>* GetQuadratureRule()
00257     {
00258         return this->mpQuadratureRule;
00259     }
00260 
00264     std::map<unsigned,DataAtQuadraturePoint>& rGetQuadPointToDataAtQuadPointMap()
00265     {
00266         return mQuadPointToDataAtQuadPointMap;
00267     }
00268 
00269 
00276     void SetConstantFibreSheetDirections(const c_matrix<double,DIM,DIM>& rFibreSheetMatrix);
00277 
00289     void SetVariableFibreSheetDirections(const FileFinder& rOrthoFile, bool definedPerQuadraturePoint);
00290 
00303     void SetCalciumAndVoltage(std::vector<double>& rCalciumConcentrations,
00304                               std::vector<double>& rVoltages);
00305 
00313     virtual void Solve(double time, double nextTime, double odeTimestep)=0;
00314 
00315 
00316 
00336     void ComputeDeformationGradientAndStretchInEachElement(std::vector<c_matrix<double,DIM,DIM> >& rDeformationGradients,
00337                                                            std::vector<double>& rStretches);
00338 };
00339 
00340 
00341 template<class ELASTICITY_SOLVER,unsigned DIM>
00342 AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::AbstractCardiacMechanicsSolver(QuadraticMesh<DIM>& rQuadMesh,
00343                                                                                       ContractionModelName contractionModelName,
00344                                                                                       ElectroMechanicsProblemDefinition<DIM>& rProblemDefinition,
00345                                                                                       std::string outputDirectory)
00346    : ELASTICITY_SOLVER(rQuadMesh,
00347                        rProblemDefinition,
00348                        outputDirectory),
00349      mContractionModelName(contractionModelName),
00350      mpMeshPair(NULL),
00351      mCurrentTime(DBL_MAX),
00352      mNextTime(DBL_MAX),
00353      mOdeTimestep(DBL_MAX),
00354      mrElectroMechanicsProblemDefinition(rProblemDefinition)
00355 {
00356 
00357 }
00358 
00359 template<class ELASTICITY_SOLVER,unsigned DIM>
00360 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::Initialise()
00361 {
00362     // compute total num quad points
00363     unsigned num_quad_pts_per_element = this->mpQuadratureRule->GetNumQuadPoints();
00364     mTotalQuadPoints = this->mrQuadMesh.GetNumElements()*num_quad_pts_per_element;
00365 
00366     std::vector<ElementAndWeights<DIM> > fine_elements = mpMeshPair->rGetElementsAndWeights();
00367     assert(fine_elements.size()==mTotalQuadPoints);
00368     assert(mpMeshPair!=NULL);
00369 
00370     for (typename AbstractTetrahedralMesh<DIM, DIM>::ElementIterator iter = this->mrQuadMesh.GetElementIteratorBegin();
00371          iter != this->mrQuadMesh.GetElementIteratorEnd();
00372          ++iter)
00373     {
00374         Element<DIM, DIM>& element = *iter;
00375 
00376         if (element.GetOwnership() == true)
00377         {
00378             for(unsigned j=0; j<num_quad_pts_per_element; j++)
00379             {
00380                 unsigned quad_pt_global_index = element.GetIndex()*num_quad_pts_per_element + j;
00381 
00382                 DataAtQuadraturePoint data_at_quad_point;
00383                 data_at_quad_point.ContractionModel = NULL;
00384                 data_at_quad_point.Stretch = 1.0;
00385                 data_at_quad_point.StretchLastTimeStep = 1.0;
00386 
00387                    if (mpMeshPair->GetFineMesh().GetElement(fine_elements[quad_pt_global_index].ElementNum)->GetUnsignedAttribute() == HeartRegionCode::GetValidBathId())
00388                 {
00389                     data_at_quad_point.Active = false;//bath
00390                 }
00391                 else
00392                 {
00393                     data_at_quad_point.Active = true;//tissue
00394                 }
00395                 mQuadPointToDataAtQuadPointMap[quad_pt_global_index] = data_at_quad_point;
00396             }
00397         }
00398     }
00399 
00400     // initialise the iterator to point at the beginning
00401     mMapIterator = mQuadPointToDataAtQuadPointMap.begin();
00402 
00403     // initialise fibre/sheet direction matrix to be the identity, fibres in X-direction, and sheet in XY-plane
00404     mConstantFibreSheetDirections = zero_matrix<double>(DIM,DIM);
00405     for(unsigned i=0; i<DIM; i++)
00406     {
00407         mConstantFibreSheetDirections(i,i) = 1.0;
00408     }
00409 
00410     mpVariableFibreSheetDirections = NULL;
00411 
00412     InitialiseContractionModels(mContractionModelName);
00413 }
00414 
00415 template<class ELASTICITY_SOLVER,unsigned DIM>
00416 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::SetFineCoarseMeshPair(FineCoarseMeshPair<DIM>* pMeshPair)
00417 {
00418     assert(pMeshPair!=NULL);
00419     if (pMeshPair->GetCoarseMesh().GetNumElements() != this->mrQuadMesh.GetNumElements())
00420     {
00421         EXCEPTION("When setting a mesh pair into the solver, the coarse mesh of the mesh pair must be the same as the quadratic mesh");
00422     }
00423     mpMeshPair = pMeshPair;
00424 }
00425 
00426 template<class ELASTICITY_SOLVER,unsigned DIM>
00427 AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::~AbstractCardiacMechanicsSolver()
00428 {
00429     for(std::map<unsigned,DataAtQuadraturePoint>::iterator iter = mQuadPointToDataAtQuadPointMap.begin();
00430         iter != mQuadPointToDataAtQuadPointMap.end();
00431         iter++)
00432     {
00433         delete iter->second.ContractionModel;
00434     }
00435 
00436     if(mpVariableFibreSheetDirections)
00437     {
00438         delete mpVariableFibreSheetDirections;
00439     }
00440 }
00441 
00442 
00443 
00444 template<class ELASTICITY_SOLVER,unsigned DIM>
00445 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::SetCalciumAndVoltage(std::vector<double>& rCalciumConcentrations,
00446                                                                                  std::vector<double>& rVoltages)
00447 
00448 {
00449     assert(rCalciumConcentrations.size() == mTotalQuadPoints);
00450     assert(rVoltages.size() == mTotalQuadPoints);
00451 
00452     ContractionModelInputParameters input_parameters;
00453 
00454     for(unsigned i=0; i<rCalciumConcentrations.size(); i++)
00455     {
00456         input_parameters.intracellularCalciumConcentration = rCalciumConcentrations[i];
00457         input_parameters.voltage = rVoltages[i];
00458 
00460         std::map<unsigned,DataAtQuadraturePoint>::iterator iter = mQuadPointToDataAtQuadPointMap.find(i);
00461         if(iter != mQuadPointToDataAtQuadPointMap.end())
00462         {
00463             iter->second.ContractionModel->SetInputParameters(input_parameters);
00464         }
00465     }
00466 }
00467 
00468 
00469 template<class ELASTICITY_SOLVER,unsigned DIM>
00470 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::SetupChangeOfBasisMatrix(unsigned elementIndex,
00471                                                                                      unsigned currentQuadPointGlobalIndex)
00472 {
00473     if(!mpVariableFibreSheetDirections) // constant fibre directions
00474     {
00475         this->mChangeOfBasisMatrix = mConstantFibreSheetDirections;
00476     }
00477     else if(!mFibreSheetDirectionsDefinedByQuadraturePoint) // fibre directions defined for each mechanics mesh element
00478     {
00479         this->mChangeOfBasisMatrix = (*mpVariableFibreSheetDirections)[elementIndex];
00480     }
00481     else // fibre directions defined for each mechanics mesh quadrature point
00482     {
00483         this->mChangeOfBasisMatrix = (*mpVariableFibreSheetDirections)[currentQuadPointGlobalIndex];
00484     }
00485 }
00486 
00487 
00488 
00489 template<class ELASTICITY_SOLVER,unsigned DIM>
00490 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::AddActiveStressAndStressDerivative(c_matrix<double,DIM,DIM>& rC,
00491                                                                                                unsigned elementIndex,
00492                                                                                                unsigned currentQuadPointGlobalIndex,
00493                                                                                                c_matrix<double,DIM,DIM>& rT,
00494                                                                                                FourthOrderTensor<DIM,DIM,DIM,DIM>& rDTdE,
00495                                                                                                bool addToDTdE)
00496 {
00497     for(unsigned i=0; i<DIM; i++)
00498     {
00499         mCurrentElementFibreDirection(i) = this->mChangeOfBasisMatrix(i,0);
00500     }
00501 
00502     //Compute the active tension and add to the stress and stress-derivative
00503     double I4_fibre = inner_prod(mCurrentElementFibreDirection, prod(rC, mCurrentElementFibreDirection));
00504     double lambda_fibre = sqrt(I4_fibre);
00505 
00506     double active_tension = 0;
00507     double d_act_tension_dlam = 0.0;     // Set and used if assembleJacobian==true
00508     double d_act_tension_d_dlamdt = 0.0; // Set and used if assembleJacobian==true
00509 
00510     GetActiveTensionAndTensionDerivs(lambda_fibre, currentQuadPointGlobalIndex, addToDTdE,
00511                                      active_tension, d_act_tension_dlam, d_act_tension_d_dlamdt);
00512 
00513 
00514     double detF = sqrt(Determinant(rC));
00515     rT += (active_tension*detF/I4_fibre)*outer_prod(mCurrentElementFibreDirection,mCurrentElementFibreDirection);
00516 
00517     // amend the stress and dTdE using the active tension
00518     double dTdE_coeff1 = -2*active_tension*detF/(I4_fibre*I4_fibre); // note: I4_fibre*I4_fibre = lam^4
00519     double dTdE_coeff2 = active_tension*detF/I4_fibre;
00520     double dTdE_coeff_s1 = 0.0; // only set non-zero if we apply cross fibre tension
00521     double dTdE_coeff_s2 = 0.0; // only set non-zero if we apply cross fibre tension
00522     double dTdE_coeff_s3 = 0.0; // only set non-zero if we apply cross fibre tension and implicit
00523 
00524     if(IsImplicitSolver())
00525     {
00526         double dt = mNextTime-mCurrentTime;
00527         //std::cout << "d sigma / d lamda = " << d_act_tension_dlam << ", d sigma / d lamdat = " << d_act_tension_d_dlamdt << "\n" << std::flush;
00528         dTdE_coeff1 += (d_act_tension_dlam + d_act_tension_d_dlamdt/dt)*detF/(lambda_fibre*I4_fibre); // note: I4_fibre*lam = lam^3
00529     }
00530 
00531     bool apply_cross_fibre_tension = (this->mrElectroMechanicsProblemDefinition.GetApplyCrossFibreTension()) && (DIM > 1);
00532     if(apply_cross_fibre_tension)
00533     {
00534         double cross_fraction = mrElectroMechanicsProblemDefinition.GetCrossFibreTensionFraction();
00535 
00536         for(unsigned i=0; i<DIM; i++)
00537         {
00538             mCurrentElementSheetDirection(i) = this->mChangeOfBasisMatrix(i,1);
00539         }
00540 
00541         double I4_sheet = inner_prod(mCurrentElementSheetDirection, prod(rC, mCurrentElementSheetDirection));
00542 
00543         // amend the stress and dTdE using the active tension
00544         dTdE_coeff_s1 = -2*cross_fraction*detF*active_tension/(I4_sheet*I4_sheet); // note: I4*I4 = lam^4
00545 
00546         if(IsImplicitSolver())
00547         {
00548            double dt = mNextTime-mCurrentTime;
00549            dTdE_coeff_s3 = cross_fraction*(d_act_tension_dlam + d_act_tension_d_dlamdt/dt)*detF/(lambda_fibre*I4_sheet); // note: I4*lam = lam^3
00550         }
00551 
00552         rT += cross_fraction*(active_tension*detF/I4_sheet)*outer_prod(mCurrentElementSheetDirection,mCurrentElementSheetDirection);
00553 
00554         dTdE_coeff_s2 = active_tension*cross_fraction*detF/I4_sheet;
00555     }
00556 
00557 
00558     if(addToDTdE)
00559     {
00560         c_matrix<double,DIM,DIM> invC = Inverse(rC);
00561 
00562         for (unsigned M=0; M<DIM; M++)
00563         {
00564             for (unsigned N=0; N<DIM; N++)
00565             {
00566                 for (unsigned P=0; P<DIM; P++)
00567                 {
00568                     for (unsigned Q=0; Q<DIM; Q++)
00569                     {
00570                         rDTdE(M,N,P,Q) +=   dTdE_coeff1 * mCurrentElementFibreDirection(M)
00571                                                         * mCurrentElementFibreDirection(N)
00572                                                         * mCurrentElementFibreDirection(P)
00573                                                         * mCurrentElementFibreDirection(Q)
00574 
00575                                          +  dTdE_coeff2 * mCurrentElementFibreDirection(M)
00576                                                         * mCurrentElementFibreDirection(N)
00577                                                         * invC(P,Q);
00578                         if(apply_cross_fibre_tension)
00579                         {
00580                             rDTdE(M,N,P,Q) += dTdE_coeff_s1 * mCurrentElementSheetDirection(M)
00581                                                             * mCurrentElementSheetDirection(N)
00582                                                             * mCurrentElementSheetDirection(P)
00583                                                             * mCurrentElementSheetDirection(Q)
00584 
00585                                            +  dTdE_coeff_s2 * mCurrentElementSheetDirection(M)
00586                                                             * mCurrentElementSheetDirection(N)
00587                                                             * invC(P,Q)
00588 
00589                                            + dTdE_coeff_s3 * mCurrentElementSheetDirection(M)
00590                                                            * mCurrentElementSheetDirection(N)
00591                                                            * mCurrentElementFibreDirection(P)
00592                                                            * mCurrentElementFibreDirection(Q);
00593                         }
00594                     }
00595                 }
00596             }
00597         }
00598     }
00599 
00600 //    ///\todo #2180 The code below applies a cross fibre tension in the 2D case. Things that need doing:
00601 //    // * Refactor the common code between the block below and the block above to avoid duplication.
00602 //    // * Handle the 3D case.
00603 //    if(this->mrElectroMechanicsProblemDefinition.GetApplyCrossFibreTension() && DIM > 1)
00604 //    {
00605 //        double cross_fraction = mrElectroMechanicsProblemDefinition.GetCrossFibreTensionFraction();
00606 //
00607 //        for(unsigned i=0; i<DIM; i++)
00608 //        {
00609 //            mCurrentElementSheetDirection(i) = this->mChangeOfBasisMatrix(i,1);
00610 //        }
00611 //
00612 //        double I4_sheet = inner_prod(mCurrentElementSheetDirection, prod(rC, mCurrentElementSheetDirection));
00613 //
00614 //        // amend the stress and dTdE using the active tension
00615 //        double dTdE_coeff_s1 = -2*cross_fraction*detF*active_tension/(I4_sheet*I4_sheet); // note: I4*I4 = lam^4
00616 //
00617 //        ///\todo #2180 The code below is specific to the implicit cardiac mechanics solver. Currently
00618 //        // the cross-fibre code is only tested using the explicit solver so the code below fails coverage.
00619 //        // This will need to be added back in once an implicit test is in place.
00620 //        double lambda_sheet = sqrt(I4_sheet);
00621 //        if(IsImplicitSolver())
00622 //        {
00623 //           double dt = mNextTime-mCurrentTime;
00624 //           dTdE_coeff_s1 += (d_act_tension_dlam + d_act_tension_d_dlamdt/dt)/(lambda_sheet*I4_sheet); // note: I4*lam = lam^3
00625 //        }
00626 //
00627 //        rT += cross_fraction*(active_tension*detF/I4_sheet)*outer_prod(mCurrentElementSheetDirection,mCurrentElementSheetDirection);
00628 //
00629 //        double dTdE_coeff_s2 = active_tension*detF/I4_sheet;
00630 //        if(addToDTdE)
00631 //        {
00632 //           for (unsigned M=0; M<DIM; M++)
00633 //           {
00634 //               for (unsigned N=0; N<DIM; N++)
00635 //               {
00636 //                   for (unsigned P=0; P<DIM; P++)
00637 //                   {
00638 //                       for (unsigned Q=0; Q<DIM; Q++)
00639 //                       {
00640 //                           rDTdE(M,N,P,Q) +=  dTdE_coeff_s1 * mCurrentElementSheetDirection(M)
00641 //                                                            * mCurrentElementSheetDirection(N)
00642 //                                                            * mCurrentElementSheetDirection(P)
00643 //                                                            * mCurrentElementSheetDirection(Q)
00644 //
00645 //                                           +  dTdE_coeff_s2 * mCurrentElementFibreDirection(M)
00646 //                                                            * mCurrentElementFibreDirection(N)
00647 //                                                            * invC(P,Q);
00648 //
00649 //                       }
00650 //                   }
00651 //               }
00652 //           }
00653 //        }
00654 //    }
00655 }
00656 
00657 
00658 
00659 
00660 template<class ELASTICITY_SOLVER,unsigned DIM>
00661 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::ComputeDeformationGradientAndStretchInEachElement(
00662     std::vector<c_matrix<double,DIM,DIM> >& rDeformationGradients,
00663     std::vector<double>& rStretches)
00664 {
00665     assert(rStretches.size()==this->mrQuadMesh.GetNumElements());
00666 
00667     // this will only work currently if the coarse mesh fibre info is defined per element, not per quad point
00668     assert(!mpVariableFibreSheetDirections || !mFibreSheetDirectionsDefinedByQuadraturePoint);
00669 
00670     static c_matrix<double,DIM,NUM_VERTICES_PER_ELEMENT> element_current_displacements;
00671     static c_matrix<double,DIM,NUM_VERTICES_PER_ELEMENT> grad_lin_phi;
00672     static c_matrix<double,DIM,DIM> F;      // the deformation gradient, F = dx/dX, F_{iM} = dx_i/dX_M
00673 
00674     static c_matrix<double,DIM,DIM> jacobian;
00675     static c_matrix<double,DIM,DIM> inverse_jacobian;
00676     double jacobian_determinant;
00677     ChastePoint<DIM> quadrature_point; // not needed, but has to be passed in
00678 
00679     // loop over all the elements
00680     for(unsigned elem_index=0; elem_index<this->mrQuadMesh.GetNumElements(); elem_index++)
00681     {
00682         Element<DIM,DIM>* p_elem = this->mrQuadMesh.GetElement(elem_index);
00683 
00684         // get the fibre direction for this element
00685         SetupChangeOfBasisMatrix(elem_index, 0); // 0 is quad index, and doesn't matter as checked that fibres not defined by quad pt above.
00686         for(unsigned i=0; i<DIM; i++)
00687         {
00688             mCurrentElementFibreDirection(i) = this->mChangeOfBasisMatrix(i,0);
00689         }
00690 
00691         // get the displacement at this element
00692         for (unsigned II=0; II<NUM_VERTICES_PER_ELEMENT; II++)
00693         {
00694             for (unsigned JJ=0; JJ<DIM; JJ++)
00695             {
00696                 // mProblemDimension = DIM for compressible elasticity and DIM+1 for incompressible elasticity
00697                 element_current_displacements(JJ,II) = this->mCurrentSolution[this->mProblemDimension*p_elem->GetNodeGlobalIndex(II) + JJ];
00698             }
00699         }
00700 
00701         // set up the linear basis functions
00702         this->mrQuadMesh.GetInverseJacobianForElement(elem_index, jacobian, jacobian_determinant, inverse_jacobian);
00703         LinearBasisFunction<DIM>::ComputeTransformedBasisFunctionDerivatives(quadrature_point, inverse_jacobian, grad_lin_phi);
00704 
00705         F = identity_matrix<double>(DIM,DIM);
00706 
00707         // loop over the vertices and interpolate F, the deformation gradient
00708         // (note: could use matrix-mult if this becomes inefficient
00709         for (unsigned node_index=0; node_index<NUM_VERTICES_PER_ELEMENT; node_index++)
00710         {
00711             for (unsigned i=0; i<DIM; i++)
00712             {
00713                 for (unsigned M=0; M<DIM; M++)
00714                 {
00715                     F(i,M) += grad_lin_phi(M,node_index)*element_current_displacements(i,node_index);
00716                 }
00717             }
00718         }
00719 
00720         rDeformationGradients[elem_index] = F;
00721 
00722         // compute and save the stretch: m^T C m = ||Fm||
00723         c_vector<double,DIM> deformed_fibre = prod(F, mCurrentElementFibreDirection);
00724         rStretches[elem_index] = norm_2(deformed_fibre);
00725     }
00726 }
00727 
00728 
00729 
00730 
00731 template<class ELASTICITY_SOLVER,unsigned DIM>
00732 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::SetVariableFibreSheetDirections(const FileFinder& rOrthoFile, bool definedPerQuadraturePoint)
00733 {
00734     mFibreSheetDirectionsDefinedByQuadraturePoint = definedPerQuadraturePoint;
00735 
00736     FibreReader<DIM> reader(rOrthoFile, ORTHO);
00737 
00738     unsigned num_entries = reader.GetNumLinesOfData();
00739 
00740     if (!mFibreSheetDirectionsDefinedByQuadraturePoint && (num_entries!=this->mrQuadMesh.GetNumElements()) )
00741     {
00742         EXCEPTION("Number of entries defined at top of file " << rOrthoFile.GetAbsolutePath() <<
00743                   " does not match number of elements in the mesh, " << "found " <<  num_entries <<
00744                   ", expected " << this->mrQuadMesh.GetNumElements());
00745     }
00746 
00747     if (mFibreSheetDirectionsDefinedByQuadraturePoint && (num_entries!=mTotalQuadPoints) )
00748     {
00749         EXCEPTION("Number of entries defined at top of file " << rOrthoFile.GetAbsolutePath() <<
00750                   " does not match number of quadrature points defined, " << "found " <<  num_entries <<
00751                   ", expected " << mTotalQuadPoints);
00752     }
00753 
00754     mpVariableFibreSheetDirections = new std::vector<c_matrix<double,DIM,DIM> >(num_entries, zero_matrix<double>(DIM,DIM));
00755     for(unsigned index=0; index<num_entries; index++)
00756     {
00757         reader.GetFibreSheetAndNormalMatrix(index, (*mpVariableFibreSheetDirections)[index] );
00758     }
00759 }
00760 
00761 
00762 
00763 template<class ELASTICITY_SOLVER,unsigned DIM>
00764 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::SetConstantFibreSheetDirections(const c_matrix<double,DIM,DIM>& rFibreSheetMatrix)
00765 {
00766     mConstantFibreSheetDirections = rFibreSheetMatrix;
00767     // check orthogonality
00768     c_matrix<double,DIM,DIM>  temp = prod(trans(rFibreSheetMatrix),rFibreSheetMatrix);
00769     for(unsigned i=0; i<DIM; i++)
00770     {
00771         for(unsigned j=0; j<DIM; j++)
00772         {
00773             double val = (i==j ? 1.0 : 0.0);
00774             if(fabs(temp(i,j)-val)>1e-4)
00775             {
00776                 EXCEPTION("The given fibre-sheet matrix, " << rFibreSheetMatrix << ", is not orthogonal");
00777             }
00778         }
00779     }
00780 }
00781 
00782 
00783 #endif /*ABSTRACTCARDIACMECHANICSSOLVER_HPP_*/