Chaste  Release::3.4
BidomainProblem.cpp
1 /*
2 
3 Copyright (c) 2005-2016, 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 
100 template<unsigned DIM>
102 {
104  if (mHasBath)
105  {
106  // get the voltage stripe
107  DistributedVector ic = this->mpMesh->GetDistributedVectorFactory()->CreateDistributedVector(init_cond);
109 
110  for (DistributedVector::Iterator index = ic.Begin();
111  index!= ic.End();
112  ++index)
113  {
114  if (HeartRegionCode::IsRegionBath( this->mpMesh->GetNode( index.Global )->GetRegion() ))
115  {
116  voltage_stripe[index] = 0.0;
117  }
118  }
119  ic.Restore();
120  }
121 
122  return init_cond;
123 }
124 
125 template<unsigned DIM>
127 {
128  AnalyseMeshForBath();
129  mpBidomainTissue = new BidomainTissue<DIM>(this->mpCellFactory, HeartConfig::Instance()->GetUseStateVariableInterpolation());
130  return mpBidomainTissue;
131 }
132 
133 template<unsigned DIM>
135 {
136  /*
137  * NOTE: The this->mpBoundaryConditionsContainer.get() lines below convert a
138  * boost::shared_ptr to a normal pointer, as this is what the solvers are
139  * expecting. We have to be a bit careful though as boost could decide to delete
140  * them whenever it feels like as it won't count the assembers as using them.
141  *
142  * As long as they are kept as member variables here for as long as they are
143  * required in the solvers it should all work OK.
144  */
145  mpSolver = new BidomainSolver<DIM,DIM>(mHasBath,
146  this->mpMesh,
147  mpBidomainTissue,
148  this->mpBoundaryConditionsContainer.get());
149 
150  try
151  {
152  mpSolver->SetFixedExtracellularPotentialNodes(mFixedExtracellularPotentialNodes);
153  mpSolver->SetRowForAverageOfPhiZeroed(mRowForAverageOfPhiZeroed);
154  }
155  catch (const Exception& e)
156  {
157  delete mpSolver;
158  throw e;
159  }
160 
161  return mpSolver;
162 }
163 
164 template<unsigned DIM>
166  AbstractCardiacCellFactory<DIM>* pCellFactory, bool hasBath)
167  : AbstractCardiacProblem<DIM,DIM, 2>(pCellFactory),
168  mpBidomainTissue(NULL),
169  mRowForAverageOfPhiZeroed(INT_MAX),
170  mHasBath(hasBath)
171 {
173 }
174 
175 template<unsigned DIM>
177  : AbstractCardiacProblem<DIM, DIM, 2>(),
178  mpBidomainTissue(NULL),
179  mRowForAverageOfPhiZeroed(INT_MAX)
180 {
182 }
183 
184 
185 
186 template<unsigned DIM>
188 {
189  mFixedExtracellularPotentialNodes.resize(nodes.size());
190  for (unsigned i=0; i<nodes.size(); i++)
191  {
192  // the solver checks that the nodes[i] is less than
193  // the number of nodes in the mesh so this is not done here
194  mFixedExtracellularPotentialNodes[i] = nodes[i];
195  }
196 }
197 
198 template<unsigned DIM>
200 {
201  mRowForAverageOfPhiZeroed = 2*node+1;
202 }
203 
204 template<unsigned DIM>
206 {
207  assert(mpBidomainTissue!=NULL);
208  return mpBidomainTissue;
209 }
210 
211 template<unsigned DIM>
213 {
214  if (PetscTools::AmMaster())
215  {
216  std::cout << "Solved to time " << time << "\n" << std::flush;
217  }
218 
219  double v_max, v_min, phi_max, phi_min;
220 
221  VecStrideMax( this->mSolution, 0, PETSC_NULL, &v_max );
222  VecStrideMin( this->mSolution, 0, PETSC_NULL, &v_min );
223 
224  VecStrideMax( this->mSolution, 1, PETSC_NULL, &phi_max );
225  VecStrideMin( this->mSolution, 1, PETSC_NULL, &phi_min );
226 
227  if (PetscTools::AmMaster())
228  {
229  std::cout << " V; phi_e = " << "[" <<v_min << ", " << v_max << "]" << ";\t"
230  << "[" <<phi_min << ", " << phi_max << "]" << "\n"
231  << std::flush;
232  }
233 }
234 
235 template<unsigned DIM>
237 {
239  if (extending)
240  {
241  mExtracelluarColumnId = this->mpWriter->GetVariableByName("Phi_e");
242  }
243  else
244  {
245  mExtracelluarColumnId = this->mpWriter->DefineVariable("Phi_e","mV");
246  }
248 }
249 
250 template<unsigned DIM>
251 void BidomainProblem<DIM>::WriteOneStep(double time, Vec voltageVec)
252 {
253  this->mpWriter->PutUnlimitedVariable(time);
254  std::vector<int> variable_ids;
255  variable_ids.push_back(this->mVoltageColumnId);
256  variable_ids.push_back(mExtracelluarColumnId);
257  this->mpWriter->PutStripedVector(variable_ids, voltageVec);
259 }
260 
261 template<unsigned DIM>
263 {
265  if (mFixedExtracellularPotentialNodes.empty())
266  {
267  // We're not pinning any nodes.
268  if (mRowForAverageOfPhiZeroed==INT_MAX)
269  {
270  // We're not using the constrain Average phi_e to 0 method, hence use a null space
271  // Check that the KSP solver isn't going to do anything stupid:
272  // phi_e is not bounded, so it'd be wrong to use a relative tolerance
274  {
275  EXCEPTION("Bidomain external voltage is not bounded in this simulation - use KSP *absolute* tolerance");
276  }
277  }
278  }
279 }
280 
281 template<unsigned DIM>
283 {
284  if (!mHasBath)
285  {
286  //Cannot set electrodes when problem has been defined to not have a bath
287  return;
288  }
289 
290  assert(this->mpMesh!=NULL);
291 
292  if (HeartConfig::Instance()->IsElectrodesPresent())
293  {
294  mpElectrodes = (boost::shared_ptr<Electrodes<DIM> >) new Electrodes<DIM>(*(this->mpMesh));
295  }
296 }
297 
298 template<unsigned DIM>
300 {
301  if ( mpElectrodes && mpElectrodes->SwitchOn(time) )
302  {
303  // At the moment mpBcc and mpDefaultBcc point to a set default BC
304  assert(this->mpBoundaryConditionsContainer);
305  //assert(this->mpDefaultBoundaryConditionsContainer);
306 
307  // Note, no point calling this->SetBoundaryConditionsContainer() as the
308  // solver has already been created..
309  mpSolver->ResetBoundaryConditionsContainer(mpElectrodes->GetBoundaryConditionsContainer().get());
310 
311  // ..but we set mpBcc anyway, so the local mpBcc is
312  // the same as the one being used in the solver...
313  this->mpBoundaryConditionsContainer = mpElectrodes->GetBoundaryConditionsContainer();
314 
316  this->mpDefaultBoundaryConditionsContainer = this->mpBoundaryConditionsContainer;
317 
318  // At t==0 or after checkpointing we won't have a system assembled at this stage: BCs will be applied once the matrix
319  // is assembled. Dirichlet BCs will be present at the time of assembly and no null space will be created either.
320  if ( mpSolver->GetLinearSystem() != NULL )
321  {
322  // System matrix is assembled once at the beginning of the simulation. After that, nobody will take care
323  // of applying new BC to the system matrix. Must be triggered explicitly.
324  if (mpElectrodes->HasGroundedElectrode())
325  {
326  this->mpBoundaryConditionsContainer->ApplyDirichletToLinearProblem( *(mpSolver->GetLinearSystem()),
327  true, false);
328  }
329 
330  // If a grounded electrode is switched on, the linear system is not singular anymore. Remove the null space.
331  if (mpElectrodes->HasGroundedElectrode())
332  {
333  mpSolver->GetLinearSystem()->RemoveNullSpace();
334  }
335  }
336  }
337 }
338 
339 template<unsigned DIM>
341 {
342  if ( mpElectrodes && mpElectrodes->SwitchOff(time) )
343  {
344  // At the moment mpBcc should exist and therefore
345  // mpDefaultBcc should be empty (not if electrodes switched on after 0ms)
346  assert(this->mpBoundaryConditionsContainer);
347  //assert(! this->mpDefaultBoundaryConditionsContainer);
348 
349  // Set up default boundary conditions container - no Neumann fluxes
350  // or Dirichlet fixed nodes
351  this->mpDefaultBoundaryConditionsContainer.reset(new BoundaryConditionsContainer<DIM,DIM,2>);
352  for (unsigned problem_index=0; problem_index<2; problem_index++)
353  {
354  this->mpDefaultBoundaryConditionsContainer->DefineZeroNeumannOnMeshBoundary(this->mpMesh, problem_index);
355  }
356 
357  // If there's a grounded electrode, we must remove BC from linear system. At the moment, we don't
358  // have a sensible way of doing this, therefore we reassemble the system.
359  if (mpElectrodes->HasGroundedElectrode())
360  {
361  delete mpSolver;
363  mpSolver->SetTimeStep(HeartConfig::Instance()->GetPdeTimeStep());
364  }
365 
366  // Note, no point calling this->SetBoundaryConditionsContainer() as the
367  // solver has already been created..
368  mpSolver->ResetBoundaryConditionsContainer(this->mpDefaultBoundaryConditionsContainer.get());
369  // ..but we set mpBcc to be mpDefaultBcc anyway, so the local mpBcc is
370  // the same as the one being used in the solver...
371  this->mpBoundaryConditionsContainer = this->mpDefaultBoundaryConditionsContainer;
372  }
373 }
374 
375 
376 
377 template<unsigned DIM>
378 void BidomainProblem<DIM>::SetUpAdditionalStoppingTimes(std::vector<double>& rAdditionalStoppingTimes)
379 {
380  if ( mpElectrodes )
381  {
382  rAdditionalStoppingTimes.push_back( mpElectrodes->GetSwitchOnTime() );
383  rAdditionalStoppingTimes.push_back( mpElectrodes->GetSwitchOffTime() );
384  }
385 }
386 
387 template<unsigned DIM>
389 {
390  return mHasBath;
391 }
392 
394 // Explicit instantiation
396 
397 template class BidomainProblem<1>;
398 template class BidomainProblem<2>;
399 template class BidomainProblem<3>;
400 
401 
402 // Serialization for Boost >= 1.36
ElementIterator GetElementIteratorBegin(bool skipDeletedElements=true)
static bool IsRegionBath(HeartRegionType regionId)
static bool ReplicateBool(bool flag)
Definition: PetscTools.cpp:186
#define EXCEPTION(message)
Definition: Exception.hpp:143
static HeartRegionType GetValidBathId()
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 SetFixedExtracellularPotentialNodes(std::vector< unsigned > fixedExtracellularPotentialNodes)
Node< SPACE_DIM > * GetNode(unsigned localIndex) const
virtual void DefineWriterColumns(bool extending)
bool GetUseStateVariableInterpolation() const
#define EXPORT_TEMPLATE_CLASS_SAME_DIMS(CLASS)
unsigned GetNumNodes() const
void OnEndOfTimestep(double time)
virtual void WriteOneStep(double time, Vec voltageVec)
NodeIterator GetNodeIteratorBegin(bool skipDeletedNodes=true)
virtual void DefineWriterColumns(bool extending)
unsigned GetUnsignedAttribute()
const std::set< unsigned > & rGetBathIdentifiers()
void SetNodeForAverageOfPhiZeroed(unsigned node)
void AtBeginningOfTimestep(double time)
static HeartRegionType GetValidTissueId()
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)