00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include "AbstractCardiacMechanicsSolver.hpp"
00037 #include "AbstractContractionCellFactory.hpp"
00038 #include "FakeBathContractionModel.hpp"
00039
00040 template<class ELASTICITY_SOLVER,unsigned DIM>
00041 AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::AbstractCardiacMechanicsSolver(QuadraticMesh<DIM>& rQuadMesh,
00042 ElectroMechanicsProblemDefinition<DIM>& rProblemDefinition,
00043 std::string outputDirectory)
00044 : ELASTICITY_SOLVER(rQuadMesh,
00045 rProblemDefinition,
00046 outputDirectory),
00047 mpMeshPair(NULL),
00048 mCurrentTime(DBL_MAX),
00049 mNextTime(DBL_MAX),
00050 mOdeTimestep(DBL_MAX),
00051 mrElectroMechanicsProblemDefinition(rProblemDefinition)
00052 {
00053 }
00054
00055 template<class ELASTICITY_SOLVER,unsigned DIM>
00056 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::Initialise()
00057 {
00058
00059 unsigned num_quad_pts_per_element = this->mpQuadratureRule->GetNumQuadPoints();
00060 mTotalQuadPoints = this->mrQuadMesh.GetNumElements()*num_quad_pts_per_element;
00061
00062 std::vector<ElementAndWeights<DIM> > fine_elements = mpMeshPair->rGetElementsAndWeights();
00063 assert(fine_elements.size()==mTotalQuadPoints);
00064 assert(mpMeshPair!=NULL);
00065
00066 AbstractContractionCellFactory<DIM>* p_factory = mrElectroMechanicsProblemDefinition.GetContractionCellFactory();
00067
00068 for (typename AbstractTetrahedralMesh<DIM, DIM>::ElementIterator iter = this->mrQuadMesh.GetElementIteratorBegin();
00069 iter != this->mrQuadMesh.GetElementIteratorEnd();
00070 ++iter)
00071 {
00072 Element<DIM, DIM>& element = *iter;
00073
00074 if (element.GetOwnership() == true)
00075 {
00076 for(unsigned j=0; j<num_quad_pts_per_element; j++)
00077 {
00078 unsigned quad_pt_global_index = element.GetIndex()*num_quad_pts_per_element + j;
00079
00080
00081
00082
00083 DataAtQuadraturePoint data_at_quad_point;
00084 data_at_quad_point.Stretch = 1.0;
00085 data_at_quad_point.StretchLastTimeStep = 1.0;
00086
00087 if ( mpMeshPair->GetFineMesh().GetElement(fine_elements[quad_pt_global_index].ElementNum)
00088 ->GetUnsignedAttribute() == HeartRegionCode::GetValidBathId() )
00089 {
00090
00091 data_at_quad_point.ContractionModel = new FakeBathContractionModel;
00092 }
00093 else
00094 {
00095
00096 data_at_quad_point.ContractionModel = p_factory->CreateContractionCellForElement( &element );
00097 }
00098 mQuadPointToDataAtQuadPointMap[quad_pt_global_index] = data_at_quad_point;
00099 }
00100 }
00101 }
00102
00103
00104 mMapIterator = mQuadPointToDataAtQuadPointMap.begin();
00105
00106
00107 mConstantFibreSheetDirections = zero_matrix<double>(DIM,DIM);
00108 for(unsigned i=0; i<DIM; i++)
00109 {
00110 mConstantFibreSheetDirections(i,i) = 1.0;
00111 }
00112
00113 mpVariableFibreSheetDirections = NULL;
00114
00115
00116 for(std::map<unsigned,DataAtQuadraturePoint>::iterator iter = this->mQuadPointToDataAtQuadPointMap.begin();
00117 iter != this->mQuadPointToDataAtQuadPointMap.end();
00118 iter++)
00119 {
00120 if (!IsImplicitSolver() && (*iter).second.ContractionModel->IsStretchRateDependent())
00121 {
00122 EXCEPTION("stretch-rate-dependent contraction model requires an IMPLICIT cardiac mechanics solver.");
00123 }
00124
00125 if (!IsImplicitSolver() && (*iter).second.ContractionModel->IsStretchDependent())
00126 {
00127 WARN_ONCE_ONLY("stretch-dependent contraction model may require an IMPLICIT cardiac mechanics solver.");
00128 }
00129 }
00130 }
00131
00132
00133 template<class ELASTICITY_SOLVER,unsigned DIM>
00134 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::SetFineCoarseMeshPair(FineCoarseMeshPair<DIM>* pMeshPair)
00135 {
00136 assert(pMeshPair!=NULL);
00137 if (pMeshPair->GetCoarseMesh().GetNumElements() != this->mrQuadMesh.GetNumElements())
00138 {
00139 EXCEPTION("When setting a mesh pair into the solver, the coarse mesh of the mesh pair must be the same as the quadratic mesh");
00140 }
00141 mpMeshPair = pMeshPair;
00142 }
00143
00144 template<class ELASTICITY_SOLVER,unsigned DIM>
00145 AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::~AbstractCardiacMechanicsSolver()
00146 {
00147 for(mMapIterator = mQuadPointToDataAtQuadPointMap.begin();
00148 mMapIterator != mQuadPointToDataAtQuadPointMap.end();
00149 ++mMapIterator)
00150 {
00151 AbstractContractionModel* p_model = mMapIterator->second.ContractionModel;
00152 if (p_model)
00153 {
00154 delete p_model;
00155 }
00156 }
00157
00158 if(mpVariableFibreSheetDirections)
00159 {
00160 delete mpVariableFibreSheetDirections;
00161 }
00162 }
00163
00164
00165
00166 template<class ELASTICITY_SOLVER,unsigned DIM>
00167 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::SetCalciumAndVoltage(std::vector<double>& rCalciumConcentrations,
00168 std::vector<double>& rVoltages)
00169
00170 {
00171 assert(rCalciumConcentrations.size() == mTotalQuadPoints);
00172 assert(rVoltages.size() == mTotalQuadPoints);
00173
00174 ContractionModelInputParameters input_parameters;
00175
00176 for(unsigned i=0; i<rCalciumConcentrations.size(); i++)
00177 {
00178 input_parameters.intracellularCalciumConcentration = rCalciumConcentrations[i];
00179 input_parameters.voltage = rVoltages[i];
00180
00182 std::map<unsigned,DataAtQuadraturePoint>::iterator iter = mQuadPointToDataAtQuadPointMap.find(i);
00183 if(iter != mQuadPointToDataAtQuadPointMap.end())
00184 {
00185 iter->second.ContractionModel->SetInputParameters(input_parameters);
00186 }
00187 }
00188 }
00189
00190
00191 template<class ELASTICITY_SOLVER,unsigned DIM>
00192 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::SetupChangeOfBasisMatrix(unsigned elementIndex,
00193 unsigned currentQuadPointGlobalIndex)
00194 {
00195 if(!mpVariableFibreSheetDirections)
00196 {
00197 this->mChangeOfBasisMatrix = mConstantFibreSheetDirections;
00198 }
00199 else if(!mFibreSheetDirectionsDefinedByQuadraturePoint)
00200 {
00201 this->mChangeOfBasisMatrix = (*mpVariableFibreSheetDirections)[elementIndex];
00202 }
00203 else
00204 {
00205 this->mChangeOfBasisMatrix = (*mpVariableFibreSheetDirections)[currentQuadPointGlobalIndex];
00206 }
00207 }
00208
00209
00210
00211 template<class ELASTICITY_SOLVER,unsigned DIM>
00212 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::AddActiveStressAndStressDerivative(c_matrix<double,DIM,DIM>& rC,
00213 unsigned elementIndex,
00214 unsigned currentQuadPointGlobalIndex,
00215 c_matrix<double,DIM,DIM>& rT,
00216 FourthOrderTensor<DIM,DIM,DIM,DIM>& rDTdE,
00217 bool addToDTdE)
00218 {
00219 for(unsigned i=0; i<DIM; i++)
00220 {
00221 mCurrentElementFibreDirection(i) = this->mChangeOfBasisMatrix(i,0);
00222 }
00223
00224
00225 double I4_fibre = inner_prod(mCurrentElementFibreDirection, prod(rC, mCurrentElementFibreDirection));
00226 double lambda_fibre = sqrt(I4_fibre);
00227
00228 double active_tension = 0;
00229 double d_act_tension_dlam = 0.0;
00230 double d_act_tension_d_dlamdt = 0.0;
00231
00232 GetActiveTensionAndTensionDerivs(lambda_fibre, currentQuadPointGlobalIndex, addToDTdE,
00233 active_tension, d_act_tension_dlam, d_act_tension_d_dlamdt);
00234
00235
00236 double detF = sqrt(Determinant(rC));
00237 rT += (active_tension*detF/I4_fibre)*outer_prod(mCurrentElementFibreDirection,mCurrentElementFibreDirection);
00238
00239
00240 double dTdE_coeff1 = -2*active_tension*detF/(I4_fibre*I4_fibre);
00241 double dTdE_coeff2 = active_tension*detF/I4_fibre;
00242 double dTdE_coeff_s1 = 0.0;
00243 double dTdE_coeff_s2 = 0.0;
00244 double dTdE_coeff_s3 = 0.0;
00245 double dTdE_coeff_n1 = 0.0;
00246 double dTdE_coeff_n2 = 0.0;
00247 double dTdE_coeff_n3 = 0.0;
00248
00249 if(IsImplicitSolver())
00250 {
00251 double dt = mNextTime-mCurrentTime;
00252
00253 dTdE_coeff1 += (d_act_tension_dlam + d_act_tension_d_dlamdt/dt)*detF/(lambda_fibre*I4_fibre);
00254 }
00255
00256 bool apply_cross_fibre_tension = (this->mrElectroMechanicsProblemDefinition.GetApplyCrossFibreTension()) && (DIM > 1);
00257 if(apply_cross_fibre_tension)
00258 {
00259 double sheet_cross_fraction = mrElectroMechanicsProblemDefinition.GetSheetTensionFraction();
00260
00261 for(unsigned i=0; i<DIM; i++)
00262 {
00263 mCurrentElementSheetDirection(i) = this->mChangeOfBasisMatrix(i,1);
00264 }
00265
00266 double I4_sheet = inner_prod(mCurrentElementSheetDirection, prod(rC, mCurrentElementSheetDirection));
00267
00268
00269 dTdE_coeff_s1 = -2*sheet_cross_fraction*detF*active_tension/(I4_sheet*I4_sheet);
00270
00271 if(IsImplicitSolver())
00272 {
00273 double dt = mNextTime-mCurrentTime;
00274 dTdE_coeff_s3 = sheet_cross_fraction*(d_act_tension_dlam + d_act_tension_d_dlamdt/dt)*detF/(lambda_fibre*I4_sheet);
00275 }
00276
00277 rT += sheet_cross_fraction*(active_tension*detF/I4_sheet)*outer_prod(mCurrentElementSheetDirection,mCurrentElementSheetDirection);
00278
00279 dTdE_coeff_s2 = active_tension*sheet_cross_fraction*detF/I4_sheet;
00280
00281 if (DIM>2)
00282 {
00283 double sheet_normal_cross_fraction = mrElectroMechanicsProblemDefinition.GetSheetNormalTensionFraction();
00284 for(unsigned i=0; i<DIM; i++)
00285 {
00286 mCurrentElementSheetNormalDirection(i) = this->mChangeOfBasisMatrix(i,2);
00287 }
00288
00289 double I4_sheet_normal = inner_prod(mCurrentElementSheetNormalDirection, prod(rC, mCurrentElementSheetNormalDirection));
00290
00291 dTdE_coeff_n1 =-2*sheet_normal_cross_fraction*detF*active_tension/(I4_sheet_normal*I4_sheet_normal);
00292
00293 rT += sheet_normal_cross_fraction*(active_tension*detF/I4_sheet_normal)*outer_prod(mCurrentElementSheetNormalDirection,mCurrentElementSheetNormalDirection);
00294
00295 dTdE_coeff_n2 = active_tension*sheet_normal_cross_fraction*detF/I4_sheet_normal;
00296 if(IsImplicitSolver())
00297 {
00298 double dt = mNextTime-mCurrentTime;
00299 dTdE_coeff_n3 = sheet_normal_cross_fraction*(d_act_tension_dlam + d_act_tension_d_dlamdt/dt)*detF/(lambda_fibre*I4_sheet_normal);
00300 }
00301 }
00302 }
00303
00304
00305 if(addToDTdE)
00306 {
00307 c_matrix<double,DIM,DIM> invC = Inverse(rC);
00308
00309 for (unsigned M=0; M<DIM; M++)
00310 {
00311 for (unsigned N=0; N<DIM; N++)
00312 {
00313 for (unsigned P=0; P<DIM; P++)
00314 {
00315 for (unsigned Q=0; Q<DIM; Q++)
00316 {
00317 rDTdE(M,N,P,Q) += dTdE_coeff1 * mCurrentElementFibreDirection(M)
00318 * mCurrentElementFibreDirection(N)
00319 * mCurrentElementFibreDirection(P)
00320 * mCurrentElementFibreDirection(Q)
00321
00322 + dTdE_coeff2 * mCurrentElementFibreDirection(M)
00323 * mCurrentElementFibreDirection(N)
00324 * invC(P,Q);
00325 if(apply_cross_fibre_tension)
00326 {
00327 rDTdE(M,N,P,Q) += dTdE_coeff_s1 * mCurrentElementSheetDirection(M)
00328 * mCurrentElementSheetDirection(N)
00329 * mCurrentElementSheetDirection(P)
00330 * mCurrentElementSheetDirection(Q)
00331
00332 + dTdE_coeff_s2 * mCurrentElementSheetDirection(M)
00333 * mCurrentElementSheetDirection(N)
00334 * invC(P,Q)
00335
00336 + dTdE_coeff_s3 * mCurrentElementSheetDirection(M)
00337 * mCurrentElementSheetDirection(N)
00338 * mCurrentElementFibreDirection(P)
00339 * mCurrentElementFibreDirection(Q);
00340 if (DIM>2)
00341 {
00342 rDTdE(M,N,P,Q) += dTdE_coeff_n1 * mCurrentElementSheetNormalDirection(M)
00343 * mCurrentElementSheetNormalDirection(N)
00344 * mCurrentElementSheetNormalDirection(P)
00345 * mCurrentElementSheetNormalDirection(Q)
00346
00347 + dTdE_coeff_n2 * mCurrentElementSheetNormalDirection(M)
00348 * mCurrentElementSheetNormalDirection(N)
00349 * invC(P,Q)
00350
00351 + dTdE_coeff_n3 * mCurrentElementSheetNormalDirection(M)
00352 * mCurrentElementSheetNormalDirection(N)
00353 * mCurrentElementFibreDirection(P)
00354 * mCurrentElementFibreDirection(Q);
00355 }
00356 }
00357 }
00358 }
00359 }
00360 }
00361 }
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418 }
00419
00420
00421 template<class ELASTICITY_SOLVER,unsigned DIM>
00422 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::ComputeDeformationGradientAndStretchInEachElement(
00423 std::vector<c_matrix<double,DIM,DIM> >& rDeformationGradients,
00424 std::vector<double>& rStretches)
00425 {
00426 assert(rStretches.size()==this->mrQuadMesh.GetNumElements());
00427
00428
00429 assert(!mpVariableFibreSheetDirections || !mFibreSheetDirectionsDefinedByQuadraturePoint);
00430
00431 static c_matrix<double,DIM,NUM_VERTICES_PER_ELEMENT> element_current_displacements;
00432 static c_matrix<double,DIM,NUM_VERTICES_PER_ELEMENT> grad_lin_phi;
00433 static c_matrix<double,DIM,DIM> F;
00434
00435 static c_matrix<double,DIM,DIM> jacobian;
00436 static c_matrix<double,DIM,DIM> inverse_jacobian;
00437 double jacobian_determinant;
00438 ChastePoint<DIM> quadrature_point;
00439
00440
00441 for(unsigned elem_index=0; elem_index<this->mrQuadMesh.GetNumElements(); elem_index++)
00442 {
00443 Element<DIM,DIM>* p_elem = this->mrQuadMesh.GetElement(elem_index);
00444
00445
00446 SetupChangeOfBasisMatrix(elem_index, 0);
00447 for(unsigned i=0; i<DIM; i++)
00448 {
00449 mCurrentElementFibreDirection(i) = this->mChangeOfBasisMatrix(i,0);
00450 }
00451
00452
00453 for (unsigned II=0; II<NUM_VERTICES_PER_ELEMENT; II++)
00454 {
00455 for (unsigned JJ=0; JJ<DIM; JJ++)
00456 {
00457
00458 element_current_displacements(JJ,II) = this->mCurrentSolution[this->mProblemDimension*p_elem->GetNodeGlobalIndex(II) + JJ];
00459 }
00460 }
00461
00462
00463 this->mrQuadMesh.GetInverseJacobianForElement(elem_index, jacobian, jacobian_determinant, inverse_jacobian);
00464 LinearBasisFunction<DIM>::ComputeTransformedBasisFunctionDerivatives(quadrature_point, inverse_jacobian, grad_lin_phi);
00465
00466 F = identity_matrix<double>(DIM,DIM);
00467
00468
00469
00470 for (unsigned node_index=0; node_index<NUM_VERTICES_PER_ELEMENT; node_index++)
00471 {
00472 for (unsigned i=0; i<DIM; i++)
00473 {
00474 for (unsigned M=0; M<DIM; M++)
00475 {
00476 F(i,M) += grad_lin_phi(M,node_index)*element_current_displacements(i,node_index);
00477 }
00478 }
00479 }
00480
00481 rDeformationGradients[elem_index] = F;
00482
00483
00484 c_vector<double,DIM> deformed_fibre = prod(F, mCurrentElementFibreDirection);
00485 rStretches[elem_index] = norm_2(deformed_fibre);
00486 }
00487 }
00488
00489
00490 template<class ELASTICITY_SOLVER,unsigned DIM>
00491 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::SetVariableFibreSheetDirections(const FileFinder& rOrthoFile, bool definedPerQuadraturePoint)
00492 {
00493 mFibreSheetDirectionsDefinedByQuadraturePoint = definedPerQuadraturePoint;
00494
00495 FibreReader<DIM> reader(rOrthoFile, ORTHO);
00496
00497 unsigned num_entries = reader.GetNumLinesOfData();
00498
00499 if (!mFibreSheetDirectionsDefinedByQuadraturePoint && (num_entries!=this->mrQuadMesh.GetNumElements()) )
00500 {
00501 EXCEPTION("Number of entries defined at top of file " << rOrthoFile.GetAbsolutePath() <<
00502 " does not match number of elements in the mesh, " << "found " << num_entries <<
00503 ", expected " << this->mrQuadMesh.GetNumElements());
00504 }
00505
00506 if (mFibreSheetDirectionsDefinedByQuadraturePoint && (num_entries!=mTotalQuadPoints) )
00507 {
00508 EXCEPTION("Number of entries defined at top of file " << rOrthoFile.GetAbsolutePath() <<
00509 " does not match number of quadrature points defined, " << "found " << num_entries <<
00510 ", expected " << mTotalQuadPoints);
00511 }
00512
00513 mpVariableFibreSheetDirections = new std::vector<c_matrix<double,DIM,DIM> >(num_entries, zero_matrix<double>(DIM,DIM));
00514 for(unsigned index=0; index<num_entries; index++)
00515 {
00516 reader.GetFibreSheetAndNormalMatrix(index, (*mpVariableFibreSheetDirections)[index] );
00517 }
00518 }
00519
00520 template<class ELASTICITY_SOLVER,unsigned DIM>
00521 void AbstractCardiacMechanicsSolver<ELASTICITY_SOLVER,DIM>::SetConstantFibreSheetDirections(const c_matrix<double,DIM,DIM>& rFibreSheetMatrix)
00522 {
00523 mConstantFibreSheetDirections = rFibreSheetMatrix;
00524
00525 c_matrix<double,DIM,DIM> temp = prod(trans(rFibreSheetMatrix),rFibreSheetMatrix);
00526 for(unsigned i=0; i<DIM; i++)
00527 {
00528 for(unsigned j=0; j<DIM; j++)
00529 {
00530 double val = (i==j ? 1.0 : 0.0);
00531 if(fabs(temp(i,j)-val)>1e-4)
00532 {
00533 EXCEPTION("The given fibre-sheet matrix, " << rFibreSheetMatrix << ", is not orthogonal");
00534 }
00535 }
00536 }
00537 }
00538
00539 template class AbstractCardiacMechanicsSolver<IncompressibleNonlinearElasticitySolver<2>,2>;
00540 template class AbstractCardiacMechanicsSolver<IncompressibleNonlinearElasticitySolver<3>,3>;
00541 template class AbstractCardiacMechanicsSolver<CompressibleNonlinearElasticitySolver<2>,2>;
00542 template class AbstractCardiacMechanicsSolver<CompressibleNonlinearElasticitySolver<3>,3>;
00543
00544