AbstractBidomainSolver.cpp

00001 
00002 /*
00003 
00004 Copyright (C) University of Oxford, 2005-2010
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 Chaste is free software: you can redistribute it and/or modify it
00013 under the terms of the GNU Lesser General Public License as published
00014 by the Free Software Foundation, either version 2.1 of the License, or
00015 (at your option) any later version.
00016 
00017 Chaste is distributed in the hope that it will be useful, but WITHOUT
00018 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00019 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
00020 License for more details. The offer of Chaste under the terms of the
00021 License is subject to the License being interpreted in accordance with
00022 English Law and subject to any action against the University of Oxford
00023 being under the jurisdiction of the English Courts.
00024 
00025 You should have received a copy of the GNU Lesser General Public License
00026 along with Chaste. If not, see <http://www.gnu.org/licenses/>.
00027 
00028 */
00029 
00030 
00031 #include "AbstractBidomainSolver.hpp"
00032 #include "TetrahedralMesh.hpp"
00033 
00034 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00035 void AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::InitialiseForSolve(Vec initialSolution)
00036 {
00037     if (this->mpLinearSystem != NULL)
00038     {
00039         return;
00040     }
00041 
00042     // linear system created here
00043     AbstractDynamicLinearPdeSolver<ELEMENT_DIM,SPACE_DIM,2>::InitialiseForSolve(initialSolution);
00044 
00045     if (HeartConfig::Instance()->GetUseAbsoluteTolerance())
00046     {
00047 #ifdef TRACE_KSP
00048         if (PetscTools::AmMaster())
00049         {
00050             std::cout << "Using absolute tolerance: " << mpConfig->GetAbsoluteTolerance() <<"\n";
00051         }
00052 #endif
00053         this->mpLinearSystem->SetAbsoluteTolerance(mpConfig->GetAbsoluteTolerance());
00054     }
00055     else
00056     {
00057 #ifdef TRACE_KSP
00058         if (PetscTools::AmMaster())
00059         {
00060             std::cout << "Using relative tolerance: " << mpConfig->GetRelativeTolerance() <<"\n";
00061         }
00062 #endif
00063         this->mpLinearSystem->SetRelativeTolerance(mpConfig->GetRelativeTolerance());
00064     }
00065 
00066     this->mpLinearSystem->SetKspType(HeartConfig::Instance()->GetKSPSolver());
00067 
00069     if(std::string("twolevelsblockdiagonal") == std::string(HeartConfig::Instance()->GetKSPPreconditioner()))
00070     {
00072         TetrahedralMesh<ELEMENT_DIM,SPACE_DIM>* p_mesh = dynamic_cast<TetrahedralMesh<ELEMENT_DIM,SPACE_DIM>*>(this->mpMesh);
00073         if (p_mesh && PetscTools::IsSequential())
00074         {
00075             boost::shared_ptr<std::vector<PetscInt> > p_bath_nodes(new std::vector<PetscInt>());
00076         
00077             for(unsigned node_index=0; node_index<this->mpMesh->GetNumNodes(); node_index++)
00078             {
00079                 if (this->mpMesh->GetNode(node_index)->GetRegion() == HeartRegionCode::BATH)
00080                 {
00081                     p_bath_nodes->push_back(node_index);
00082                 }
00083             }
00084             
00085             this->mpLinearSystem->SetPcType(HeartConfig::Instance()->GetKSPPreconditioner(), p_bath_nodes);
00086         }
00087         else
00088         {
00089             TERMINATE("Two levels block diagonal only works with TetrahedralMesh and p=1");
00090         }
00091     }
00092     else
00093     { 
00094         this->mpLinearSystem->SetPcType(HeartConfig::Instance()->GetKSPPreconditioner());
00095     }
00096 
00097     if (mRowForAverageOfPhiZeroed==INT_MAX)
00098     {
00099         // not applying average(phi)=0 constraint, so matrix is symmetric
00100         this->mpLinearSystem->SetMatrixIsSymmetric(true);
00101     }
00102     else
00103     {
00104         // applying average(phi)=0 constraint, so matrix is not symmetric
00105         this->mpLinearSystem->SetMatrixIsSymmetric(false);
00106     }
00107 }
00108 
00109 
00110 
00111 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00112 void AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::PrepareForSetupLinearSystem(Vec existingSolution)
00113 {
00114     double time = PdeSimulationTime::GetTime();
00115     mpBidomainTissue->SolveCellSystems(existingSolution, time, time+this->mDt);
00116 }
00117 
00118 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00119 Vec AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::GenerateNullBasis() const
00120 {
00121     double sqrt_num_nodes = sqrt((double) this->mpMesh->GetNumNodes());
00122 
00123     Vec null_basis;
00124     DistributedVectorFactory* p_factory = this->mpMesh->GetDistributedVectorFactory();
00125     null_basis = p_factory->CreateVec(2);
00126 
00127     DistributedVector dist_null_basis = p_factory->CreateDistributedVector(null_basis);
00128     DistributedVector::Stripe null_basis_stripe_0(dist_null_basis,0);
00129     DistributedVector::Stripe null_basis_stripe_1(dist_null_basis,1);
00130     for (DistributedVector::Iterator index = dist_null_basis.Begin();
00131          index != dist_null_basis.End();
00132          ++index)
00133     {
00134         null_basis_stripe_0[index] = 0.0;
00135         null_basis_stripe_1[index] = 1.0/sqrt_num_nodes; // normalised vector
00136     }
00137     dist_null_basis.Restore();
00138 
00139     return null_basis;
00140 }
00141 
00142 
00143 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00144 void AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::FinaliseLinearSystem(Vec existingSolution)
00145 {
00146     // If no dirichlet boundary conditions
00147     //  (i) Check compatibility condition to check we are solving
00148     //      a linear system that can be solved
00149     // Then either
00150     //  (a) If not setting average(phi)=0, we are solving a singular system,
00151     //      so set up a null space
00152     //  (b) Apply average(phi)=0 constraint by altering the last row, to
00153     //      get a non-singular system
00154     //
00155     if (!(GetBoundaryConditions()->HasDirichletBoundaryConditions()))
00156     {
00157         // first check compatibility condition
00158         CheckCompatibilityCondition();
00159 
00160         // Check whether applying average(phi_e)=0 constraint
00161         if (mRowForAverageOfPhiZeroed==INT_MAX)
00162         {
00163             // We're not using the constraint, hence use a null space
00164             if (!mNullSpaceCreated)
00165             {
00166                 // No null space set up, so create one and pass it to the linear system
00167                 Vec null_basis[] = {GenerateNullBasis()};
00168 
00169                 this->mpLinearSystem->SetNullBasis(null_basis, 1);
00170 
00171                 VecDestroy(null_basis[0]);
00172                 mNullSpaceCreated = true;
00173             }
00174         }
00175         else
00176         {
00177             // Using the average(phi_e)=0 constraint
00178 
00179             // CG (default solver) won't work since the system isn't symmetric anymore. Switch to GMRES
00180             this->mpLinearSystem->SetKspType("gmres"); // Switches the solver
00181             mpConfig->SetKSPSolver("gmres"); // Makes sure this change will be reflected in the XML file written to disk at the end of the simulation.
00182 
00183             // Set average phi_e to zero
00184             unsigned matrix_size = this->mpLinearSystem->GetSize();
00185             if (!this->mMatrixIsAssembled)
00186             {
00187 
00188                 // Set the mRowForAverageOfPhiZeroed-th matrix row to 0 1 0 1 ...
00189                 std::vector<unsigned> row_for_average;
00190                 row_for_average.push_back(mRowForAverageOfPhiZeroed);
00191                 this->mpLinearSystem->ZeroMatrixRowsWithValueOnDiagonal(row_for_average, 0.0);
00192                 for (unsigned col_index=0; col_index<matrix_size; col_index++)
00193                 {
00194                     if (col_index%2 == 1)
00195                     {
00196                         this->mpLinearSystem->SetMatrixElement(mRowForAverageOfPhiZeroed, col_index, 1);
00197                     }
00198 
00199                 }
00200                 this->mpLinearSystem->AssembleFinalLhsMatrix();
00201 
00202             }
00203             // Set the mRowForAverageOfPhiZeroed-th rhs vector row to 0
00204             this->mpLinearSystem->SetRhsVectorElement(mRowForAverageOfPhiZeroed, 0);
00205 
00206             this->mpLinearSystem->AssembleRhsVector();
00207         }
00208     }
00209 }
00210 
00211 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00212 void AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::CheckCompatibilityCondition()
00213 {
00214     if (GetBoundaryConditions()->HasDirichletBoundaryConditions() )
00215     {
00216         // not a singular system, no compability condition
00217         return;
00218     }
00219 
00220 #ifndef NDEBUG
00222     ReplicatableVector rep(this->mpLinearSystem->rGetRhsVector());
00223     double sum = 0;
00224     for(unsigned i=1; i<rep.GetSize(); i+=2) // i=1,3,5,..  ie all the phi_e components
00225     {
00226         sum += rep[i];
00227     }
00228 
00229     if(fabs(sum)>1e-6) // magic number! sum should really be a sum of zeros and exactly zero though anyway (or a-a+b-b+c-c.. etc in the case of electrodes)
00230     {
00231         #define COVERAGE_IGNORE
00232         // shouldn't ever reach this line but useful to have the error printed out if you do
00233         //std::cout << "Sum of b_{2i+1} = " << sum << " (should be zero for compatibility)\n";
00234         EXCEPTION("Linear system does not satisfy compatibility constraint!");
00235         #undef COVERAGE_IGNORE
00236     }
00237 #endif
00238 }
00239 
00240 
00241 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00242 AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::AbstractBidomainSolver(
00243             bool bathSimulation,
00244             AbstractTetrahedralMesh<ELEMENT_DIM,SPACE_DIM>* pMesh,
00245             BidomainTissue<SPACE_DIM>* pTissue,
00246             BoundaryConditionsContainer<ELEMENT_DIM,SPACE_DIM,2>* pBoundaryConditions,
00247             unsigned numQuadPoints)
00248     : AbstractDynamicLinearPdeSolver<ELEMENT_DIM,SPACE_DIM,2>(pMesh),
00249       mBathSimulation(bathSimulation),
00250       mpBidomainTissue(pTissue),
00251       mpBoundaryConditions(pBoundaryConditions),
00252       mNumQuadPoints(numQuadPoints)
00253 {
00254     assert(pTissue != NULL);
00255     assert(pBoundaryConditions != NULL);
00256 
00257     mNullSpaceCreated = false;
00258 
00259     // important!
00260     this->mMatrixIsConstant = true;
00261 
00262     mRowForAverageOfPhiZeroed = INT_MAX; //this->mpLinearSystem->GetSize() - 1;
00263     mpConfig = HeartConfig::Instance();
00264 
00265     mpBidomainAssembler = NULL; // can't initialise until know what dt is
00266 }
00267 
00268 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00269 AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::~AbstractBidomainSolver()
00270 {
00271     if(mpBidomainAssembler)
00272     {
00273         delete mpBidomainAssembler;
00274     }
00275 }
00276 
00277 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00278 void AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::SetFixedExtracellularPotentialNodes(
00279             std::vector<unsigned> fixedExtracellularPotentialNodes)
00280 {
00281     for (unsigned i=0; i<fixedExtracellularPotentialNodes.size(); i++)
00282     {
00283         if (fixedExtracellularPotentialNodes[i] >= this->mpMesh->GetNumNodes() )
00284         {
00285             EXCEPTION("Fixed node number must be less than total number nodes");
00286         }
00287     }
00288 
00289     mFixedExtracellularPotentialNodes = fixedExtracellularPotentialNodes;
00290 
00291     // We will need to recalculate this when HasDirichletBoundaryConditions() is called.
00292     GetBoundaryConditions()->ResetDirichletCommunication();
00293 
00294     for (unsigned i=0; i<mFixedExtracellularPotentialNodes.size(); i++)
00295     {
00296         if (this->mpMesh->GetDistributedVectorFactory()->IsGlobalIndexLocal(mFixedExtracellularPotentialNodes[i]))
00297         {
00298             ConstBoundaryCondition<SPACE_DIM>* p_boundary_condition
00299                  = new ConstBoundaryCondition<SPACE_DIM>(0.0);
00300 
00301             //Throws if node is not owned locally
00302             Node<SPACE_DIM>* p_node = this->mpMesh->GetNode(mFixedExtracellularPotentialNodes[i]);
00303 
00304             GetBoundaryConditions()->AddDirichletBoundaryCondition(p_node, p_boundary_condition, 1);
00305 
00306         }
00307     }
00308 }
00309 
00310 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00311 void AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::SetRowForAverageOfPhiZeroed(unsigned row)
00312 {
00313     // Row should be odd in C++-like indexing
00314     if (row%2 == 0)
00315     {
00316         EXCEPTION("Row for applying the constraint 'Average of phi_e = zero' should be odd in C++ like indexing");
00317     }
00318 
00319     mRowForAverageOfPhiZeroed = row;
00320 }
00321 
00322 
00323 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00324 void AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::FinaliseForBath(bool computeMatrix, bool computeVector)
00325 {
00326     assert(mBathSimulation);
00327     
00329     // CG (default solver) won't work since the system is indefinite. Switch to SYMMLQ
00330 //    this->mpLinearSystem->SetKspType("symmlq"); // Switches the solver
00331 //    this->mpConfig->SetKSPSolver("symmlq"); // Makes sure this change will be reflected in the XML file written to disk at the end of the simulation.
00332 
00333     unsigned* is_node_bath = new unsigned[this->mpMesh->GetNumNodes()];
00334     for(unsigned i = 0; i < this->mpMesh->GetNumNodes(); ++i)
00335     {
00336         is_node_bath[i] = 0;
00337     }
00338 
00339     for (typename AbstractTetrahedralMesh<ELEMENT_DIM,SPACE_DIM>::NodeIterator iter=this->mpMesh->GetNodeIteratorBegin();
00340          iter != this->mpMesh->GetNodeIteratorEnd();
00341          ++iter)
00342     {
00348         if ((*iter).GetRegion() == HeartRegionCode::BATH)
00349         {
00350             is_node_bath[(*iter).GetIndex()] = 1;
00351         }
00352     }
00353 
00354     unsigned* is_node_bath_reduced = new unsigned[this->mpMesh->GetNumNodes()];
00355     MPI_Allreduce(is_node_bath, is_node_bath_reduced, this->mpMesh->GetNumNodes(), MPI_UNSIGNED, MPI_SUM, PETSC_COMM_WORLD);
00356 
00357     for(unsigned i=0; i<this->mpMesh->GetNumNodes(); i++)
00358     {
00359         if(is_node_bath_reduced[i] > 0) // ie is a bath node
00360         {
00361             PetscInt index[1];
00362             index[0] = 2*i; // assumes Vm and Phie are interleaved
00363 
00364             if(computeMatrix)
00365             {
00366                 /*
00367                  *  Before revision 6516, we used to zero out i-th row and column here. It seems to be redundant because they are already zero after assembly.
00368                  *  When assembling a bath element you get a matrix subblock that looks like (2D example):
00369                  *
00370                  *     Vm   0 0 0 0 0 0
00371                  *     Vm   0 0 0 0 0 0
00372                  *     Vm   0 0 0 0 0 0
00373                  *     Phie 0 0 0 x x x
00374                  *     Phie 0 0 0 x x x  -> the x subblock is assembled from div(grad_phi) = 0
00375                  *     Phie 0 0 0 x x x
00376                  *
00377                  *  Therefore, all the Vm entries of this node are already 0.
00378                  *
00379                  *  Explicitly checking it in non-production builds.
00380                  */
00381 #ifndef NDEBUG
00382                 int num_equation = 2*i; // assumes Vm and Phie are interleaved
00383 
00384                 // Matrix need to be assembled in order to use GetMatrixElement()
00385                 MatAssemblyBegin(this->mpLinearSystem->rGetLhsMatrix(), MAT_FINAL_ASSEMBLY);
00386                 MatAssemblyEnd(this->mpLinearSystem->rGetLhsMatrix(), MAT_FINAL_ASSEMBLY);
00387 
00388                 PetscInt local_lo, local_hi;
00389                 this->mpLinearSystem->GetOwnershipRange(local_lo, local_hi);
00390 
00391                 // If this processor owns i-th row, check it.
00392                 if ((local_lo <= (int)num_equation) && ((int)num_equation < local_hi))
00393                 {
00394                     for (unsigned column=0; column < this->mpLinearSystem->GetSize(); column++)
00395                     {
00396                         assert(this->mpLinearSystem->GetMatrixElement(num_equation, column)==0.0);
00397                     }
00398                 }
00399 
00400                 // Check the local entries of the i-th column
00401                 for (int row=local_lo; row<local_hi; row++)
00402                 {
00403                     assert(this->mpLinearSystem->GetMatrixElement(row, num_equation)==0);
00404                 }
00405 #endif
00406                 // put 1.0 on the diagonal
00407                 Mat& r_matrix = this->mpLinearSystem->rGetLhsMatrix();
00408                 MatSetValue(r_matrix,index[0],index[0],1.0,INSERT_VALUES);
00409             }
00410 
00411             if(computeVector)
00412             {
00413                 // zero rhs vector entry
00414                 VecSetValue(this->mpLinearSystem->rGetRhsVector(), index[0], 0.0, INSERT_VALUES);
00415             }
00416         }
00417     }
00418 
00419     delete[] is_node_bath;
00420     delete[] is_node_bath_reduced;
00421 }
00422 
00423 
00425 // explicit instantiation
00427 
00428 template class AbstractBidomainSolver<1,1>;
00429 template class AbstractBidomainSolver<2,2>;
00430 template class AbstractBidomainSolver<3,3>;

Generated on Mon Nov 1 12:35:17 2010 for Chaste by  doxygen 1.5.5