AbstractBidomainSolver.cpp

00001 /*
00002 
00003 Copyright (c) 2005-2015, 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 
00037 #include "AbstractBidomainSolver.hpp"
00038 #include "TetrahedralMesh.hpp"
00039 #include "PetscMatTools.hpp"
00040 #include "PetscVecTools.hpp"
00041 
00042 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00043 void AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::InitialiseForSolve(Vec initialSolution)
00044 {
00045     if (this->mpLinearSystem != NULL)
00046     {
00047         return;
00048     }
00049 
00050     // linear system created here
00051     AbstractDynamicLinearPdeSolver<ELEMENT_DIM,SPACE_DIM,2>::InitialiseForSolve(initialSolution);
00052 
00053     if (HeartConfig::Instance()->GetUseAbsoluteTolerance())
00054     {
00055 #ifdef TRACE_KSP
00056         if (PetscTools::AmMaster())
00057         {
00058             std::cout << "Using absolute tolerance: " << mpConfig->GetAbsoluteTolerance() <<"\n";
00059         }
00060 #endif
00061         this->mpLinearSystem->SetAbsoluteTolerance(mpConfig->GetAbsoluteTolerance());
00062     }
00063     else
00064     {
00065 #ifdef TRACE_KSP
00066         if (PetscTools::AmMaster())
00067         {
00068             std::cout << "Using relative tolerance: " << mpConfig->GetRelativeTolerance() <<"\n";
00069         }
00070 #endif
00071         this->mpLinearSystem->SetRelativeTolerance(mpConfig->GetRelativeTolerance());
00072     }
00073 
00074     this->mpLinearSystem->SetKspType(HeartConfig::Instance()->GetKSPSolver());
00075 
00077     if(std::string("twolevelsblockdiagonal") == std::string(HeartConfig::Instance()->GetKSPPreconditioner()))
00078     {
00080         TetrahedralMesh<ELEMENT_DIM,SPACE_DIM>* p_mesh = dynamic_cast<TetrahedralMesh<ELEMENT_DIM,SPACE_DIM>*>(this->mpMesh);
00081         if (p_mesh && PetscTools::IsSequential())
00082         {
00083             boost::shared_ptr<std::vector<PetscInt> > p_bath_nodes(new std::vector<PetscInt>());
00084 
00085             for(unsigned node_index=0; node_index<this->mpMesh->GetNumNodes(); node_index++)
00086             {
00087                 if (HeartRegionCode::IsRegionBath( this->mpMesh->GetNode(node_index)->GetRegion() ))
00088                 {
00089                     p_bath_nodes->push_back(node_index);
00090                 }
00091             }
00092 
00093             this->mpLinearSystem->SetPcType(HeartConfig::Instance()->GetKSPPreconditioner(), p_bath_nodes);
00094         }
00095         else
00096         {
00097             TERMINATE("Two levels block diagonal only works with TetrahedralMesh and p=1");
00098         }
00099     }
00100     else
00101     {
00102         this->mpLinearSystem->SetPcType(HeartConfig::Instance()->GetKSPPreconditioner());
00103     }
00104 
00105     if (mRowForAverageOfPhiZeroed==INT_MAX)
00106     {
00107         // not applying average(phi)=0 constraint, so matrix is symmetric
00108         this->mpLinearSystem->SetMatrixIsSymmetric(true);
00109     }
00110     else
00111     {
00112         //Turn off preallocation so that we can have one dense row in the matrix.
00113         PetscMatTools::TurnOffVariableAllocationError(this->mpLinearSystem->rGetLhsMatrix());
00114 
00115         // applying average(phi)=0 constraint, so matrix is not symmetric
00116         this->mpLinearSystem->SetMatrixIsSymmetric(false);
00117     }
00118 
00119     this->mpLinearSystem->SetUseFixedNumberIterations(HeartConfig::Instance()->GetUseFixedNumberIterationsLinearSolver(), HeartConfig::Instance()->GetEvaluateNumItsEveryNSolves());
00120 }
00121 
00122 
00123 
00124 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00125 void AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::PrepareForSetupLinearSystem(Vec existingSolution)
00126 {
00127     mpBidomainTissue->SolveCellSystems(existingSolution, PdeSimulationTime::GetTime(), PdeSimulationTime::GetNextTime());
00128 }
00129 
00130 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00131 Vec AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::GenerateNullBasis() const
00132 {
00133     double sqrt_num_nodes = sqrt((double) this->mpMesh->GetNumNodes());
00134 
00135     Vec null_basis;
00136     DistributedVectorFactory* p_factory = this->mpMesh->GetDistributedVectorFactory();
00137     null_basis = p_factory->CreateVec(2);
00138 
00139     DistributedVector dist_null_basis = p_factory->CreateDistributedVector(null_basis);
00140     DistributedVector::Stripe null_basis_stripe_0(dist_null_basis,0);
00141     DistributedVector::Stripe null_basis_stripe_1(dist_null_basis,1);
00142     for (DistributedVector::Iterator index = dist_null_basis.Begin();
00143          index != dist_null_basis.End();
00144          ++index)
00145     {
00146         null_basis_stripe_0[index] = 0.0;
00147         null_basis_stripe_1[index] = 1.0/sqrt_num_nodes; // normalised vector
00148     }
00149     dist_null_basis.Restore();
00150 
00151     return null_basis;
00152 }
00153 
00154 
00155 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00156 void AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::FinaliseLinearSystem(Vec existingSolution)
00157 {
00158     // If no dirichlet boundary conditions
00159     //  (i) Check compatibility condition to check we are solving
00160     //      a linear system that can be solved
00161     // Then either
00162     //  (a) If not setting average(phi)=0, we are solving a singular system,
00163     //      so set up a null space
00164     //  (b) Apply average(phi)=0 constraint by altering the last row, to
00165     //      get a non-singular system
00166     //
00167     if (!(GetBoundaryConditions()->HasDirichletBoundaryConditions()))
00168     {
00169         // first check compatibility condition
00170         CheckCompatibilityCondition();
00171 
00172         // Check whether applying average(phi_e)=0 constraint
00173         if (mRowForAverageOfPhiZeroed==INT_MAX)
00174         {
00175             // We're not using the constraint, hence use a null space
00176             if (!mNullSpaceCreated)
00177             {
00178                 // No null space set up, so create one and pass it to the linear system
00179                 Vec null_basis[] = {GenerateNullBasis()};
00180 
00181                 this->mpLinearSystem->SetNullBasis(null_basis, 1);
00182 
00183                 PetscTools::Destroy(null_basis[0]);
00184                 mNullSpaceCreated = true;
00185             }
00186         }
00187         else
00188         {
00189             // Using the average(phi_e)=0 constraint
00190 
00191             // CG (default solver) won't work since the system isn't symmetric anymore. Switch to GMRES
00192             this->mpLinearSystem->SetKspType("gmres"); // Switches the solver
00193             mpConfig->SetKSPSolver("gmres", true); // Makes sure this change will be reflected in the XML file written to disk at the end of the simulation.
00194             //(If the user doesn't have gmres then the "true" warns the user about the switch)
00195 
00196             // Set average phi_e to zero
00197             unsigned matrix_size = this->mpLinearSystem->GetSize();
00198             if (!this->mMatrixIsAssembled)
00199             {
00200 
00201                 // Set the mRowForAverageOfPhiZeroed-th matrix row to 0 1 0 1 ...
00202                 std::vector<unsigned> row_for_average;
00203                 row_for_average.push_back(mRowForAverageOfPhiZeroed);
00204                 this->mpLinearSystem->ZeroMatrixRowsWithValueOnDiagonal(row_for_average, 0.0);
00205                 for (unsigned col_index=0; col_index<matrix_size; col_index++)
00206                 {
00207                     if (col_index%2 == 1)
00208                     {
00209                         this->mpLinearSystem->SetMatrixElement(mRowForAverageOfPhiZeroed, col_index, 1);
00210                     }
00211 
00212                 }
00213                 this->mpLinearSystem->FinaliseLhsMatrix();
00214 
00215             }
00216             // Set the mRowForAverageOfPhiZeroed-th rhs vector row to 0
00217             this->mpLinearSystem->SetRhsVectorElement(mRowForAverageOfPhiZeroed, 0);
00218 
00219             this->mpLinearSystem->FinaliseRhsVector();
00220         }
00221     }
00222 }
00223 
00224 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00225 void AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::CheckCompatibilityCondition()
00226 {
00227     if (GetBoundaryConditions()->HasDirichletBoundaryConditions() || this->mRowForAverageOfPhiZeroed!=INT_MAX)
00228     {
00229         // not a singular system, no compatibility condition
00230         return;
00231     }
00232 
00233 #ifndef NDEBUG
00234     DistributedVector distributed_rhs=this->mpMesh->GetDistributedVectorFactory()->CreateDistributedVector(this->mpLinearSystem->rGetRhsVector());
00235     DistributedVector::Stripe distributed_rhs_phi_entries(distributed_rhs,1);
00236 
00237     double local_sum=0;
00238     for (DistributedVector::Iterator index = distributed_rhs.Begin();
00239          index!= distributed_rhs.End();
00240          ++index)
00241     {
00242         local_sum += distributed_rhs_phi_entries[index];
00243     }
00244 
00245     double global_sum;
00246     int mpi_ret = MPI_Allreduce(&local_sum, &global_sum, 1, MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD);
00247     assert(mpi_ret == MPI_SUCCESS);
00248 
00249     if(fabs(global_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)
00250     {
00251         #define COVERAGE_IGNORE
00252         // shouldn't ever reach this line but useful to have the error printed out if you do
00253         //std::cout << "Sum of b_{2i+1} = " << global_sum << " (should be zero for compatibility)\n";
00254         EXCEPTION("Linear system does not satisfy compatibility constraint!");
00255         #undef COVERAGE_IGNORE
00256     }
00257 #endif
00258 }
00259 
00260 
00261 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00262 AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::AbstractBidomainSolver(
00263             bool bathSimulation,
00264             AbstractTetrahedralMesh<ELEMENT_DIM,SPACE_DIM>* pMesh,
00265             BidomainTissue<SPACE_DIM>* pTissue,
00266             BoundaryConditionsContainer<ELEMENT_DIM,SPACE_DIM,2>* pBoundaryConditions)
00267     : AbstractDynamicLinearPdeSolver<ELEMENT_DIM,SPACE_DIM,2>(pMesh),
00268       mBathSimulation(bathSimulation),
00269       mpBidomainTissue(pTissue),
00270       mpBoundaryConditions(pBoundaryConditions)
00271 {
00272     assert(pTissue != NULL);
00273     assert(pBoundaryConditions != NULL);
00274 
00275     mNullSpaceCreated = false;
00276 
00277     // important!
00278     this->mMatrixIsConstant = true;
00279 
00280     mRowForAverageOfPhiZeroed = INT_MAX; //this->mpLinearSystem->GetSize() - 1;
00281     mpConfig = HeartConfig::Instance();
00282 }
00283 
00284 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00285 AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::~AbstractBidomainSolver()
00286 {
00287 }
00288 
00289 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00290 void AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::SetFixedExtracellularPotentialNodes(
00291             std::vector<unsigned> fixedExtracellularPotentialNodes)
00292 {
00293     for (unsigned i=0; i<fixedExtracellularPotentialNodes.size(); i++)
00294     {
00295         if (fixedExtracellularPotentialNodes[i] >= this->mpMesh->GetNumNodes() )
00296         {
00297             EXCEPTION("Fixed node number must be less than total number nodes");
00298         }
00299     }
00300 
00301     mFixedExtracellularPotentialNodes = fixedExtracellularPotentialNodes;
00302 
00303     // We will need to recalculate this when HasDirichletBoundaryConditions() is called.
00304     GetBoundaryConditions()->ResetDirichletCommunication();
00305 
00306     for (unsigned i=0; i<mFixedExtracellularPotentialNodes.size(); i++)
00307     {
00308         if (this->mpMesh->GetDistributedVectorFactory()->IsGlobalIndexLocal(mFixedExtracellularPotentialNodes[i]))
00309         {
00310             ConstBoundaryCondition<SPACE_DIM>* p_boundary_condition
00311                  = new ConstBoundaryCondition<SPACE_DIM>(0.0);
00312 
00313             //Throws if node is not owned locally
00314             Node<SPACE_DIM>* p_node = this->mpMesh->GetNode(mFixedExtracellularPotentialNodes[i]);
00315 
00316             GetBoundaryConditions()->AddDirichletBoundaryCondition(p_node, p_boundary_condition, 1);
00317 
00318         }
00319     }
00320 }
00321 
00322 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00323 void AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::SetRowForAverageOfPhiZeroed(unsigned row)
00324 {
00325     // Row should be odd in C++-like indexing
00326     if (row%2 == 0)
00327     {
00328         EXCEPTION("Row for applying the constraint 'Average of phi_e = zero' should be odd in C++ like indexing");
00329     }
00330 
00331     mRowForAverageOfPhiZeroed = row;
00332 }
00333 
00334 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00335 void AbstractBidomainSolver<ELEMENT_DIM,SPACE_DIM>::FinaliseForBath(bool computeMatrix, bool computeVector)
00336 {
00337     assert(mBathSimulation);
00338     PetscBool is_matrix_assembled;
00339     MatAssembled(this->mpLinearSystem->GetLhsMatrix(), &is_matrix_assembled);
00340     assert(is_matrix_assembled == PETSC_TRUE);
00341 
00342     /*
00343      *  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.
00344      *  When assembling a bath element you get a matrix subblock that looks like (2D example):
00345      *
00346      *     Vm   0 0 0 0 0 0
00347      *     Vm   0 0 0 0 0 0
00348      *     Vm   0 0 0 0 0 0
00349      *     Phie 0 0 0 x x x
00350      *     Phie 0 0 0 x x x  -> the x subblock is assembled from div(grad_phi) = 0
00351      *     Phie 0 0 0 x x x
00352      *
00353      *  Therefore, all the Vm entries of this node are already 0.
00354      *
00355      *  Explicitly checking it in non-production builds.
00356      */
00357 #ifndef NDEBUG
00358     if(computeMatrix)
00359     {
00360         for (typename AbstractTetrahedralMesh<ELEMENT_DIM,SPACE_DIM>::NodeIterator iter=this->mpMesh->GetNodeIteratorBegin();
00361              iter != this->mpMesh->GetNodeIteratorEnd();
00362              ++iter)
00363         {
00364             if (HeartRegionCode::IsRegionBath( (*iter).GetRegion() ))
00365             {
00366                 int num_equation = 2*iter->GetIndex(); // assumes Vm and Phie are interleaved
00367 
00368                 PetscInt local_lo, local_hi;
00369                 this->mpLinearSystem->GetOwnershipRange(local_lo, local_hi);
00370 
00371                 // If this processor owns i-th row, check it.
00372                 if ((local_lo <= (int)num_equation) && ((int)num_equation < local_hi))
00373                 {
00374                     for (unsigned column=0; column < this->mpLinearSystem->GetSize(); column++)
00375                     {
00376                         assert(this->mpLinearSystem->GetMatrixElement(num_equation, column)==0.0);
00377                     }
00378                 }
00379 
00380                 // Check the local entries of the i-th column
00381                 for (int row=local_lo; row<local_hi; row++)
00382                 {
00383                     assert(this->mpLinearSystem->GetMatrixElement(row, num_equation)==0);
00384                 }
00385             }
00386         }
00387     }
00388 #endif
00389 
00390     /*
00391      *  These two loops are decoupled because interleaving calls to GetMatrixElement and MatSetValue
00392      *  require reassembling the matrix before GetMatrixElement which generates massive communication
00393      *  overhead for large models and/or large core counts.
00394      */
00395 
00396     for (typename AbstractTetrahedralMesh<ELEMENT_DIM,SPACE_DIM>::NodeIterator iter=this->mpMesh->GetNodeIteratorBegin();
00397          iter != this->mpMesh->GetNodeIteratorEnd();
00398          ++iter)
00399     {
00400         if (HeartRegionCode::IsRegionBath((*iter).GetRegion() ))
00401         {
00402             PetscInt index = 2*iter->GetIndex(); // assumes Vm and Phie are interleaved
00403 
00404             if(computeMatrix)
00405             {
00406                 // put 1.0 on the diagonal
00407                 PetscMatTools::SetElement(this->mpLinearSystem->rGetLhsMatrix(), index, index, 1.0);
00408             }
00409 
00410             if(computeVector)
00411             {
00412                 // zero rhs vector entry
00413                 PetscVecTools::SetElement(this->mpLinearSystem->rGetRhsVector(), index, 0.0);
00414             }
00415         }
00416     }
00417 }
00418 
00420 // explicit instantiation
00422 
00423 template class AbstractBidomainSolver<1,1>;
00424 template class AbstractBidomainSolver<2,2>;
00425 template class AbstractBidomainSolver<3,3>;

Generated by  doxygen 1.6.2