BidomainProblem.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 "BidomainProblem.hpp"
00038 #include "BidomainSolver.hpp"
00039 #include "HeartConfig.hpp"
00040 #include "Exception.hpp"
00041 #include "DistributedVector.hpp"
00042 #include "ReplicatableVector.hpp"
00043 
00044 template <unsigned DIM>
00045 void BidomainProblem<DIM>::AnalyseMeshForBath()
00046 {
00047     // Annotate bath notes with correct region code
00048     if (mHasBath)
00049     {
00050         // Initialize all nodes to be bath nodes
00051         for (typename AbstractTetrahedralMesh<DIM,DIM>::NodeIterator iter=this->mpMesh->GetNodeIteratorBegin();
00052              iter != this->mpMesh->GetNodeIteratorEnd();
00053             ++iter)
00054         {
00055             (*iter).SetRegion(HeartRegionCode::GetValidBathId());
00056         }
00057 
00058         bool any_bath_element_found = false;
00059 
00060         // Set nodes that are part of a heart element to be heart nodes
00061         //for (unsigned i=0; i<this->mpMesh->GetNumElements(); i++)
00062         for (typename AbstractTetrahedralMesh<DIM,DIM>::ElementIterator it = this->mpMesh->GetElementIteratorBegin();
00063              it != this->mpMesh->GetElementIteratorEnd();
00064              ++it)
00065         {
00066             Element<DIM, DIM>& r_element = *it;
00067 
00068             if (HeartRegionCode::IsRegionTissue( r_element.GetUnsignedAttribute() ))
00069             {
00070                 for (unsigned j=0; j<r_element.GetNumNodes(); j++)
00071                 {
00072                     r_element.GetNode(j)->SetRegion(HeartRegionCode::GetValidTissueId());
00073                 }
00074             }
00075             else
00076             {
00077                 assert(HeartRegionCode::IsRegionBath( r_element.GetUnsignedAttribute() ));
00078                 any_bath_element_found = true;
00079             }
00080         }
00081 
00082         if (!PetscTools::ReplicateBool(any_bath_element_found))
00083         {
00084            EXCEPTION("No bath element found");
00085         }
00086     }
00087     else
00088     {
00089         // The problem hasn't been set up with a bath, so check that the user hasn't set any options
00090         // which would suggest they're expecting there to be a bath!
00091         std::set<unsigned> bath_identifiers = HeartConfig::Instance()->rGetBathIdentifiers();
00092         if ( !(bath_identifiers.size()==1 && bath_identifiers.find(1)==bath_identifiers.begin()) ) // IF NOT only 1 in the bath identifiers set
00093         {
00094             EXCEPTION("User has set bath identifiers, but the BidomainProblem isn't expecting a bath. Did you mean to use BidomainProblem(..., true)? Or alternatively, BidomainWithBathProblem(...)?");
00095         }
00096     }
00097 }
00098 
00099 
00100 template<unsigned DIM>
00101 Vec BidomainProblem<DIM>::CreateInitialCondition()
00102 {
00103     Vec init_cond = AbstractCardiacProblem<DIM,DIM,2>::CreateInitialCondition();
00104     if (mHasBath)
00105     {
00106         // get the voltage stripe
00107         DistributedVector ic = this->mpMesh->GetDistributedVectorFactory()->CreateDistributedVector(init_cond);
00108         DistributedVector::Stripe voltage_stripe = DistributedVector::Stripe(ic,0);
00109 
00110         for (DistributedVector::Iterator index = ic.Begin();
00111              index!= ic.End();
00112              ++index)
00113         {
00114             if (HeartRegionCode::IsRegionBath( this->mpMesh->GetNode( index.Global )->GetRegion() ))
00115             {
00116                 voltage_stripe[index] = 0.0;
00117             }
00118         }
00119         ic.Restore();
00120     }
00121 
00122     return init_cond;
00123 }
00124 
00125 template<unsigned DIM>
00126 AbstractCardiacTissue<DIM> * BidomainProblem<DIM>::CreateCardiacTissue()
00127 {
00128     AnalyseMeshForBath();
00129     mpBidomainTissue = new BidomainTissue<DIM>(this->mpCellFactory, HeartConfig::Instance()->GetUseStateVariableInterpolation());
00130     return mpBidomainTissue;
00131 }
00132 
00133 template<unsigned DIM>
00134 AbstractDynamicLinearPdeSolver<DIM, DIM, 2>* BidomainProblem<DIM>::CreateSolver()
00135 {
00136     /*
00137      * NOTE: The this->mpBoundaryConditionsContainer.get() lines below convert a
00138      * boost::shared_ptr to a normal pointer, as this is what the solvers are
00139      * expecting. We have to be a bit careful though as boost could decide to delete
00140      * them whenever it feels like as it won't count the assembers as using them.
00141      *
00142      * As long as they are kept as member variables here for as long as they are
00143      * required in the solvers it should all work OK.
00144      */
00145     mpSolver = new BidomainSolver<DIM,DIM>(mHasBath,
00146                                            this->mpMesh,
00147                                            mpBidomainTissue,
00148                                            this->mpBoundaryConditionsContainer.get());
00149 
00150     try
00151     {
00152         mpSolver->SetFixedExtracellularPotentialNodes(mFixedExtracellularPotentialNodes);
00153         mpSolver->SetRowForAverageOfPhiZeroed(mRowForAverageOfPhiZeroed);
00154     }
00155     catch (const Exception& e)
00156     {
00157         delete mpSolver;
00158         throw e;
00159     }
00160 
00161     return mpSolver;
00162 }
00163 
00164 template<unsigned DIM>
00165 BidomainProblem<DIM>::BidomainProblem(
00166             AbstractCardiacCellFactory<DIM>* pCellFactory, bool hasBath)
00167     : AbstractCardiacProblem<DIM,DIM, 2>(pCellFactory),
00168       mpBidomainTissue(NULL),
00169       mRowForAverageOfPhiZeroed(INT_MAX),
00170       mHasBath(hasBath)
00171 {
00172     mFixedExtracellularPotentialNodes.resize(0);
00173 }
00174 
00175 template<unsigned DIM>
00176 BidomainProblem<DIM>::BidomainProblem()
00177     : AbstractCardiacProblem<DIM, DIM, 2>(),
00178       mpBidomainTissue(NULL),
00179       mRowForAverageOfPhiZeroed(INT_MAX)
00180 {
00181     mFixedExtracellularPotentialNodes.resize(0);
00182 }
00183 
00184 
00185 
00186 template<unsigned DIM>
00187 void BidomainProblem<DIM>::SetFixedExtracellularPotentialNodes(std::vector<unsigned> nodes)
00188 {
00189     mFixedExtracellularPotentialNodes.resize(nodes.size());
00190     for (unsigned i=0; i<nodes.size(); i++)
00191     {
00192         // the solver checks that the nodes[i] is less than
00193         // the number of nodes in the mesh so this is not done here
00194         mFixedExtracellularPotentialNodes[i] = nodes[i];
00195     }
00196 }
00197 
00198 template<unsigned DIM>
00199 void BidomainProblem<DIM>::SetNodeForAverageOfPhiZeroed(unsigned node)
00200 {
00201     mRowForAverageOfPhiZeroed = 2*node+1;
00202 }
00203 
00204 template<unsigned DIM>
00205 BidomainTissue<DIM>* BidomainProblem<DIM>::GetBidomainTissue()
00206 {
00207     assert(mpBidomainTissue!=NULL);
00208     return mpBidomainTissue;
00209 }
00210 
00211 template<unsigned DIM>
00212 void BidomainProblem<DIM>::WriteInfo(double time)
00213 {
00214     if (PetscTools::AmMaster())
00215     {
00216         std::cout << "Solved to time " << time << "\n" << std::flush;
00217     }
00218 
00219     double v_max, v_min, phi_max, phi_min;
00220 
00221     VecStrideMax( this->mSolution, 0, PETSC_NULL, &v_max );
00222     VecStrideMin( this->mSolution, 0, PETSC_NULL, &v_min );
00223 
00224     VecStrideMax( this->mSolution, 1, PETSC_NULL, &phi_max );
00225     VecStrideMin( this->mSolution, 1, PETSC_NULL, &phi_min );
00226 
00227     if (PetscTools::AmMaster())
00228     {
00229         std::cout << " V; phi_e = " << "[" <<v_min << ", " << v_max << "]" << ";\t"
00230                   << "[" <<phi_min << ", " << phi_max << "]" << "\n"
00231                   << std::flush;
00232     }
00233 }
00234 
00235 template<unsigned DIM>
00236 void BidomainProblem<DIM>::DefineWriterColumns(bool extending)
00237 {
00238     AbstractCardiacProblem<DIM,DIM,2>::DefineWriterColumns(extending);
00239     if (extending)
00240     {
00241         mExtracelluarColumnId = this->mpWriter->GetVariableByName("Phi_e");
00242     }
00243     else
00244     {
00245         mExtracelluarColumnId = this->mpWriter->DefineVariable("Phi_e","mV");
00246     }
00247     AbstractCardiacProblem<DIM,DIM,2>::DefineExtraVariablesWriterColumns(extending);
00248 }
00249 
00250 template<unsigned DIM>
00251 void BidomainProblem<DIM>::WriteOneStep(double time, Vec voltageVec)
00252 {
00253     this->mpWriter->PutUnlimitedVariable(time);
00254     std::vector<int> variable_ids;
00255     variable_ids.push_back(this->mVoltageColumnId);
00256     variable_ids.push_back(mExtracelluarColumnId);
00257     this->mpWriter->PutStripedVector(variable_ids, voltageVec);
00258     AbstractCardiacProblem<DIM,DIM,2>::WriteExtraVariablesOneStep();
00259 }
00260 
00261 template<unsigned DIM>
00262 void BidomainProblem<DIM>::PreSolveChecks()
00263 {
00264     AbstractCardiacProblem<DIM,DIM, 2>::PreSolveChecks();
00265     if (mFixedExtracellularPotentialNodes.empty())
00266     {
00267         // We're not pinning any nodes.
00268         if (mRowForAverageOfPhiZeroed==INT_MAX)
00269         {
00270             // We're not using the constrain Average phi_e to 0 method, hence use a null space
00271             // Check that the KSP solver isn't going to do anything stupid:
00272             // phi_e is not bounded, so it'd be wrong to use a relative tolerance
00273             if (HeartConfig::Instance()->GetUseRelativeTolerance())
00274             {
00275                 EXCEPTION("Bidomain external voltage is not bounded in this simulation - use KSP *absolute* tolerance");
00276             }
00277         }
00278     }
00279 }
00280 
00281 template<unsigned DIM>
00282 void BidomainProblem<DIM>::SetElectrodes()
00283 {
00284     if (!mHasBath)
00285     {
00286         //Cannot set electrodes when problem has been defined to not have a bath
00287         return;
00288     }
00289 
00290     assert(this->mpMesh!=NULL);
00291 
00292     if (HeartConfig::Instance()->IsElectrodesPresent())
00293     {
00294         mpElectrodes = (boost::shared_ptr<Electrodes<DIM> >) new Electrodes<DIM>(*(this->mpMesh));
00295     }
00296 }
00297 
00298 template<unsigned DIM>
00299 void BidomainProblem<DIM>::AtBeginningOfTimestep(double time)
00300 {
00301     if ( mpElectrodes && mpElectrodes->SwitchOn(time) )
00302     {
00303         // At the moment mpBcc and mpDefaultBcc point to a set default BC
00304         assert(this->mpBoundaryConditionsContainer);
00305         //assert(this->mpDefaultBoundaryConditionsContainer);
00306 
00307         // Note, no point calling this->SetBoundaryConditionsContainer() as the
00308         // solver has already been created..
00309         mpSolver->ResetBoundaryConditionsContainer(mpElectrodes->GetBoundaryConditionsContainer().get());
00310 
00311         // ..but we set mpBcc anyway, so the local mpBcc is
00312         // the same as the one being used in the solver...
00313         this->mpBoundaryConditionsContainer = mpElectrodes->GetBoundaryConditionsContainer();
00314 
00316         this->mpDefaultBoundaryConditionsContainer = this->mpBoundaryConditionsContainer;
00317 
00318         // At t==0 or after checkpointing we won't have a system assembled at this stage: BCs will be applied once the matrix
00319         // is assembled. Dirichlet BCs will be present at the time of assembly and no null space will be created either.
00320         if ( mpSolver->GetLinearSystem() != NULL )
00321         {
00322             // System matrix is assembled once at the beginning of the simulation. After that, nobody will take care
00323             // of applying new BC to the system matrix. Must be triggered explicitly.
00324             if (mpElectrodes->HasGroundedElectrode())
00325             {
00326                 this->mpBoundaryConditionsContainer->ApplyDirichletToLinearProblem( *(mpSolver->GetLinearSystem()),
00327                                                                                    true, false);
00328             }
00329 
00330             // If a grounded electrode is switched on, the linear system is not singular anymore. Remove the null space.
00331             if (mpElectrodes->HasGroundedElectrode())
00332             {
00333                 mpSolver->GetLinearSystem()->RemoveNullSpace();
00334             }
00335         }
00336     }
00337 }
00338 
00339 template<unsigned DIM>
00340 void BidomainProblem<DIM>::OnEndOfTimestep(double time)
00341 {
00342     if ( mpElectrodes && mpElectrodes->SwitchOff(time) )
00343     {
00344         // At the moment mpBcc should exist and therefore
00345         // mpDefaultBcc should be empty (not if electrodes switched on after 0ms)
00346         assert(this->mpBoundaryConditionsContainer);
00347         //assert(! this->mpDefaultBoundaryConditionsContainer);
00348 
00349         // Set up default boundary conditions container - no Neumann fluxes
00350         // or Dirichlet fixed nodes
00351         this->mpDefaultBoundaryConditionsContainer.reset(new BoundaryConditionsContainer<DIM,DIM,2>);
00352         for (unsigned problem_index=0; problem_index<2; problem_index++)
00353         {
00354             this->mpDefaultBoundaryConditionsContainer->DefineZeroNeumannOnMeshBoundary(this->mpMesh, problem_index);
00355         }
00356 
00357         // If there's a grounded electrode, we must remove BC from linear system. At the moment, we don't
00358         // have a sensible way of doing this, therefore we reassemble the system.
00359         if (mpElectrodes->HasGroundedElectrode())
00360         {
00361             delete mpSolver;
00362             AbstractCardiacProblem<DIM,DIM,2>::mpSolver = CreateSolver();
00363             mpSolver->SetTimeStep(HeartConfig::Instance()->GetPdeTimeStep());
00364         }
00365 
00366         // Note, no point calling this->SetBoundaryConditionsContainer() as the
00367         // solver has already been created..
00368         mpSolver->ResetBoundaryConditionsContainer(this->mpDefaultBoundaryConditionsContainer.get());
00369         // ..but we set mpBcc to be mpDefaultBcc anyway, so the local mpBcc is
00370         // the same as the one being used in the solver...
00371         this->mpBoundaryConditionsContainer = this->mpDefaultBoundaryConditionsContainer;
00372     }
00373 }
00374 
00375 
00376 
00377 template<unsigned DIM>
00378 void BidomainProblem<DIM>::SetUpAdditionalStoppingTimes(std::vector<double>& rAdditionalStoppingTimes)
00379 {
00380     if ( mpElectrodes )
00381     {
00382         rAdditionalStoppingTimes.push_back( mpElectrodes->GetSwitchOnTime() );
00383         rAdditionalStoppingTimes.push_back( mpElectrodes->GetSwitchOffTime() );
00384     }
00385 }
00386 
00387 template<unsigned DIM>
00388 bool BidomainProblem<DIM>::GetHasBath()
00389 {
00390     return mHasBath;
00391 }
00392 
00394 // Explicit instantiation
00396 
00397 template class BidomainProblem<1>;
00398 template class BidomainProblem<2>;
00399 template class BidomainProblem<3>;
00400 
00401 
00402 // Serialization for Boost >= 1.36
00403 #include "SerializationExportWrapperForCpp.hpp"
00404 EXPORT_TEMPLATE_CLASS_SAME_DIMS(BidomainProblem)

Generated by  doxygen 1.6.2