Chaste Release::3.1
AbstractExtendedBidomainSolver.cpp
00001 /*
00002 
00003 Copyright (c) 2005-2012, 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 #include "AbstractExtendedBidomainSolver.hpp"
00037 
00038 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00039 void AbstractExtendedBidomainSolver<ELEMENT_DIM,SPACE_DIM>::InitialiseForSolve(Vec initialSolution)
00040 {
00041     if (this->mpLinearSystem != NULL)
00042     {
00043         return;
00044     }
00045 
00046     // linear system created here
00047     AbstractDynamicLinearPdeSolver<ELEMENT_DIM,SPACE_DIM,3>::InitialiseForSolve(initialSolution);
00048 
00049     if (HeartConfig::Instance()->GetUseAbsoluteTolerance())
00050     {
00051 #ifdef TRACE_KSP
00052         std::cout << "Using absolute tolerance: " << mpConfig->GetAbsoluteTolerance() <<"\n";
00053 #endif
00054         this->mpLinearSystem->SetAbsoluteTolerance(mpConfig->GetAbsoluteTolerance());
00055     }
00056     else
00057     {
00058 #ifdef TRACE_KSP
00059         std::cout << "Using relative tolerance: " << mpConfig->GetRelativeTolerance() <<"\n";
00060 #endif
00061         this->mpLinearSystem->SetRelativeTolerance(mpConfig->GetRelativeTolerance());
00062     }
00063 
00064     this->mpLinearSystem->SetKspType(HeartConfig::Instance()->GetKSPSolver());
00065     this->mpLinearSystem->SetPcType(HeartConfig::Instance()->GetKSPPreconditioner());
00066 
00067     if (mRowForAverageOfPhiZeroed==INT_MAX)
00068     {
00069         // not applying average(phi)=0 constraint, so matrix is symmetric
00070         this->mpLinearSystem->SetMatrixIsSymmetric(true);
00071     }
00072     else
00073     {
00074         // applying average(phi)=0 constraint, so matrix is not symmetric
00075         this->mpLinearSystem->SetMatrixIsSymmetric(false);
00076     }
00077 }
00078 
00079 
00080 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00081 void AbstractExtendedBidomainSolver<ELEMENT_DIM,SPACE_DIM>::PrepareForSetupLinearSystem(Vec existingSolution)
00082 {
00083     double time = PdeSimulationTime::GetTime();
00084     double delta_t = PdeSimulationTime::GetPdeTimeStep();
00085     mpExtendedBidomainTissue->SolveCellSystems(existingSolution, time, time + delta_t);
00086 }
00087 
00088 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00089 Vec AbstractExtendedBidomainSolver<ELEMENT_DIM,SPACE_DIM>::GenerateNullBasis() const
00090 {
00091     double sqrrt_num_nodes = sqrt(  this->mpMesh->GetNumNodes());
00092     double normalised_basis_value = 1.0 / sqrrt_num_nodes;
00093 
00094     Vec nullbasis;
00095     DistributedVectorFactory* p_factory = this->mpMesh->GetDistributedVectorFactory();
00096     nullbasis=p_factory->CreateVec(3);
00097     DistributedVector dist_null_basis = p_factory->CreateDistributedVector(nullbasis);
00098     DistributedVector::Stripe null_basis_stripe_0(dist_null_basis,0);
00099     DistributedVector::Stripe null_basis_stripe_1(dist_null_basis,1);
00100     DistributedVector::Stripe null_basis_stripe_2(dist_null_basis,2);
00101     for (DistributedVector::Iterator index = dist_null_basis.Begin();
00102          index != dist_null_basis.End();
00103          ++index)
00104     {
00105         null_basis_stripe_0[index] = 0.0;
00106         null_basis_stripe_1[index] = 0.0;
00107         null_basis_stripe_2[index] = normalised_basis_value;
00108     }
00109     dist_null_basis.Restore();
00110     return nullbasis;
00111 }
00112 
00113 
00114 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00115 void AbstractExtendedBidomainSolver<ELEMENT_DIM,SPACE_DIM>::FinaliseLinearSystem(Vec existingSolution)
00116 {
00117     if (!(this->mpBoundaryConditions->HasDirichletBoundaryConditions()))
00118     {
00119         // We're not pinning any nodes.
00120         if (mRowForAverageOfPhiZeroed==INT_MAX)
00121         {
00122             // We're not using the 'Average phi_e = 0' method, hence use a null space
00123             if (!mNullSpaceCreated)
00124             {
00125                 // No null space set up, so create one and pass it to the linear system
00126                 Vec nullbasis[] = {GenerateNullBasis()};
00127 
00128                 this->mpLinearSystem->SetNullBasis(nullbasis, 1);
00129 
00130                 PetscTools::Destroy(nullbasis[0]);
00131                 mNullSpaceCreated = true;
00132             }
00133         }
00134         else  // mRowForAverageOfPhiZeroed!=INT_MAX, i.e. we're using the 'Average phi_e = 0' method
00135         {
00136             // CG (default solver) won't work since the system isn't symmetric anymore. Switch to GMRES
00137             this->mpLinearSystem->SetKspType("gmres"); // Switches the solver
00138             mpConfig->SetKSPSolver("gmres"); // Makes sure this change will be reflected in the XML file written to disk at the end of the simulation.
00139 
00140             // Set average phi_e to zero
00141             unsigned matrix_size = this->mpLinearSystem->GetSize();
00142             if (!this->mMatrixIsAssembled)
00143             {
00144 
00145                 // Set the mRowForAverageOfPhiZeroed-th matrix row to 0 0 1 0 0 1 ...
00146                 std::vector<unsigned> row_for_average;
00147                 row_for_average.push_back(mRowForAverageOfPhiZeroed);
00148                 this->mpLinearSystem->ZeroMatrixRowsWithValueOnDiagonal(row_for_average, 0.0);
00149                 for (unsigned col_index=0; col_index<matrix_size; col_index++)
00150                 {
00151                     if (((col_index-2)%3 == 0) && (col_index>1))//phi_e column indices are 2,5,8,11 etc....
00152                     {
00153                         this->mpLinearSystem->SetMatrixElement(mRowForAverageOfPhiZeroed, col_index, 1);
00154                     }
00155 
00156                 }
00157                 this->mpLinearSystem->FinaliseLhsMatrix();
00158             }
00159             // Set the mRowForAverageOfPhiZeroed-th rhs vector row to 0
00160             this->mpLinearSystem->SetRhsVectorElement(mRowForAverageOfPhiZeroed, 0);
00161 
00162             this->mpLinearSystem->FinaliseRhsVector();
00163         }
00164     }
00165     CheckCompatibilityCondition();
00166 }
00167 
00168 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00169 void AbstractExtendedBidomainSolver<ELEMENT_DIM,SPACE_DIM>::CheckCompatibilityCondition()
00170 {
00171     if (this->mpBoundaryConditions->HasDirichletBoundaryConditions() || mRowForAverageOfPhiZeroed!=INT_MAX )
00172     {
00173         // not a singular system, no compability condition
00174         return;
00175     }
00176 
00177 #ifndef NDEBUG
00178     DistributedVector distributed_rhs=this->mpMesh->GetDistributedVectorFactory()->CreateDistributedVector(this->mpLinearSystem->rGetRhsVector());
00179     DistributedVector::Stripe distributed_rhs_phi_entries(distributed_rhs,2); // stripe number 2 -> phi_e
00180 
00181     double local_sum=0;
00182     for (DistributedVector::Iterator index = distributed_rhs.Begin();
00183          index!= distributed_rhs.End();
00184          ++index)
00185     {
00186         local_sum += distributed_rhs_phi_entries[index];
00187     }
00188 
00189     double global_sum;
00190     int mpi_ret = MPI_Allreduce(&local_sum, &global_sum, 1, MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD);
00191     assert(mpi_ret == MPI_SUCCESS);
00192 
00193     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)
00194     {
00195         #define COVERAGE_IGNORE
00196         // shouldn't ever reach this line but useful to have the error printed out if you do
00197         std::cout << "Sum of b_{every 3 items} = " << global_sum << " (should be zero for compatibility)\n";
00198         EXCEPTION("Linear system does not satisfy compatibility constraint!");
00199         #undef COVERAGE_IGNORE
00200     }
00201 #endif
00202 }
00203 
00204 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00205 AbstractExtendedBidomainSolver<ELEMENT_DIM,SPACE_DIM>::AbstractExtendedBidomainSolver(
00206         bool bathSimulation,
00207         AbstractTetrahedralMesh<ELEMENT_DIM,SPACE_DIM>* pMesh,
00208         ExtendedBidomainTissue<SPACE_DIM>* pTissue,
00209         BoundaryConditionsContainer<ELEMENT_DIM, SPACE_DIM, 3>* pBcc,
00210         unsigned numQuadPoints)
00211     : AbstractDynamicLinearPdeSolver<ELEMENT_DIM,SPACE_DIM,3>(pMesh),
00212               mBathSimulation(bathSimulation),
00213               mpExtendedBidomainTissue(pTissue),
00214               mpBoundaryConditions(pBcc),
00215               mNumQuadPoints(numQuadPoints)
00216 {
00217     assert(pTissue != NULL);
00218     assert(pBcc != NULL);
00219 
00220     mNullSpaceCreated = false;
00221 
00222     // important!
00223     this->mMatrixIsConstant = true;
00224 
00225     mRowForAverageOfPhiZeroed = INT_MAX; //this->mpLinearSystem->GetSize() - 1;
00226     mpConfig = HeartConfig::Instance();
00227 
00228     mpExtendedBidomainAssembler = NULL; // can't initialise until know what dt is
00229 }
00230 
00231 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00232 AbstractExtendedBidomainSolver<ELEMENT_DIM,SPACE_DIM>::~AbstractExtendedBidomainSolver()
00233 {
00234 }
00235 
00236 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00237 void AbstractExtendedBidomainSolver<ELEMENT_DIM,SPACE_DIM>::SetFixedExtracellularPotentialNodes(
00238             std::vector<unsigned> fixedExtracellularPotentialNodes)
00239 {
00240     for (unsigned i=0; i<fixedExtracellularPotentialNodes.size(); i++)
00241     {
00242         if (fixedExtracellularPotentialNodes[i] >= this->mpMesh->GetNumNodes() )
00243         {
00244             EXCEPTION("Fixed node number must be less than total number nodes");
00245         }
00246     }
00247 
00248     mFixedExtracellularPotentialNodes = fixedExtracellularPotentialNodes;
00249 
00250     // We will need to recalculate this when HasDirichletBoundaryConditions() is called.
00251     this->mpBoundaryConditions->ResetDirichletCommunication();
00252 
00253     for (unsigned i=0; i<mFixedExtracellularPotentialNodes.size(); i++)
00254     {
00255         if (this->mpMesh->GetDistributedVectorFactory()->IsGlobalIndexLocal(mFixedExtracellularPotentialNodes[i]))
00256         {
00257             ConstBoundaryCondition<SPACE_DIM>* p_boundary_condition = new ConstBoundaryCondition<SPACE_DIM>(0.0);
00258 
00259             //Throws if node is not owned locally
00260             Node<SPACE_DIM>* p_node = this->mpMesh->GetNode(mFixedExtracellularPotentialNodes[i]);
00261 
00262             //the "false" passed in tells not to check that it is a boundary node (since we might have grounded electrodes within the tissue)
00263             GetBoundaryConditions()->AddDirichletBoundaryCondition(p_node, p_boundary_condition, 2, false);
00264         }
00265     }
00266 }
00267 
00268 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00269 void AbstractExtendedBidomainSolver<ELEMENT_DIM,SPACE_DIM>::SetRowForAverageOfPhiZeroed(unsigned row)
00270 {
00271     // Row should be every 3 entries, starting from zero...
00272     if ( ((row-2)%3 != 0) && row!=INT_MAX)
00273     {
00274         EXCEPTION("Row for applying the constraint 'Average of phi_e = zero' should be every 3 rows");
00275     }
00276 
00277     mRowForAverageOfPhiZeroed = row;
00278 }
00279 
00281 // Explicit instantiation
00283 
00284 template class AbstractExtendedBidomainSolver<1,1>;
00285 template class AbstractExtendedBidomainSolver<2,2>;
00286 template class AbstractExtendedBidomainSolver<3,3>;