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 #include "BidomainDg0Assembler.hpp"
00030 #include <boost/numeric/ublas/vector_proxy.hpp>
00031
00032 #include "Exception.hpp"
00033 #include "DistributedVector.hpp"
00034 #include "ConstBoundaryCondition.hpp"
00035
00036 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00037 void BidomainDg0Assembler<ELEMENT_DIM,SPACE_DIM>::ResetInterpolatedQuantities( void )
00038 {
00039 mIionic=0;
00040 mIIntracellularStimulus=0;
00041 }
00042
00043 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00044 void BidomainDg0Assembler<ELEMENT_DIM,SPACE_DIM>::InitialiseForSolve(Vec initialSolution)
00045 {
00046 if (this->mpLinearSystem != NULL)
00047 {
00048 return;
00049 }
00050
00051
00052 BaseClassType::InitialiseForSolve(initialSolution);
00053
00054 if (HeartConfig::Instance()->GetUseAbsoluteTolerance())
00055 {
00056 #ifdef TRACE_KSP
00057 std::cout << "Using absolute tolerance: " << mpConfig->GetAbsoluteTolerance() <<"\n";
00058 #endif
00059 this->mpLinearSystem->SetAbsoluteTolerance(mpConfig->GetAbsoluteTolerance());
00060 }
00061 else
00062 {
00063 #ifdef TRACE_KSP
00064 std::cout << "Using relative tolerance: " << mpConfig->GetRelativeTolerance() <<"\n";
00065 #endif
00066 this->mpLinearSystem->SetRelativeTolerance(mpConfig->GetRelativeTolerance());
00067 }
00068
00069 this->mpLinearSystem->SetKspType(HeartConfig::Instance()->GetKSPSolver());
00070 this->mpLinearSystem->SetPcType(HeartConfig::Instance()->GetKSPPreconditioner());
00071
00072 if (mRowForAverageOfPhiZeroed==INT_MAX)
00073 {
00074
00075 this->mpLinearSystem->SetMatrixIsSymmetric(true);
00076 }
00077 else
00078 {
00079
00080 this->mpLinearSystem->SetMatrixIsSymmetric(false);
00081 }
00082 }
00083
00084
00085 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00086 void BidomainDg0Assembler<ELEMENT_DIM,SPACE_DIM>::IncrementInterpolatedQuantities(double phiI, const Node<SPACE_DIM>* pNode)
00087 {
00088 unsigned node_global_index = pNode->GetIndex();
00089
00090 mIionic += phiI * mpBidomainPde->rGetIionicCacheReplicated()[ node_global_index ];
00091 mIIntracellularStimulus += phiI * mpBidomainPde->rGetIntracellularStimulusCacheReplicated()[ node_global_index ];
00092 }
00093
00094 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00095 c_matrix<double,2*(ELEMENT_DIM+1),2*(ELEMENT_DIM+1)>
00096 BidomainDg0Assembler<ELEMENT_DIM,SPACE_DIM>::ComputeMatrixTerm(
00097 c_vector<double, ELEMENT_DIM+1> &rPhi,
00098 c_matrix<double, SPACE_DIM, ELEMENT_DIM+1> &rGradPhi,
00099 ChastePoint<SPACE_DIM> &rX,
00100 c_vector<double,2> &rU,
00101 c_matrix<double, 2, SPACE_DIM> &rGradU ,
00102 Element<ELEMENT_DIM,SPACE_DIM>* pElement)
00103 {
00104
00105 double Am = mpConfig->GetSurfaceAreaToVolumeRatio();
00106 double Cm = mpConfig->GetCapacitance();
00107
00108 const c_matrix<double, SPACE_DIM, SPACE_DIM>& sigma_i = mpBidomainPde->rGetIntracellularConductivityTensor(pElement->GetIndex());
00109 const c_matrix<double, SPACE_DIM, SPACE_DIM>& sigma_e = mpBidomainPde->rGetExtracellularConductivityTensor(pElement->GetIndex());
00110
00111
00112 c_matrix<double, SPACE_DIM, ELEMENT_DIM+1> temp = prod(sigma_i, rGradPhi);
00113 c_matrix<double, ELEMENT_DIM+1, ELEMENT_DIM+1> grad_phi_sigma_i_grad_phi =
00114 prod(trans(rGradPhi), temp);
00115
00116 c_matrix<double, ELEMENT_DIM+1, ELEMENT_DIM+1> basis_outer_prod =
00117 outer_prod(rPhi, rPhi);
00118
00119 c_matrix<double, SPACE_DIM, ELEMENT_DIM+1> temp2 = prod(sigma_e, rGradPhi);
00120 c_matrix<double, ELEMENT_DIM+1, ELEMENT_DIM+1> grad_phi_sigma_e_grad_phi =
00121 prod(trans(rGradPhi), temp2);
00122
00123
00124 c_matrix<double,2*(ELEMENT_DIM+1),2*(ELEMENT_DIM+1)> ret;
00125
00126
00127 matrix_slice<c_matrix<double, 2*ELEMENT_DIM+2, 2*ELEMENT_DIM+2> >
00128 slice00(ret, slice (0, 2, ELEMENT_DIM+1), slice (0, 2, ELEMENT_DIM+1));
00129 slice00 = (Am*Cm/this->mDt)*basis_outer_prod + grad_phi_sigma_i_grad_phi;
00130
00131
00132 matrix_slice<c_matrix<double, 2*ELEMENT_DIM+2, 2*ELEMENT_DIM+2> >
00133 slice10(ret, slice (1, 2, ELEMENT_DIM+1), slice (0, 2, ELEMENT_DIM+1));
00134 slice10 = grad_phi_sigma_i_grad_phi;
00135
00136
00137 matrix_slice<c_matrix<double, 2*ELEMENT_DIM+2, 2*ELEMENT_DIM+2> >
00138 slice01(ret, slice (0, 2, ELEMENT_DIM+1), slice (1, 2, ELEMENT_DIM+1));
00139 slice01 = grad_phi_sigma_i_grad_phi;
00140
00141
00142 matrix_slice<c_matrix<double, 2*ELEMENT_DIM+2, 2*ELEMENT_DIM+2> >
00143 slice11(ret, slice (1, 2, ELEMENT_DIM+1), slice (1, 2, ELEMENT_DIM+1));
00144 slice11 = grad_phi_sigma_i_grad_phi + grad_phi_sigma_e_grad_phi;
00145
00146 return ret;
00147 }
00148
00149
00150 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00151 c_vector<double,2*(ELEMENT_DIM+1)>
00152 BidomainDg0Assembler<ELEMENT_DIM,SPACE_DIM>::ComputeVectorTerm(
00153 c_vector<double, ELEMENT_DIM+1> &rPhi,
00154 c_matrix<double, SPACE_DIM, ELEMENT_DIM+1> &rGradPhi,
00155 ChastePoint<SPACE_DIM> &rX,
00156 c_vector<double,2> &u,
00157 c_matrix<double, 2, SPACE_DIM> &rGradU ,
00158 Element<ELEMENT_DIM,SPACE_DIM>* pElement)
00159 {
00160
00161 double Am = mpConfig->GetSurfaceAreaToVolumeRatio();
00162 double Cm = mpConfig->GetCapacitance();
00163
00164 c_vector<double,2*(ELEMENT_DIM+1)> ret;
00165
00166 vector_slice<c_vector<double, 2*(ELEMENT_DIM+1)> > slice_V (ret, slice (0, 2, ELEMENT_DIM+1));
00167 vector_slice<c_vector<double, 2*(ELEMENT_DIM+1)> > slice_Phi(ret, slice (1, 2, ELEMENT_DIM+1));
00168
00169
00170 noalias(slice_V) = (Am*Cm*u(0)/this->mDt - Am*mIionic - mIIntracellularStimulus) * rPhi;
00171 noalias(slice_Phi) = zero_vector<double>(ELEMENT_DIM+1);
00172
00173 return ret;
00174 }
00175
00176
00177
00178
00179 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00180 c_vector<double, 2*ELEMENT_DIM> BidomainDg0Assembler<ELEMENT_DIM,SPACE_DIM>::ComputeVectorSurfaceTerm(
00181 const BoundaryElement<ELEMENT_DIM-1,SPACE_DIM> &rSurfaceElement,
00182 c_vector<double,ELEMENT_DIM> &rPhi,
00183 ChastePoint<SPACE_DIM> &rX)
00184 {
00185
00186 double sigma_i_times_grad_phi_i_dot_n = this->mpBoundaryConditions->GetNeumannBCValue(&rSurfaceElement, rX, 0);
00187 double sigma_e_times_grad_phi_e_dot_n = this->mpBoundaryConditions->GetNeumannBCValue(&rSurfaceElement, rX, 1);
00188
00189 c_vector<double, 2*ELEMENT_DIM> ret;
00190 for (unsigned i=0; i<ELEMENT_DIM; i++)
00191 {
00192 ret(2*i) = rPhi(i)*sigma_i_times_grad_phi_i_dot_n;
00193 ret(2*i+1) = rPhi(i)*(sigma_i_times_grad_phi_i_dot_n + sigma_e_times_grad_phi_e_dot_n);
00194 }
00195
00196 return ret;
00197 }
00198
00199
00200
00201 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00202 void BidomainDg0Assembler<ELEMENT_DIM,SPACE_DIM>::PrepareForAssembleSystem(Vec existingSolution, double time)
00203 {
00204 mpBidomainPde->SolveCellSystems(existingSolution, time, time+this->mDt);
00205 }
00206
00207 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00208 Vec BidomainDg0Assembler<ELEMENT_DIM,SPACE_DIM>::GenerateNullBasis() const
00209 {
00210 double sqrt_num_nodes = sqrt((double) this->mpMesh->GetNumNodes());
00211
00212 Vec nullbasis;
00213 DistributedVectorFactory* p_factory = this->mpMesh->GetDistributedVectorFactory();
00214 nullbasis=p_factory->CreateVec(2);
00215 DistributedVector dist_null_basis = p_factory->CreateDistributedVector(nullbasis);
00216 DistributedVector::Stripe null_basis_stripe_0(dist_null_basis,0);
00217 DistributedVector::Stripe null_basis_stripe_1(dist_null_basis,1);
00218 for (DistributedVector::Iterator index = dist_null_basis.Begin();
00219 index != dist_null_basis.End();
00220 ++index)
00221 {
00222 null_basis_stripe_0[index] = 0.0;
00223 null_basis_stripe_1[index] = 1.0/sqrt_num_nodes;
00224 }
00225 dist_null_basis.Restore();
00226
00227 return nullbasis;
00228 }
00229
00230
00231 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00232 void BidomainDg0Assembler<ELEMENT_DIM,SPACE_DIM>::FinaliseAssembleSystem(Vec existingSolution, double time)
00233 {
00234 if (!(this->mpBoundaryConditions->HasDirichletBoundaryConditions()))
00235 {
00236
00237 if (mRowForAverageOfPhiZeroed==INT_MAX)
00238 {
00239
00240 if (!mNullSpaceCreated)
00241 {
00242
00243 Vec nullbasis[] = {GenerateNullBasis()};
00244
00245 this->mpLinearSystem->SetNullBasis(nullbasis, 1);
00246
00247 VecDestroy(nullbasis[0]);
00248 mNullSpaceCreated = true;
00249 }
00250 }
00251 else
00252 {
00253
00254
00255
00256 this->mpLinearSystem->SetKspType("gmres");
00257 mpConfig->SetKSPSolver("gmres");
00258
00259
00260 unsigned matrix_size = this->mpLinearSystem->GetSize();
00261 if (!this->mMatrixIsAssembled)
00262 {
00263
00264
00265 std::vector<unsigned> row_for_average;
00266 row_for_average.push_back(mRowForAverageOfPhiZeroed);
00267 this->mpLinearSystem->ZeroMatrixRowsWithValueOnDiagonal(row_for_average, 0.0);
00268 for (unsigned col_index=0; col_index<matrix_size; col_index++)
00269 {
00270 if (col_index%2 == 1)
00271 {
00272 this->mpLinearSystem->SetMatrixElement(mRowForAverageOfPhiZeroed, col_index, 1);
00273 }
00274
00275 }
00276 this->mpLinearSystem->AssembleFinalLhsMatrix();
00277
00278 }
00279
00280 this->mpLinearSystem->SetRhsVectorElement(mRowForAverageOfPhiZeroed, 0);
00281
00282 this->mpLinearSystem->AssembleRhsVector();
00283 }
00284 }
00285
00286 CheckCompatibilityCondition();
00287 }
00288
00289 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00290 void BidomainDg0Assembler<ELEMENT_DIM,SPACE_DIM>::CheckCompatibilityCondition()
00291 {
00292 if (this->mpBoundaryConditions->HasDirichletBoundaryConditions() || mRowForAverageOfPhiZeroed!=INT_MAX )
00293 {
00294
00295 return;
00296 }
00297
00298 #ifndef NDEBUG
00300 ReplicatableVector rep(this->mpLinearSystem->rGetRhsVector());
00301 double sum = 0;
00302 for(unsigned i=1; i<rep.GetSize(); i+=2)
00303 {
00304 sum += rep[i];
00305 }
00306
00307 if(fabs(sum)>1e-6)
00308 {
00309 #define COVERAGE_IGNORE
00310
00311
00312 EXCEPTION("Linear system does not satisfy compatibility constraint!");
00313 #undef COVERAGE_IGNORE
00314 }
00315 #endif
00316 }
00317
00318
00319 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00320 BidomainDg0Assembler<ELEMENT_DIM,SPACE_DIM>::BidomainDg0Assembler(
00321 AbstractTetrahedralMesh<ELEMENT_DIM,SPACE_DIM>* pMesh,
00322 BidomainPde<SPACE_DIM>* pPde,
00323 BoundaryConditionsContainer<ELEMENT_DIM, SPACE_DIM, 2>* pBcc,
00324 unsigned numQuadPoints)
00325 : AbstractAssembler<ELEMENT_DIM,SPACE_DIM,2>(),
00326 BaseClassType(numQuadPoints),
00327 AbstractDynamicAssemblerMixin<ELEMENT_DIM,SPACE_DIM,2>()
00328 {
00329 assert(pPde != NULL);
00330 assert(pMesh != NULL);
00331 assert(pBcc != NULL);
00332
00333 mpBidomainPde = pPde;
00334 this->SetMesh(pMesh);
00335
00336 this->mpBoundaryConditions = pBcc;
00337
00338 mNullSpaceCreated = false;
00339
00340 this->SetMatrixIsConstant();
00341
00342 mRowForAverageOfPhiZeroed = INT_MAX;
00343
00344 mpConfig = HeartConfig::Instance();
00345 }
00346
00347 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00348 BidomainDg0Assembler<ELEMENT_DIM,SPACE_DIM>::~BidomainDg0Assembler()
00349 {
00350 }
00351
00352 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00353 void BidomainDg0Assembler<ELEMENT_DIM,SPACE_DIM>::SetFixedExtracellularPotentialNodes(
00354 std::vector<unsigned> fixedExtracellularPotentialNodes)
00355 {
00356 for (unsigned i=0; i<fixedExtracellularPotentialNodes.size(); i++)
00357 {
00358 if (fixedExtracellularPotentialNodes[i] >= this->mpMesh->GetNumNodes() )
00359 {
00360 EXCEPTION("Fixed node number must be less than total number nodes");
00361 }
00362 }
00363
00364 mFixedExtracellularPotentialNodes = fixedExtracellularPotentialNodes;
00365
00366
00367 this->mpBoundaryConditions->ResetDirichletCommunication();
00368
00369 for (unsigned i=0; i<mFixedExtracellularPotentialNodes.size(); i++)
00370 {
00371 if (this->mpMesh->GetDistributedVectorFactory()->IsGlobalIndexLocal(mFixedExtracellularPotentialNodes[i]))
00372 {
00373 ConstBoundaryCondition<SPACE_DIM>* p_boundary_condition
00374 = new ConstBoundaryCondition<SPACE_DIM>(0.0);
00375
00376
00377 Node<SPACE_DIM>* p_node = this->mpMesh->GetNode(mFixedExtracellularPotentialNodes[i]);
00378
00379 this->mpBoundaryConditions->AddDirichletBoundaryCondition(p_node, p_boundary_condition, 1);
00380
00381 }
00382 }
00383 }
00384
00385 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00386 void BidomainDg0Assembler<ELEMENT_DIM,SPACE_DIM>::SetRowForAverageOfPhiZeroed(unsigned row)
00387 {
00388
00389 if (row%2 == 0)
00390 {
00391 EXCEPTION("Row for applying the constraint 'Average of phi_e = zero' should be odd in C++ like indexing");
00392 }
00393
00394 mRowForAverageOfPhiZeroed = row;
00395 }
00396
00398
00400
00401 template class BidomainDg0Assembler<1,1>;
00402 template class BidomainDg0Assembler<2,2>;
00403 template class BidomainDg0Assembler<3,3>;