Chaste Release::3.1
|
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_*/