BidomainProblem.cpp

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