BidomainProblem.cpp

00001 /*
00002 
00003 Copyright (C) University of Oxford, 2005-2010
00004 
00005 University of Oxford means the Chancellor, Masters and Scholars of the
00006 University of Oxford, having an administrative office at Wellington
00007 Square, Oxford OX1 2JD, UK.
00008 
00009 This file is part of Chaste.
00010 
00011 Chaste is free software: you can redistribute it and/or modify it
00012 under the terms of the GNU Lesser General Public License as published
00013 by the Free Software Foundation, either version 2.1 of the License, or
00014 (at your option) any later version.
00015 
00016 Chaste is distributed in the hope that it will be useful, but WITHOUT
00017 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00018 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
00019 License for more details. The offer of Chaste under the terms of the
00020 License is subject to the License being interpreted in accordance with
00021 English Law and subject to any action against the University of Oxford
00022 being under the jurisdiction of the English Courts.
00023 
00024 You should have received a copy of the GNU Lesser General Public License
00025 along with Chaste. If not, see <http://www.gnu.org/licenses/>.
00026 
00027 */
00028 
00029 
00030 #include "BidomainProblem.hpp"
00031 #include "BidomainDg0Assembler.hpp"
00032 #include "BidomainMatrixBasedAssembler.hpp"
00033 #include "BidomainWithBathAssembler.hpp"
00034 #include "BidomainWithBathMatrixBasedAssembler.hpp"
00035 #include "HeartConfig.hpp"
00036 #include "Exception.hpp"
00037 #include "DistributedVector.hpp"
00038 #include "ReplicatableVector.hpp"
00039 
00040 template <unsigned DIM>
00041 void BidomainProblem<DIM>::AnalyseMeshForBath()
00042 {
00043     // Annotate bath notes with correct region code
00044     if (mHasBath)
00045     {
00046         // Initialize all nodes to be bath nodes
00047         for (typename AbstractTetrahedralMesh<DIM,DIM>::NodeIterator iter=this->mpMesh->GetNodeIteratorBegin();
00048              iter != this->mpMesh->GetNodeIteratorEnd();
00049             ++iter)
00050         {
00051             (*iter).SetRegion(HeartRegionCode::BATH);
00052         }
00053 
00054         bool any_bath_element_found = false;
00055 
00056         // Set nodes that are part of a heart element to be heart nodes
00057         //for (unsigned i=0; i<this->mpMesh->GetNumElements(); i++)
00058         for (typename AbstractTetrahedralMesh<DIM,DIM>::ElementIterator it = this->mpMesh->GetElementIteratorBegin();
00059              it != this->mpMesh->GetElementIteratorEnd();
00060              ++it)
00061         {
00062             Element<DIM, DIM>& r_element = *it;
00063 
00064             if (r_element.GetRegion() == HeartRegionCode::TISSUE)
00065             {
00066                 for (unsigned j=0; j<r_element.GetNumNodes(); j++)
00067                 {
00068                     r_element.GetNode(j)->SetRegion(HeartRegionCode::TISSUE);
00069                 }
00070             }
00071             else
00072             {
00073                 assert(r_element.GetRegion() == HeartRegionCode::BATH);
00074                 any_bath_element_found = true;
00075             }
00076         }
00077 
00078         if (!any_bath_element_found)
00079         {
00080             EXCEPTION("No bath element found");
00081         }
00082     }
00083 }
00084 
00085 
00086 template<unsigned DIM>
00087 Vec BidomainProblem<DIM>::CreateInitialCondition()
00088 {
00089     Vec init_cond = AbstractCardiacProblem<DIM,DIM,2>::CreateInitialCondition();
00090     if (mHasBath)
00091     {
00092         // get the voltage stripe
00093         DistributedVector ic = this->mpMesh->GetDistributedVectorFactory()->CreateDistributedVector(init_cond);
00094         DistributedVector::Stripe voltage_stripe = DistributedVector::Stripe(ic,0);
00095 
00096         for (DistributedVector::Iterator index = ic.Begin();
00097              index!= ic.End();
00098              ++index)
00099         {
00100             if (this->mpMesh->GetNode( index.Global )->GetRegion() == HeartRegionCode::BATH)
00101             {
00102                 voltage_stripe[index] = 0.0;
00103             }
00104         }
00105         ic.Restore();
00106     }
00107 
00108     return init_cond;
00109 }
00110 
00111 template<unsigned DIM>
00112 AbstractCardiacPde<DIM> * BidomainProblem<DIM>::CreateCardiacPde()
00113 {
00114     AnalyseMeshForBath();
00115     mpBidomainPde = new BidomainPde<DIM>(this->mpCellFactory);
00116     return mpBidomainPde;
00117 }
00118 
00119 template<unsigned DIM>
00120 AbstractDynamicAssemblerMixin<DIM, DIM, 2>* BidomainProblem<DIM>::CreateAssembler()
00121 {
00122     /*
00123      * NOTE: The this->mpBoundaryConditionsContainer.get() lines below convert a
00124      * boost::shared_ptr to a normal pointer, as this is what the assemblers are
00125      * expecting. We have to be a bit careful though as boost could decide to delete
00126      * them whenever it feels like as it won't count the assembers as using them.
00127      *
00128      * As long as they are kept as member variables here for as long as they are
00129      * required in the assemblers it should all work OK.
00130      */
00131     if (mHasBath)
00132     {
00133         if (!this->mUseMatrixBasedRhsAssembly)
00134         {
00135             mpAssembler
00136                 = new BidomainWithBathAssembler<DIM,DIM>(this->mpMesh,
00137                                                          mpBidomainPde,
00138                                                          this->mpBoundaryConditionsContainer.get(),
00139                                                          2);
00140         }
00141         else
00142         {
00143             mpAssembler
00144                 = new BidomainWithBathMatrixBasedAssembler<DIM,DIM>(this->mpMesh,
00145                                                             mpBidomainPde,
00146                                                             this->mpBoundaryConditionsContainer.get(),
00147                                                             2);
00148         }
00149 
00150     }
00151     else
00152     {
00153         if (!this->mUseMatrixBasedRhsAssembly)
00154         {
00155             mpAssembler
00156                 = new BidomainDg0Assembler<DIM,DIM>(this->mpMesh,
00157                                                     mpBidomainPde,
00158                                                     this->mpBoundaryConditionsContainer.get(),
00159                                                     2);
00160         }
00161         else
00162         {
00163             mpAssembler
00164                 = new BidomainMatrixBasedAssembler<DIM,DIM>(this->mpMesh,
00165                                                             mpBidomainPde,
00166                                                             this->mpBoundaryConditionsContainer.get(),
00167                                                             2);
00168         }
00169     }
00170 
00171     try
00172     {
00173         mpAssembler->SetFixedExtracellularPotentialNodes(mFixedExtracellularPotentialNodes);
00174         mpAssembler->SetRowForAverageOfPhiZeroed(mRowForAverageOfPhiZeroed);
00175     }
00176     catch (const Exception& e)
00177     {
00178         delete mpAssembler;
00179         throw e;
00180     }
00181 
00182     return mpAssembler;
00183 }
00184 
00185 template<unsigned DIM>
00186 BidomainProblem<DIM>::BidomainProblem(
00187             AbstractCardiacCellFactory<DIM>* pCellFactory, bool hasBath)
00188     : AbstractCardiacProblem<DIM,DIM, 2>(pCellFactory),
00189       mpBidomainPde(NULL),
00190       mRowForAverageOfPhiZeroed(INT_MAX),
00191       mHasBath(hasBath)
00192 {
00193     mFixedExtracellularPotentialNodes.resize(0);
00194 }
00195 
00196 template<unsigned DIM>
00197 BidomainProblem<DIM>::BidomainProblem()
00198     : AbstractCardiacProblem<DIM, DIM, 2>(),
00199       mpBidomainPde(NULL),
00200       mRowForAverageOfPhiZeroed(INT_MAX)
00201 {
00202     mFixedExtracellularPotentialNodes.resize(0);
00203 }
00204 
00205 
00206 
00207 template<unsigned DIM>
00208 void BidomainProblem<DIM>::SetFixedExtracellularPotentialNodes(std::vector<unsigned> nodes)
00209 {
00210     mFixedExtracellularPotentialNodes.resize(nodes.size());
00211     for (unsigned i=0; i<nodes.size(); i++)
00212     {
00213         // the assembler checks that the nodes[i] is less than
00214         // the number of nodes in the mesh so this is not done here
00215         mFixedExtracellularPotentialNodes[i] = nodes[i];
00216     }
00217 }
00218 
00219 template<unsigned DIM>
00220 void BidomainProblem<DIM>::SetNodeForAverageOfPhiZeroed(unsigned node)
00221 {
00222     mRowForAverageOfPhiZeroed = 2*node+1;
00223 }
00224 
00225 template<unsigned DIM>
00226 BidomainPde<DIM>* BidomainProblem<DIM>::GetBidomainPde()
00227 {
00228     assert(mpBidomainPde!=NULL);
00229     return mpBidomainPde;
00230 }
00231 
00232 template<unsigned DIM>
00233 void BidomainProblem<DIM>::WriteInfo(double time)
00234 {
00235     if (PetscTools::AmMaster())
00236     {
00237         std::cout << "Solved to time " << time << "\n" << std::flush;
00238     }
00239 
00240     double v_max, v_min, phi_max, phi_min;
00241 
00242     VecSetBlockSize( this->mSolution, 2 );
00243 
00244     VecStrideMax( this->mSolution, 0, PETSC_NULL, &v_max );
00245     VecStrideMin( this->mSolution, 0, PETSC_NULL, &v_min );
00246 
00247     VecStrideMax( this->mSolution, 1, PETSC_NULL, &phi_max );
00248     VecStrideMin( this->mSolution, 1, PETSC_NULL, &phi_min );
00249 
00250     if (PetscTools::AmMaster())
00251     {
00252         std::cout << " V; phi_e = " << "[" <<v_min << ", " << v_max << "]" << ";\t"
00253                   << "[" <<phi_min << ", " << phi_max << "]" << "\n"
00254                   << std::flush;
00255     }
00256 }
00257 
00258 template<unsigned DIM>
00259 void BidomainProblem<DIM>::DefineWriterColumns(bool extending)
00260 {
00261     AbstractCardiacProblem<DIM,DIM,2>::DefineWriterColumns(extending);
00262     if (extending)
00263     {
00264         mExtracelluarColumnId = this->mpWriter->GetVariableByName("Phi_e");
00265     }
00266     else
00267     {
00268         mExtracelluarColumnId = this->mpWriter->DefineVariable("Phi_e","mV");
00269     }
00270     AbstractCardiacProblem<DIM,DIM,2>::DefineExtraVariablesWriterColumns(extending);
00271 }
00272 
00273 template<unsigned DIM>
00274 void BidomainProblem<DIM>::WriteOneStep(double time, Vec voltageVec)
00275 {
00276     this->mpWriter->PutUnlimitedVariable(time);
00277     this->mpWriter->PutStripedVector(this->mVoltageColumnId, mExtracelluarColumnId, voltageVec);
00278     AbstractCardiacProblem<DIM,DIM,2>::WriteExtraVariablesOneStep();
00279 }
00280 
00281 template<unsigned DIM>
00282 void BidomainProblem<DIM>::PreSolveChecks()
00283 {
00284     AbstractCardiacProblem<DIM,DIM, 2>::PreSolveChecks();
00285     if (mFixedExtracellularPotentialNodes.empty())
00286     {
00287         // We're not pinning any nodes.
00288         if (mRowForAverageOfPhiZeroed==INT_MAX)
00289         {
00290             // We're not using the constrain Average phi_e to 0 method, hence use a null space
00291             // Check that the KSP solver isn't going to do anything stupid:
00292             // phi_e is not bounded, so it'd be wrong to use a relative tolerance
00293             if (HeartConfig::Instance()->GetUseRelativeTolerance())
00294             {
00295                 EXCEPTION("Bidomain external voltage is not bounded in this simulation - use KSP *absolute* tolerance");
00296             }
00297         }
00298     }
00299 }
00300 
00301 template<unsigned DIM>
00302 void BidomainProblem<DIM>::SetElectrodes(boost::shared_ptr<Electrodes<DIM> > pElectrodes)
00303 {
00304     if (!mHasBath)
00305     {
00306         EXCEPTION("Cannot set electrodes when problem has been defined to not have a bath");
00307     }
00308 
00309     mpElectrodes = pElectrodes;
00310 }
00311 
00312 template<unsigned DIM>
00313 void BidomainProblem<DIM>::AtBeginningOfTimestep(double time)
00314 {
00315     if ( mpElectrodes && mpElectrodes->SwitchOn(time) )
00316     {
00317         // At the moment mpBcc and mpDefaultBcc point to a set default BC
00318         assert(this->mpBoundaryConditionsContainer);
00319         //assert(this->mpDefaultBoundaryConditionsContainer);
00320 
00321         // Note, no point calling this->SetBoundaryConditionsContainer() as the
00322         // assembler has already been created..
00323         mpAssembler->SetBoundaryConditionsContainer(mpElectrodes->GetBoundaryConditionsContainer().get());
00324 
00325         // ..but we set mpBcc anyway, so the local mpBcc is
00326         // the same as the one being used in the assembler...
00327         this->mpBoundaryConditionsContainer = mpElectrodes->GetBoundaryConditionsContainer();
00328 
00330         this->mpDefaultBoundaryConditionsContainer = this->mpBoundaryConditionsContainer;
00331 
00332         // At t==0 or after checkpointing we won't have a system assembled at this stage: BCs will be applied once the matrix
00333         // is assembled. Dirichlet BCs will be present at the time of assembly and no null space will be created either.
00334         if ( *mpAssembler->GetLinearSystem() != NULL )
00335         {
00336             // System matrix is assembled once at the beginning of the simulation. After that, nobody will take care
00337             // of applying new BC to the system matrix. Must be triggered explicitly.
00338             if (mpElectrodes->HasGroundedElectrode())
00339             {
00340                 this->mpBoundaryConditionsContainer->ApplyDirichletToLinearProblem( ** mpAssembler->GetLinearSystem(),
00341                                                                                    true, false);
00342             }
00343 
00344             // If a grounded electrode is switched on, the linear system is not singular anymore. Remove the null space.
00345             if (mpElectrodes->HasGroundedElectrode())
00346             {
00347                 (*(mpAssembler->GetLinearSystem()))->RemoveNullSpace();
00348             }
00349         }
00350     }
00351 }
00352 
00353 template<unsigned DIM>
00354 void BidomainProblem<DIM>::OnEndOfTimestep(double time)
00355 {
00356     if ( mpElectrodes && mpElectrodes->SwitchOff(time) )
00357     {
00358         // At the moment mpBcc should exist and therefore
00359         // mpDefaultBcc should be empty (not if electrodes switched on after 0ms)
00360         assert(this->mpBoundaryConditionsContainer);
00361         //assert(! this->mpDefaultBoundaryConditionsContainer);
00362 
00363         // Set up default boundary conditions container - no Neumann fluxes
00364         // or Dirichlet fixed nodes
00365         this->mpDefaultBoundaryConditionsContainer.reset(new BoundaryConditionsContainer<DIM,DIM,2>);
00366         for (unsigned problem_index=0; problem_index<2; problem_index++)
00367         {
00368             this->mpDefaultBoundaryConditionsContainer->DefineZeroNeumannOnMeshBoundary(this->mpMesh, problem_index);
00369         }
00370 
00371         // If there's a grounded electrode, we must remove BC from linear system. At the moment, we don't
00372         // have a sensible way of doing this, therefore we reassemble the system.
00373         if (mpElectrodes->HasGroundedElectrode())
00374         {
00375             delete mpAssembler;
00376             AbstractCardiacProblem<DIM,DIM,2>::mpAssembler = CreateAssembler();
00377         }
00378 
00379         // Note, no point calling this->SetBoundaryConditionsContainer() as the
00380         // assembler has already been created..
00381         mpAssembler->SetBoundaryConditionsContainer(this->mpDefaultBoundaryConditionsContainer.get());
00382         // ..but we set mpBcc to be mpDefaultBcc anyway, so the local mpBcc is
00383         // the same as the one being used in the assembler...
00384         this->mpBoundaryConditionsContainer = this->mpDefaultBoundaryConditionsContainer;
00385     }
00386 }
00387 
00388 
00389 
00390 template<unsigned DIM>
00391 void BidomainProblem<DIM>::SetUpAdditionalStoppingTimes(std::vector<double>& rAdditionalStoppingTimes)
00392 {
00393     if ( mpElectrodes )
00394     {
00395         rAdditionalStoppingTimes.push_back( mpElectrodes->GetSwitchOnTime() );
00396         rAdditionalStoppingTimes.push_back( mpElectrodes->GetSwitchOffTime() );
00397     }
00398 }
00399 
00401 // Explicit instantiation
00403 
00404 template class BidomainProblem<1>;
00405 template class BidomainProblem<2>;
00406 template class BidomainProblem<3>;
00407 
00408 
00409 // Serialization for Boost >= 1.36
00410 #include "SerializationExportWrapperForCpp.hpp"
00411 EXPORT_TEMPLATE_CLASS_SAME_DIMS(BidomainProblem);

Generated by  doxygen 1.6.2