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 #ifndef ABSTRACTCARDIACMECHANICSASSEMBLER_HPP_
00030 #define ABSTRACTCARDIACMECHANICSASSEMBLER_HPP_
00031
00032 #include "NonlinearElasticityAssembler.hpp"
00033 #include "NashHunterPoleZeroLaw.hpp"
00034 #include "QuadraticBasisFunction.hpp"
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
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
00192 mTotalQuadPoints = pQuadMesh->GetNumElements()*this->mpQuadratureRule->GetNumQuadPoints();
00193
00194
00195
00196 mAllocatedMaterialLawMemory = (pMaterialLaw==NULL);
00197
00198
00199 mStretches.resize(mTotalQuadPoints, 1.0);
00200
00201
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);
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
00320 assert(mCurrentTime != DBL_MAX);
00321 assert(mNextTime != DBL_MAX);
00322 assert(mOdeTimestep != DBL_MAX);
00323 double mech_dt = mNextTime - mCurrentTime;
00324
00325
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
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
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
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
00375 AbstractIncompressibleMaterialLaw<DIM>* p_material_law;
00376 if (this->mMaterialLaws.size()==1)
00377 {
00378
00379 p_material_law = this->mMaterialLaws[0];
00380 }
00381 else
00382 {
00383
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
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
00413
00415
00417 static c_matrix<double,DIM,DIM> grad_u;
00418 grad_u = zero_matrix<double>(DIM,DIM);
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
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
00464
00465
00466
00467 p_material_law->SetChangeOfBasisMatrix(r_fibre_sheet_matrix);
00468 p_material_law->ComputeStressAndStressDerivative(C,inv_C,pressure,T,this->dTdE,assembleJacobian);
00469
00470
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;
00476 double d_act_tension_d_dlamdt = 0.0;
00477
00478 GetActiveTensionAndTensionDerivs(lambda, current_quad_point_global_index, assembleJacobian,
00479 active_tension, d_act_tension_dlam, d_act_tension_d_dlamdt);
00480
00481
00482 double dTdE_coeff = -2*active_tension/(I4*I4);
00483 if(IsImplicitSolver())
00484 {
00485 dTdE_coeff += (d_act_tension_dlam + d_act_tension_d_dlamdt/mech_dt)/(lambda*I4);
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
00506
00507
00508
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);
00517 dTdE_FF1.SetAsProduct(dTdE_F, F, 3);
00518 dTdE_FF2.SetAsProduct(dTdE_F, F, 2);
00519
00520
00522
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
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
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
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
00641
00642
00643
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
00660
00661
00662
00663
00664
00665
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