AbstractCardiacMechanicsAssembler.hpp

00001 /*
00002 
00003 Copyright (C) University of Oxford, 2005-2010
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 ABSTRACTCARDIACMECHANICSASSEMBLER_HPP_
00030 #define ABSTRACTCARDIACMECHANICSASSEMBLER_HPP_
00031 
00032 #include "NonlinearElasticityAssembler.hpp"
00033 #include "NashHunterPoleZeroLaw.hpp"
00034 #include "QuadraticBasisFunction.hpp" // not included in NonlinearElasticityAssembler.hpp, just the cpp
00035 #include "LinearBasisFunction.hpp"
00036 #include "AbstractContractionModel.hpp"
00037 
00046 template<unsigned DIM>
00047 class AbstractCardiacMechanicsAssembler : public NonlinearElasticityAssembler<DIM>
00048 {
00049 protected:
00050     static const unsigned STENCIL_SIZE = NonlinearElasticityAssembler<DIM>::STENCIL_SIZE;
00051     static const unsigned NUM_NODES_PER_ELEMENT = NonlinearElasticityAssembler<DIM>::NUM_NODES_PER_ELEMENT;
00052     static const unsigned NUM_VERTICES_PER_ELEMENT = NonlinearElasticityAssembler<DIM>::NUM_VERTICES_PER_ELEMENT;
00053 
00059     std::vector<AbstractContractionModel*> mContractionModelSystems;
00060 
00066     std::vector<double> mStretches;
00067 
00069     unsigned mTotalQuadPoints;
00070 
00072     bool mAllocatedMaterialLawMemory;
00073 
00075     double mCurrentTime;
00077     double mNextTime;
00079     double mOdeTimestep;
00080 
00082     c_matrix<double,DIM,DIM> mConstantFibreSheetDirections;
00083 
00088     std::vector<c_matrix<double,DIM,DIM> >* mpVariableFibreSheetDirections;
00089 
00094     void CheckOrthogonality(c_matrix<double,DIM,DIM>& rMatrix)
00095     {
00096         c_matrix<double,DIM,DIM>  temp = prod(trans(rMatrix),rMatrix);
00097         double tol = 1e-6;
00098         // check temp is equal to the identity
00099         for(unsigned i=0; i<DIM; i++)
00100         {
00101             for(unsigned j=0; j<DIM; j++)
00102             {
00103                 double val = (i==j ? 1.0 : 0.0);
00104                 if(fabs(temp(i,j)-val)>tol)
00105                 {
00106                     std::stringstream ss;
00107                     ss << "The given fibre-sheet matrix, " << rMatrix << ", is not orthogonal"
00108                        << " (A^T A not equal to I to tolerance " << tol << ")";
00109                     EXCEPTION(ss.str());
00110                 }
00111             }
00112         }
00113     }
00114 
00115 
00120     virtual bool IsImplicitSolver()=0;
00121 
00141     void AssembleOnElement(Element<DIM, DIM>& rElement,
00142                            c_matrix<double,STENCIL_SIZE,STENCIL_SIZE>& rAElem,
00143                            c_matrix<double,STENCIL_SIZE,STENCIL_SIZE>& rAElemPrecond,
00144                            c_vector<double,STENCIL_SIZE>& rBElem,
00145                            bool assembleResidual,
00146                            bool assembleJacobian);
00147 
00148 
00161     virtual void GetActiveTensionAndTensionDerivs(double currentFibreStretch,
00162                                                   unsigned currentQuadPointGlobalIndex,
00163                                                   bool assembleJacobian,
00164                                                   double& rActiveTension,
00165                                                   double& rDerivActiveTensionWrtLambda,
00166                                                   double& rDerivActiveTensionWrtDLambdaDt)=0;
00167 public:
00177     AbstractCardiacMechanicsAssembler(QuadraticMesh<DIM>* pQuadMesh,
00178                                       std::string outputDirectory,
00179                                       std::vector<unsigned>& rFixedNodes,
00180                                       AbstractIncompressibleMaterialLaw<DIM>* pMaterialLaw)
00181         : NonlinearElasticityAssembler<DIM>(pQuadMesh,
00182                                         pMaterialLaw!=NULL ? pMaterialLaw : new NashHunterPoleZeroLaw<DIM>,
00183                                         zero_vector<double>(DIM),
00184                                         DOUBLE_UNSET,
00185                                         outputDirectory,
00186                                         rFixedNodes),
00187         mCurrentTime(DBL_MAX),
00188         mNextTime(DBL_MAX),
00189         mOdeTimestep(DBL_MAX)
00190     {
00191         // compute total num quad points
00192         mTotalQuadPoints = pQuadMesh->GetNumElements()*this->mpQuadratureRule->GetNumQuadPoints();
00193 
00194         // note that if pMaterialLaw is NULL a new NashHunter law was sent to the
00195         // NonlinElas constuctor (see above)
00196         mAllocatedMaterialLawMemory = (pMaterialLaw==NULL);
00197 
00198         // initialise the store of fibre stretches
00199         mStretches.resize(mTotalQuadPoints, 1.0);
00200 
00201         // initialise fibre/sheet direction matrix to be the identity, fibres in X-direction, and sheet in XY-plane
00202         mConstantFibreSheetDirections = zero_matrix<double>(DIM,DIM);
00203         for(unsigned i=0; i<DIM; i++)
00204         {
00205             mConstantFibreSheetDirections(i,i) = 1.0;
00206         }
00207 
00208         mpVariableFibreSheetDirections = NULL;
00209     }
00210 
00214     ~AbstractCardiacMechanicsAssembler()
00215     {
00216         if(mAllocatedMaterialLawMemory)
00217         {
00218             assert(this->mMaterialLaws.size()==1); // haven't implemented heterogeniety yet
00219             delete this->mMaterialLaws[0];
00220         }
00221 
00222         if(mpVariableFibreSheetDirections)
00223         {
00224             delete mpVariableFibreSheetDirections;
00225         }
00226     }
00227 
00228 
00230     unsigned GetTotalNumQuadPoints()
00231     {
00232         return mTotalQuadPoints;
00233     }
00234 
00236     virtual GaussianQuadratureRule<DIM>* GetQuadratureRule()
00237     {
00238         return this->mpQuadratureRule;
00239     }
00240 
00241 
00248     void SetConstantFibreSheetDirections(const c_matrix<double,DIM,DIM>& rFibreSheetMatrix)
00249     {
00250         mConstantFibreSheetDirections = rFibreSheetMatrix;
00251         CheckOrthogonality(mConstantFibreSheetDirections);
00252     }
00253 
00260     void SetVariableFibreSheetDirections(std::string orthoFile);
00261 
00262 
00263 
00276     void SetCalciumAndVoltage(std::vector<double>& rCalciumConcentrations,
00277                               std::vector<double>& rVoltages);
00278 
00286     virtual void Solve(double time, double nextTime, double odeTimestep)=0;
00287 };
00288 
00289 
00290 
00291 template<unsigned DIM>
00292 void AbstractCardiacMechanicsAssembler<DIM>::SetCalciumAndVoltage(std::vector<double>& rCalciumConcentrations,
00293                                                                   std::vector<double>& rVoltages)
00294 
00295 {
00296     assert(rCalciumConcentrations.size() == this->mTotalQuadPoints);
00297     assert(rVoltages.size() == this->mTotalQuadPoints);
00298 
00299     ContractionModelInputParameters input_parameters;
00300 
00301     for(unsigned i=0; i<rCalciumConcentrations.size(); i++)
00302     {
00303         input_parameters.intracellularCalciumConcentration = rCalciumConcentrations[i];
00304         input_parameters.voltage = rVoltages[i];
00305 
00306         mContractionModelSystems[i]->SetInputParameters(input_parameters);
00307     }
00308 }
00309 
00310 
00311 template<unsigned DIM>
00312 void AbstractCardiacMechanicsAssembler<DIM>::AssembleOnElement(Element<DIM, DIM>& rElement,
00313                                                                c_matrix<double,STENCIL_SIZE,STENCIL_SIZE>& rAElem,
00314                                                                c_matrix<double,STENCIL_SIZE,STENCIL_SIZE>& rAElemPrecond,
00315                                                                c_vector<double,STENCIL_SIZE>& rBElem,
00316                                                                bool assembleResidual,
00317                                                                bool assembleJacobian)
00318 {
00319     // check these have been set
00320     assert(mCurrentTime != DBL_MAX);
00321     assert(mNextTime != DBL_MAX);
00322     assert(mOdeTimestep != DBL_MAX);
00323     double mech_dt = mNextTime - mCurrentTime;
00324 
00325     // set up the fibre direction for this element
00326     c_matrix<double,DIM,DIM> r_fibre_sheet_matrix = mpVariableFibreSheetDirections ? (*mpVariableFibreSheetDirections)[rElement.GetIndex()] : mConstantFibreSheetDirections;
00327     c_vector<double,DIM> fibre_dir;
00328     for(unsigned i=0; i<DIM; i++)
00329     {
00330         fibre_dir(i) = r_fibre_sheet_matrix(i,0);
00331     }
00332 
00333     c_matrix<double, DIM, DIM> jacobian, inverse_jacobian;
00334     double jacobian_determinant;
00335     this->mpQuadMesh->GetInverseJacobianForElement(rElement.GetIndex(), jacobian, jacobian_determinant, inverse_jacobian);
00336 
00337     if (assembleJacobian)
00338     {
00339         rAElem.clear();
00340         rAElemPrecond.clear();
00341     }
00342 
00343     if (assembleResidual)
00344     {
00345         rBElem.clear();
00346     }
00347 
00349     // Get the current displacement at the nodes
00351     static c_matrix<double,DIM,NUM_NODES_PER_ELEMENT> element_current_displacements;
00352     static c_vector<double,NUM_VERTICES_PER_ELEMENT> element_current_pressures;
00353     for (unsigned II=0; II<NUM_NODES_PER_ELEMENT; II++)
00354     {
00355         for (unsigned JJ=0; JJ<DIM; JJ++)
00356         {
00357             element_current_displacements(JJ,II) = this->mCurrentSolution[DIM*rElement.GetNodeGlobalIndex(II) + JJ];
00358         }
00359     }
00360 
00362     // Get the current pressure at the vertices
00364     for (unsigned II=0; II<NUM_VERTICES_PER_ELEMENT; II++)
00365     {
00366         element_current_pressures(II) = this->mCurrentSolution[DIM*this->mpQuadMesh->GetNumNodes() + rElement.GetNodeGlobalIndex(II)];
00367     }
00368 
00369     // allocate memory for the basis functions values and derivative values
00370     c_vector<double, NUM_VERTICES_PER_ELEMENT> linear_phi;
00371     c_vector<double, NUM_NODES_PER_ELEMENT> quad_phi;
00372     c_matrix<double, DIM, NUM_NODES_PER_ELEMENT> grad_quad_phi;
00373 
00374     // get the material law
00375     AbstractIncompressibleMaterialLaw<DIM>* p_material_law;
00376     if (this->mMaterialLaws.size()==1)
00377     {
00378         // homogeneous
00379         p_material_law = this->mMaterialLaws[0];
00380     }
00381     else
00382     {
00383         // heterogeneous
00384         #define COVERAGE_IGNORE // not going to have tests in cts for everything
00385         p_material_law = this->mMaterialLaws[rElement.GetIndex()];
00386         #undef COVERAGE_IGNORE
00387     }
00388 
00394     for (unsigned quadrature_index=0; quadrature_index < this->mpQuadratureRule->GetNumQuadPoints(); quadrature_index++)
00395     {
00396         unsigned current_quad_point_global_index =   rElement.GetIndex()*this->mpQuadratureRule->GetNumQuadPoints()
00397                                                    + quadrature_index;
00398 
00399         double wJ = jacobian_determinant * this->mpQuadratureRule->GetWeight(quadrature_index);
00400 
00401         const ChastePoint<DIM>& quadrature_point = this->mpQuadratureRule->rGetQuadPoint(quadrature_index);
00402 
00404         // set up basis function info
00406         LinearBasisFunction<DIM>::ComputeBasisFunctions(quadrature_point, linear_phi);
00407         QuadraticBasisFunction<DIM>::ComputeBasisFunctions(quadrature_point, quad_phi);
00408         QuadraticBasisFunction<DIM>::ComputeTransformedBasisFunctionDerivatives(quadrature_point, inverse_jacobian, grad_quad_phi);
00409 
00411         // (dont get the body force)
00413 
00415         // interpolate grad_u and p
00417         static c_matrix<double,DIM,DIM> grad_u; // grad_u = (du_i/dX_M)
00418         grad_u = zero_matrix<double>(DIM,DIM);  // must be on new line!!
00419 
00420         for (unsigned node_index=0; node_index<NUM_NODES_PER_ELEMENT; node_index++)
00421         {
00422             for (unsigned i=0; i<DIM; i++)
00423             {
00424                 for (unsigned M=0; M<DIM; M++)
00425                 {
00426                     grad_u(i,M) += grad_quad_phi(M,node_index)*element_current_displacements(i,node_index);
00427                 }
00428             }
00429         }
00430 
00431         double pressure = 0;
00432         for (unsigned vertex_index=0; vertex_index<NUM_VERTICES_PER_ELEMENT; vertex_index++)
00433         {
00434             pressure += linear_phi(vertex_index)*element_current_pressures(vertex_index);
00435         }
00436 
00437 
00439         // calculate C, inv(C) and T
00441         static c_matrix<double,DIM,DIM> F;
00442         static c_matrix<double,DIM,DIM> C;
00443         static c_matrix<double,DIM,DIM> inv_C;
00444         static c_matrix<double,DIM,DIM> inv_F;
00445         static c_matrix<double,DIM,DIM> T;
00446 
00447         for (unsigned i=0; i<DIM; i++)
00448         {
00449             for (unsigned M=0; M<DIM; M++)
00450             {
00451                 F(i,M) = (i==M?1:0) + grad_u(i,M);
00452             }
00453         }
00454 
00455         C = prod(trans(F),F);
00456         inv_C = Inverse(C);
00457         inv_F = Inverse(F);
00458 
00459         double detF = Determinant(F);
00460 
00461 
00462         /*****************************
00463          * The cardiac-specific code
00464          *****************************/
00465 
00466         // 1. Compute T and dTdE for the PASSIVE part of the strain energy.
00467         p_material_law->SetChangeOfBasisMatrix(r_fibre_sheet_matrix);
00468         p_material_law->ComputeStressAndStressDerivative(C,inv_C,pressure,T,this->dTdE,assembleJacobian);
00469 
00470         // 2. Compute the active tension and add to the stress and stress-derivative
00471         double I4 = inner_prod(fibre_dir, prod(C, fibre_dir));
00472         double lambda = sqrt(I4);
00473 
00474         double active_tension = 0;
00475         double d_act_tension_dlam = 0.0;     // Set and used if assembleJacobian==true
00476         double d_act_tension_d_dlamdt = 0.0; // Set and used if assembleJacobian==true
00477 
00478         GetActiveTensionAndTensionDerivs(lambda, current_quad_point_global_index, assembleJacobian,
00479                                          active_tension, d_act_tension_dlam, d_act_tension_d_dlamdt);
00480 
00481         // amend the stress and dTdE using the active tension
00482         double dTdE_coeff = -2*active_tension/(I4*I4); // note: I4*I4 = lam^4
00483         if(IsImplicitSolver())
00484         {
00485             dTdE_coeff += (d_act_tension_dlam + d_act_tension_d_dlamdt/mech_dt)/(lambda*I4); // note: I4*lam = lam^3
00486         }
00487 
00488         T += (active_tension/I4)*outer_prod(fibre_dir,fibre_dir);
00489 
00490         for (unsigned M=0; M<DIM; M++)
00491         {
00492             for (unsigned N=0; N<DIM; N++)
00493             {
00494                 for (unsigned P=0; P<DIM; P++)
00495                 {
00496                     for (unsigned Q=0; Q<DIM; Q++)
00497                     {
00498                         this->dTdE(M,N,P,Q) +=  dTdE_coeff*fibre_dir(M)*fibre_dir(N)*fibre_dir(P)*fibre_dir(Q);
00499                     }
00500                 }
00501             }
00502         }
00503 
00504         /*******************************************************************
00505          * End of cardiac specific code
00506          *
00507          * The following, excluding the lack of body force term, should be
00508          * identical to NonlinearElasticityAssembler::AssembleOnElement
00509          *******************************************************************/
00510 
00511 
00512         static FourthOrderTensor<DIM> dTdE_F;
00513         static FourthOrderTensor<DIM> dTdE_FF1;
00514         static FourthOrderTensor<DIM> dTdE_FF2;
00515 
00516         dTdE_F.SetAsProduct(this->dTdE, F, 1);  // B^{MdPQ}  = F^d_N * dTdE^{MdPQ}
00517         dTdE_FF1.SetAsProduct(dTdE_F, F, 3);    // B1^{MdPe} = F^d_N * F^e_Q * dTdE^{MNPQ}
00518         dTdE_FF2.SetAsProduct(dTdE_F, F, 2);    // B2^{MdeQ} = F^d_N * F^e_P * dTdE^{MNPQ}
00519 
00520 
00522         // residual vector
00524         if (assembleResidual)
00525         {
00526             for (unsigned index=0; index<NUM_NODES_PER_ELEMENT*DIM; index++)
00527             {
00528                 unsigned spatial_dim = index%DIM;
00529                 unsigned node_index = (index-spatial_dim)/DIM;
00530 
00531                 assert(node_index < NUM_NODES_PER_ELEMENT);
00532 
00533                 // no body force bit as body force = 0
00534 
00535                 for (unsigned M=0; M<DIM; M++)
00536                 {
00537                     for (unsigned N=0; N<DIM; N++)
00538                     {
00539                         rBElem(index) +=   T(M,N)
00540                                          * F(spatial_dim,M)
00541                                          * grad_quad_phi(N,node_index)
00542                                          * wJ;
00543                     }
00544                 }
00545             }
00546 
00547             for (unsigned vertex_index=0; vertex_index<NUM_VERTICES_PER_ELEMENT; vertex_index++)
00548             {
00549                 rBElem( NUM_NODES_PER_ELEMENT*DIM + vertex_index ) +=   linear_phi(vertex_index)
00550                                                                       * (detF - 1)
00551                                                                       * wJ;
00552             }
00553         }
00554 
00556         // Jacobian matrix
00558         if (assembleJacobian)
00559         {
00560             for (unsigned index1=0; index1<NUM_NODES_PER_ELEMENT*DIM; index1++)
00561             {
00562                 unsigned spatial_dim1 = index1%DIM;
00563                 unsigned node_index1 = (index1-spatial_dim1)/DIM;
00564 
00565 
00566                 for (unsigned index2=0; index2<NUM_NODES_PER_ELEMENT*DIM; index2++)
00567                 {
00568                     unsigned spatial_dim2 = index2%DIM;
00569                     unsigned node_index2 = (index2-spatial_dim2)/DIM;
00570 
00571                     for (unsigned M=0; M<DIM; M++)
00572                     {
00573                         for (unsigned N=0; N<DIM; N++)
00574                         {
00575                             rAElem(index1,index2) +=   T(M,N)
00576                                                      * grad_quad_phi(M,node_index1)
00577                                                      * grad_quad_phi(N,node_index2)
00578                                                      * (spatial_dim1==spatial_dim2?1:0)
00579                                                      * wJ;
00580                         }
00581                     }
00582 
00583                     for (unsigned M=0; M<DIM; M++)
00584                     {
00585                         for (unsigned P=0; P<DIM; P++)
00586                         {
00587                             rAElem(index1,index2)  +=   0.5
00588                                                       * dTdE_FF1(M,spatial_dim1,P,spatial_dim2)
00589                                                       * grad_quad_phi(P,node_index2)
00590                                                       * grad_quad_phi(M,node_index1)
00591                                                       * wJ;
00592                         }
00593 
00594                         for (unsigned Q=0; Q<DIM; Q++)
00595                         {
00596                            rAElem(index1,index2)  +=   0.5
00597                                                      * dTdE_FF2(M,spatial_dim1,spatial_dim2,Q)
00598                                                      * grad_quad_phi(Q,node_index2)
00599                                                      * grad_quad_phi(M,node_index1)
00600                                                      * wJ;
00601                         }
00602                     }
00603                 }
00604 
00605                 for (unsigned vertex_index=0; vertex_index<NUM_VERTICES_PER_ELEMENT; vertex_index++)
00606                 {
00607                     unsigned index2 = NUM_NODES_PER_ELEMENT*DIM + vertex_index;
00608 
00609                     for (unsigned M=0; M<DIM; M++)
00610                     {
00611                          rAElem(index1,index2)  +=  - inv_F(M,spatial_dim1)
00612                                                     * grad_quad_phi(M,node_index1)
00613                                                     * linear_phi(vertex_index)
00614                                                     * wJ;
00615                     }
00616                 }
00617             }
00618 
00619             for (unsigned vertex_index=0; vertex_index<NUM_VERTICES_PER_ELEMENT; vertex_index++)
00620             {
00621                 unsigned index1 = NUM_NODES_PER_ELEMENT*DIM + vertex_index;
00622 
00623                 for (unsigned index2=0; index2<NUM_NODES_PER_ELEMENT*DIM; index2++)
00624                 {
00625                     unsigned spatial_dim2 = index2%DIM;
00626                     unsigned node_index2 = (index2-spatial_dim2)/DIM;
00627 
00628                     for (unsigned M=0; M<DIM; M++)
00629                     {
00630                         // same as (negative of) the opposite block (ie a few lines up), except for detF
00631                         rAElem(index1,index2) +=   detF
00632                                                  * inv_F(M,spatial_dim2)
00633                                                  * grad_quad_phi(M,node_index2)
00634                                                  * linear_phi(vertex_index)
00635                                                  * wJ;
00636                     }
00637                 }
00638 
00640                 // Preconditioner matrix
00641                 // Fill the mass matrix (ie \intgl phi_i phi_j) in the
00642                 // pressure-pressure block. Note, the rest of the
00643                 // entries are filled in at the end
00645                 for (unsigned vertex_index2=0; vertex_index2<NUM_VERTICES_PER_ELEMENT; vertex_index2++)
00646                 {
00647                     unsigned index2 = NUM_NODES_PER_ELEMENT*DIM + vertex_index2;
00648                     rAElemPrecond(index1,index2) +=   linear_phi(vertex_index)
00649                                                     * linear_phi(vertex_index2)
00650                                                     * wJ;
00651                 }
00652             }
00653         }
00654     }
00655 
00656 
00657     if (assembleJacobian)
00658     {
00659         // Fill in the other blocks of the preconditioner matrix, by adding 
00660         // the jacobian matrix (this doesn't effect the pressure-pressure block 
00661         // of rAElemPrecond as the pressure-pressure block of rAElem is zero),
00662         // and the zero a block.
00663         //
00664         // The following altogether gives the preconditioner  [ A  B1^T ]
00665         //                                                    [ 0  M    ]
00666 
00667         rAElemPrecond = rAElemPrecond + rAElem;
00668         for(unsigned i=NUM_NODES_PER_ELEMENT*DIM; i<STENCIL_SIZE; i++)
00669         {
00670             for(unsigned j=0; j<NUM_NODES_PER_ELEMENT*DIM; j++)
00671             {
00672                 rAElemPrecond(i,j) = 0.0;
00673             }
00674         }
00675     }
00676 }
00677 
00678 
00679 template<unsigned DIM>
00680 void AbstractCardiacMechanicsAssembler<DIM>::SetVariableFibreSheetDirections(std::string orthoFile)
00681 {
00682     if((orthoFile.length()<7) || orthoFile.substr(orthoFile.length()-6,orthoFile.length()) != ".ortho")
00683     {
00684         EXCEPTION("Fibre file must be a .ortho file");
00685     }
00686 
00687     std::ifstream ifs(orthoFile.c_str());
00688     if (!ifs.is_open())
00689     {
00690         EXCEPTION("Could not open file: " + orthoFile);
00691     }
00692 
00693     unsigned num_elem_read_from_file;
00694     ifs >> num_elem_read_from_file;
00695     assert(num_elem_read_from_file == this->mpQuadMesh->GetNumElements());
00696 
00697     mpVariableFibreSheetDirections = new std::vector<c_matrix<double,DIM,DIM> >(this->mpQuadMesh->GetNumElements(), zero_matrix<double>(DIM,DIM));
00698     for(unsigned elem_index=0; elem_index<this->mpQuadMesh->GetNumElements(); elem_index++)
00699     {
00700         for(unsigned j=0; j<DIM*DIM; j++)
00701         {
00702             double data;
00703             ifs >> data;
00704             if(ifs.fail())
00705             {
00706                 std::stringstream error_message;
00707                 error_message << "Error occurred when reading file " << orthoFile
00708                               << ". Expected " << this->mpQuadMesh->GetNumElements() << " rows and "
00709                               << "three (not DIM!) columns, ie three components for each fibre, whichever "
00710                               << "dimension you are in";
00711                 delete mpVariableFibreSheetDirections;
00712                 EXCEPTION(error_message.str());
00713             }
00714 
00715             (*mpVariableFibreSheetDirections)[elem_index](j/DIM,j%DIM) = data;
00716         }
00717     }
00718 
00719     ifs.close();
00720 
00721     for(unsigned elem_index=0; elem_index<this->mpQuadMesh->GetNumElements(); elem_index++)
00722     {
00723         CheckOrthogonality((*mpVariableFibreSheetDirections)[elem_index]);
00724     }
00725 }
00726 
00727 
00728 #endif /*ABSTRACTCARDIACMECHANICSASSEMBLER_HPP_*/

Generated by  doxygen 1.6.2