Chaste  Release::2017.1
BidomainProblem.cpp
1 /*
2 
3 Copyright (c) 2005-2017, University of Oxford.
4 All rights reserved.
5 
6 University of Oxford means the Chancellor, Masters and Scholars of the
7 University of Oxford, having an administrative office at Wellington
8 Square, Oxford OX1 2JD, UK.
9 
10 This file is part of Chaste.
11 
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions are met:
14  * Redistributions of source code must retain the above copyright notice,
15  this list of conditions and the following disclaimer.
16  * Redistributions in binary form must reproduce the above copyright notice,
17  this list of conditions and the following disclaimer in the documentation
18  and/or other materials provided with the distribution.
19  * Neither the name of the University of Oxford nor the names of its
20  contributors may be used to endorse or promote products derived from this
21  software without specific prior written permission.
22 
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 
34 */
35 
36 
37 #include "BidomainProblem.hpp"
38 #include "BidomainSolver.hpp"
39 #include "HeartConfig.hpp"
40 #include "Exception.hpp"
41 #include "DistributedVector.hpp"
42 #include "ReplicatableVector.hpp"
43 
44 template <unsigned DIM>
46 {
47  // Annotate bath notes with correct region code
48  if (mHasBath)
49  {
50  // Initialize all nodes to be bath nodes
52  iter != this->mpMesh->GetNodeIteratorEnd();
53  ++iter)
54  {
55  (*iter).SetRegion(HeartRegionCode::GetValidBathId());
56  }
57 
58  bool any_bath_element_found = false;
59 
60  // Set nodes that are part of a heart element to be heart nodes
61  //for (unsigned i=0; i<this->mpMesh->GetNumElements(); i++)
63  it != this->mpMesh->GetElementIteratorEnd();
64  ++it)
65  {
66  Element<DIM, DIM>& r_element = *it;
67 
69  {
70  for (unsigned j=0; j<r_element.GetNumNodes(); j++)
71  {
72  r_element.GetNode(j)->SetRegion(HeartRegionCode::GetValidTissueId());
73  }
74  }
75  else
76  {
78  any_bath_element_found = true;
79  }
80  }
81 
82  if (!PetscTools::ReplicateBool(any_bath_element_found))
83  {
84  EXCEPTION("No bath element found");
85  }
86  }
87  else
88  {
89  // The problem hasn't been set up with a bath, so check that the user hasn't set any options
90  // which would suggest they're expecting there to be a bath!
91  std::set<unsigned> bath_identifiers = HeartConfig::Instance()->rGetBathIdentifiers();
92  if (!(bath_identifiers.size()==1 && bath_identifiers.find(1)==bath_identifiers.begin())) // IF NOT only 1 in the bath identifiers set
93  {
94  EXCEPTION("User has set bath identifiers, but the BidomainProblem isn't expecting a bath. Did you mean to use BidomainProblem(..., true)? Or alternatively, BidomainWithBathProblem(...)?");
95  }
96  }
97 }
98 
99 template<unsigned DIM>
101 {
103  if (mHasBath)
104  {
105  // get the voltage stripe
106  DistributedVector ic = this->mpMesh->GetDistributedVectorFactory()->CreateDistributedVector(init_cond);
108 
109  for (DistributedVector::Iterator index = ic.Begin();
110  index!= ic.End();
111  ++index)
112  {
113  if (HeartRegionCode::IsRegionBath( this->mpMesh->GetNode( index.Global )->GetRegion() ))
114  {
115  voltage_stripe[index] = 0.0;
116  }
117  }
118  ic.Restore();
119  }
120 
121  return init_cond;
122 }
123 
124 template<unsigned DIM>
126 {
127  AnalyseMeshForBath();
128  mpBidomainTissue = new BidomainTissue<DIM>(this->mpCellFactory, HeartConfig::Instance()->GetUseStateVariableInterpolation());
129  return mpBidomainTissue;
130 }
131 
132 template<unsigned DIM>
134 {
135  /*
136  * NOTE: The this->mpBoundaryConditionsContainer.get() lines below convert a
137  * boost::shared_ptr to a normal pointer, as this is what the solvers are
138  * expecting. We have to be a bit careful though as boost could decide to delete
139  * them whenever it feels like as it won't count the assembers as using them.
140  *
141  * As long as they are kept as member variables here for as long as they are
142  * required in the solvers it should all work OK.
143  */
144  mpSolver = new BidomainSolver<DIM,DIM>(mHasBath,
145  this->mpMesh,
146  mpBidomainTissue,
147  this->mpBoundaryConditionsContainer.get());
148 
149  try
150  {
151  mpSolver->SetFixedExtracellularPotentialNodes(mFixedExtracellularPotentialNodes);
152  mpSolver->SetRowForAverageOfPhiZeroed(mRowForAverageOfPhiZeroed);
153  }
154  catch (const Exception& e)
155  {
156  delete mpSolver;
157  throw e;
158  }
159 
160  return mpSolver;
161 }
162 
163 template<unsigned DIM>
165  AbstractCardiacCellFactory<DIM>* pCellFactory, bool hasBath)
166  : AbstractCardiacProblem<DIM,DIM, 2>(pCellFactory),
167  mpBidomainTissue(NULL),
168  mRowForAverageOfPhiZeroed(INT_MAX),
169  mHasBath(hasBath)
170 {
172 }
173 
174 template<unsigned DIM>
176  : AbstractCardiacProblem<DIM, DIM, 2>(),
177  mpBidomainTissue(NULL),
179 {
181 }
182 
183 template<unsigned DIM>
185 {
186  mFixedExtracellularPotentialNodes.resize(nodes.size());
187  for (unsigned i=0; i<nodes.size(); i++)
188  {
189  // the solver checks that the nodes[i] is less than
190  // the number of nodes in the mesh so this is not done here
191  mFixedExtracellularPotentialNodes[i] = nodes[i];
192  }
193 }
194 
195 template<unsigned DIM>
197 {
198  mRowForAverageOfPhiZeroed = 2*node+1;
199 }
200 
201 template<unsigned DIM>
203 {
204  assert(mpBidomainTissue!=NULL);
205  return mpBidomainTissue;
206 }
207 
208 template<unsigned DIM>
210 {
211  if (PetscTools::AmMaster())
212  {
213  std::cout << "Solved to time " << time << "\n" << std::flush;
214  }
215 
216  double v_max, v_min, phi_max, phi_min;
217 
218  VecStrideMax( this->mSolution, 0, PETSC_NULL, &v_max );
219  VecStrideMin( this->mSolution, 0, PETSC_NULL, &v_min );
220 
221  VecStrideMax( this->mSolution, 1, PETSC_NULL, &phi_max );
222  VecStrideMin( this->mSolution, 1, PETSC_NULL, &phi_min );
223 
224  if (PetscTools::AmMaster())
225  {
226  std::cout << " V; phi_e = " << "[" <<v_min << ", " << v_max << "]" << ";\t"
227  << "[" <<phi_min << ", " << phi_max << "]" << "\n"
228  << std::flush;
229  }
230 }
231 
232 template<unsigned DIM>
234 {
236  if (extending)
237  {
239  }
240  else
241  {
242  mExtracelluarColumnId = this->mpWriter->DefineVariable("Phi_e","mV");
243  }
245 }
246 
247 template<unsigned DIM>
248 void BidomainProblem<DIM>::WriteOneStep(double time, Vec voltageVec)
249 {
250  this->mpWriter->PutUnlimitedVariable(time);
251  std::vector<int> variable_ids;
252  variable_ids.push_back(this->mVoltageColumnId);
253  variable_ids.push_back(mExtracelluarColumnId);
254  this->mpWriter->PutStripedVector(variable_ids, voltageVec);
256 }
257 
258 template<unsigned DIM>
260 {
263  {
264  // We're not pinning any nodes.
265  if (mRowForAverageOfPhiZeroed==INT_MAX)
266  {
267  // We're not using the constrain Average phi_e to 0 method, hence use a null space
268  // Check that the KSP solver isn't going to do anything stupid:
269  // phi_e is not bounded, so it'd be wrong to use a relative tolerance
271  {
272  EXCEPTION("Bidomain external voltage is not bounded in this simulation - use KSP *absolute* tolerance");
273  }
274  }
275  }
276 }
277 
278 template<unsigned DIM>
280 {
281  if (!mHasBath)
282  {
283  //Cannot set electrodes when problem has been defined to not have a bath
284  return;
285  }
286 
287  assert(this->mpMesh!=NULL);
288 
289  if (HeartConfig::Instance()->IsElectrodesPresent())
290  {
291  mpElectrodes = (boost::shared_ptr<Electrodes<DIM> >) new Electrodes<DIM>(*(this->mpMesh));
292  }
293 }
294 
295 template<unsigned DIM>
297 {
298  if (mpElectrodes && mpElectrodes->SwitchOn(time))
299  {
300  // At the moment mpBcc and mpDefaultBcc point to a set default BC
301  assert(this->mpBoundaryConditionsContainer);
302  //assert(this->mpDefaultBoundaryConditionsContainer);
303 
304  // Note, no point calling this->SetBoundaryConditionsContainer() as the
305  // solver has already been created..
306  mpSolver->ResetBoundaryConditionsContainer(mpElectrodes->GetBoundaryConditionsContainer().get());
307 
308  // ..but we set mpBcc anyway, so the local mpBcc is
309  // the same as the one being used in the solver...
310  this->mpBoundaryConditionsContainer = mpElectrodes->GetBoundaryConditionsContainer();
311 
314 
315  // At t==0 or after checkpointing we won't have a system assembled at this stage: BCs will be applied once the matrix
316  // is assembled. Dirichlet BCs will be present at the time of assembly and no null space will be created either.
317  if (mpSolver->GetLinearSystem() != NULL)
318  {
319  // System matrix is assembled once at the beginning of the simulation. After that, nobody will take care
320  // of applying new BC to the system matrix. Must be triggered explicitly.
321  if (mpElectrodes->HasGroundedElectrode())
322  {
323  this->mpBoundaryConditionsContainer->ApplyDirichletToLinearProblem( *(mpSolver->GetLinearSystem()),
324  true, false);
325  }
326 
327  // If a grounded electrode is switched on, the linear system is not singular anymore. Remove the null space.
328  if (mpElectrodes->HasGroundedElectrode())
329  {
331  }
332  }
333  }
334 }
335 
336 template<unsigned DIM>
338 {
339  if (mpElectrodes && mpElectrodes->SwitchOff(time))
340  {
341  // At the moment mpBcc should exist and therefore
342  // mpDefaultBcc should be empty (not if electrodes switched on after 0ms)
343  assert(this->mpBoundaryConditionsContainer);
344  //assert(! this->mpDefaultBoundaryConditionsContainer);
345 
346  // Set up default boundary conditions container - no Neumann fluxes
347  // or Dirichlet fixed nodes
349  for (unsigned problem_index=0; problem_index<2; problem_index++)
350  {
351  this->mpDefaultBoundaryConditionsContainer->DefineZeroNeumannOnMeshBoundary(this->mpMesh, problem_index);
352  }
353 
354  // If there's a grounded electrode, we must remove BC from linear system. At the moment, we don't
355  // have a sensible way of doing this, therefore we reassemble the system.
356  if (mpElectrodes->HasGroundedElectrode())
357  {
358  delete mpSolver;
360  mpSolver->SetTimeStep(HeartConfig::Instance()->GetPdeTimeStep());
361  }
362 
363  // Note, no point calling this->SetBoundaryConditionsContainer() as the
364  // solver has already been created..
366  // ..but we set mpBcc to be mpDefaultBcc anyway, so the local mpBcc is
367  // the same as the one being used in the solver...
369  }
370 }
371 
372 template<unsigned DIM>
373 void BidomainProblem<DIM>::SetUpAdditionalStoppingTimes(std::vector<double>& rAdditionalStoppingTimes)
374 {
375  if (mpElectrodes)
376  {
377  rAdditionalStoppingTimes.push_back(mpElectrodes->GetSwitchOnTime());
378  rAdditionalStoppingTimes.push_back(mpElectrodes->GetSwitchOffTime());
379  }
380 }
381 
382 template<unsigned DIM>
384 {
385  return mHasBath;
386 }
387 
388 // Explicit instantiation
389 template class BidomainProblem<1>;
390 template class BidomainProblem<2>;
391 template class BidomainProblem<3>;
392 
393 
394 // Serialization for Boost >= 1.36
ElementIterator GetElementIteratorBegin(bool skipDeletedElements=true)
boost::shared_ptr< Electrodes< DIM > > mpElectrodes
unsigned mExtracelluarColumnId
static bool IsRegionBath(HeartRegionType regionId)
static bool ReplicateBool(bool flag)
Definition: PetscTools.cpp:186
int GetVariableByName(const std::string &rVariableName)
#define EXCEPTION(message)
Definition: Exception.hpp:143
static HeartRegionType GetValidBathId()
AbstractBidomainSolver< DIM, DIM > * mpSolver
static bool AmMaster()
Definition: PetscTools.cpp:120
BidomainTissue< DIM > * GetBidomainTissue()
void SetUpAdditionalStoppingTimes(std::vector< double > &rAdditionalStoppingTimes)
void SetFixedExtracellularPotentialNodes(std::vector< unsigned > nodes)
std::vector< unsigned > mFixedExtracellularPotentialNodes
void WriteInfo(double time)
void RemoveNullSpace()
void SetFixedExtracellularPotentialNodes(std::vector< unsigned > fixedExtracellularPotentialNodes)
Node< SPACE_DIM > * GetNode(unsigned localIndex) const
AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > * mpMesh
virtual void DefineWriterColumns(bool extending)
bool GetUseStateVariableInterpolation() const
#define EXPORT_TEMPLATE_CLASS_SAME_DIMS(CLASS)
unsigned GetNumNodes() const
void OnEndOfTimestep(double time)
void ResetBoundaryConditionsContainer(BoundaryConditionsContainer< ELEMENT_DIM, SPACE_DIM, 2 > *pBcc)
int DefineVariable(const std::string &rVariableName, const std::string &rVariableUnits)
virtual void WriteOneStep(double time, Vec voltageVec)
unsigned mRowForAverageOfPhiZeroed
NodeIterator GetNodeIteratorBegin(bool skipDeletedNodes=true)
BidomainTissue< DIM > * mpBidomainTissue
virtual void DefineWriterColumns(bool extending)
unsigned GetUnsignedAttribute()
const std::set< unsigned > & rGetBathIdentifiers()
void SetNodeForAverageOfPhiZeroed(unsigned node)
void AtBeginningOfTimestep(double time)
static HeartRegionType GetValidTissueId()
void PutStripedVector(std::vector< int > variableIDs, Vec petscVector)
void PutUnlimitedVariable(double value)
static HeartConfig * Instance()
virtual AbstractCardiacTissue< DIM > * CreateCardiacTissue()
bool GetUseRelativeTolerance() const
virtual AbstractDynamicLinearPdeSolver< DIM, DIM, 2 > * CreateSolver()
void DefineExtraVariablesWriterColumns(bool extending)
static bool IsRegionTissue(HeartRegionType regionId)