Chaste  Release::2018.1
ExtendedBidomainProblem.cpp
1 /*
2 
3 Copyright (c) 2005-2018, 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 "ExtendedBidomainProblem.hpp"
38 #include "ExtendedBidomainSolver.hpp"
39 #include "AbstractDynamicLinearPdeSolver.hpp"
40 #include "HeartConfig.hpp"
41 #include "Exception.hpp"
42 #include "DistributedVector.hpp"
43 #include "ReplicatableVector.hpp"
44 
45 template<unsigned DIM>
47  AbstractCardiacCellFactory<DIM>* pCellFactory, AbstractCardiacCellFactory<DIM>* pSecondCellFactory, bool hasBath)
48  : AbstractCardiacProblem<DIM,DIM, 3>(pCellFactory),
49  mpSecondCellFactory(pSecondCellFactory),
50  mpExtendedBidomainTissue(NULL),
51  mUserSpecifiedSecondCellConductivities(false),
52  mUserHasSetBidomainValuesExplicitly(false),
53  mpExtracellularStimulusFactory(NULL),
54  mRowForAverageOfPhiZeroed(INT_MAX),
55  mApplyAveragePhieZeroConstraintAfterSolving(false),
56  mUserSuppliedExtracellularStimulus(false),
57  mHasBath(hasBath)
58 {
60 }
61 
62 template<unsigned DIM>
64  : AbstractCardiacProblem<DIM,DIM, 3>(),
65  mpSecondCellFactory(NULL),
66  mpExtendedBidomainTissue(NULL),
67  mUserSpecifiedSecondCellConductivities(false),
68  mUserHasSetBidomainValuesExplicitly(false),
69  mpExtracellularStimulusFactory(NULL),
70  mRowForAverageOfPhiZeroed(INT_MAX),
71  mApplyAveragePhieZeroConstraintAfterSolving(false),
72  mUserSuppliedExtracellularStimulus(false)
73 {
75 }
76 
77 template<unsigned DIM>
79 {
80 
81  DistributedVectorFactory* p_factory = this->mpMesh->GetDistributedVectorFactory();
82  Vec initial_condition = p_factory->CreateVec(3);
83  DistributedVector ic = p_factory->CreateDistributedVector(initial_condition);
84  std::vector<DistributedVector::Stripe> stripe;
85  stripe.reserve(3);
86 
87  stripe.push_back(DistributedVector::Stripe(ic, 0));
88  stripe.push_back(DistributedVector::Stripe(ic, 1));
89  stripe.push_back(DistributedVector::Stripe(ic, 2));
90 
91  for (DistributedVector::Iterator index = ic.Begin();
92  index != ic.End();
93  ++index)
94  {
95  stripe[0][index] = mpExtendedBidomainTissue->GetCardiacCell(index.Global)->GetVoltage();//phi_i of frrst cell = Vm first cell at the start
96  stripe[1][index] = mpExtendedBidomainTissue->GetCardiacSecondCell(index.Global)->GetVoltage();//phi_i of second cell = Vm second cell at the start
97  stripe[2][index] = 0.0;//
98  }
99  ic.Restore();
100 
101  return initial_condition;
102 }
103 
104 template<unsigned DIM>
106 {
107  if (mpExtracellularStimulusFactory == NULL) // user has not passed in any extracellular stimulus in any form
108  {
109  mpExtracellularStimulusFactory = new AbstractStimulusFactory<DIM>();
110  // Create one (with default implementation to zero stimulus everywhere)
111  }
112 
113  assert(mpExtracellularStimulusFactory); // should be created by now, either above or by the user...
114  mpExtracellularStimulusFactory->SetMesh(this->mpMesh);//so, set the mesh into it.
115  mpExtracellularStimulusFactory->SetCompatibleExtracellularStimulus();//make sure compatibility condition will be valid
116 
117  std::vector<AbstractChasteRegion<DIM>* > grounded_regions = mpExtracellularStimulusFactory->GetRegionsToBeGrounded();
118 
119  if ((mUserSuppliedExtracellularStimulus) && grounded_regions.size() > 0) //we check for grunded nodes here
120  {
121  std::vector<unsigned> grounded_indices;
122  for (unsigned global_node_index = 0; global_node_index < this->mpMesh->GetNumNodes(); global_node_index++)
123  {
124  if (this->mpMesh->GetDistributedVectorFactory()->IsGlobalIndexLocal(global_node_index))
125  {
126  for (unsigned region_index = 0; region_index <grounded_regions.size(); region_index++)
127  {
128  if (grounded_regions[region_index]->DoesContain(this->mpMesh->GetNode(global_node_index)->GetPoint()))
129  {
130  grounded_indices.push_back( this->mpMesh->GetNode(global_node_index)->GetIndex() );
131  }
132  }
133  }
134  }
136  SetFixedExtracellularPotentialNodes(grounded_indices);
137  }
138 }
139 
140 template<unsigned DIM>
142 {
143  //set the mesh into the second cell factory as well.
144  mpSecondCellFactory->SetMesh(this->mpMesh);
145 
146  //deal with extracellular stimulus, if any
147  ProcessExtracellularStimulus();
148 
149  //Now create the tissue object
150  mpExtendedBidomainTissue = new ExtendedBidomainTissue<DIM>(this->mpCellFactory, mpSecondCellFactory,mpExtracellularStimulusFactory);
151 
152  //Let the Tissue know if the user wants an extracellular stimulus (or if we had to create a default zero one).
153  mpExtendedBidomainTissue->SetUserSuppliedExtracellularStimulus(mUserSuppliedExtracellularStimulus);
154 
155  //if the user remembered to set a different value for the sigma of the second cell...
156  if (mUserSpecifiedSecondCellConductivities)
157  {
158  mpExtendedBidomainTissue->SetIntracellularConductivitiesSecondCell(mIntracellularConductivitiesSecondCell);
159  }
160  else //..otherwise it gets the same as the first cell (according to heartconfig...)
161  {
162  c_vector<double, DIM> intra_conductivities;
163  HeartConfig::Instance()->GetIntracellularConductivities(intra_conductivities);
164  mpExtendedBidomainTissue->SetIntracellularConductivitiesSecondCell(intra_conductivities);
165  }
166 
167  //the conductivities for the first cell are created within the tissue constructor in the abstract class
168  //here we create the ones for the second cell
169  mpExtendedBidomainTissue->CreateIntracellularConductivityTensorSecondCell();
170 
171  if (mUserHasSetBidomainValuesExplicitly)
172  {
173  mpExtendedBidomainTissue->SetAmFirstCell(mAmFirstCell);
174  mpExtendedBidomainTissue->SetAmSecondCell(mAmSecondCell);
175  mpExtendedBidomainTissue->SetAmGap(mAmGap);
176  mpExtendedBidomainTissue->SetGGap(mGGap);
177  mpExtendedBidomainTissue->SetCmFirstCell(mCmFirstCell);
178  mpExtendedBidomainTissue->SetCmSecondCell(mCmSecondCell);
179  }
180  else//we set all the Am and Cm to the values set by the heartconfig (only one value for all Am and one value for all Cms)
181  {
182  mpExtendedBidomainTissue->SetAmFirstCell(HeartConfig::Instance()->GetSurfaceAreaToVolumeRatio());
183  mpExtendedBidomainTissue->SetAmSecondCell(HeartConfig::Instance()->GetSurfaceAreaToVolumeRatio());
184  mpExtendedBidomainTissue->SetAmGap(HeartConfig::Instance()->GetSurfaceAreaToVolumeRatio());
185  mpExtendedBidomainTissue->SetGGap(0.0);
186  mpExtendedBidomainTissue->SetCmFirstCell(HeartConfig::Instance()->GetCapacitance());
187  mpExtendedBidomainTissue->SetCmSecondCell(HeartConfig::Instance()->GetCapacitance());
188  }
189 
190  mpExtendedBidomainTissue->SetGgapHeterogeneities(mGgapHeterogeneityRegions, mGgapHeterogenousValues);//set user input into the tissue class
191  mpExtendedBidomainTissue->CreateGGapConductivities();//if vectors are empty, mGgap will be put everywhere by this method.
192 
193  return mpExtendedBidomainTissue;
194 }
195 
196 template<unsigned DIM>
197 void ExtendedBidomainProblem<DIM>::SetExtendedBidomainParameters(double Am1, double Am2, double AmGap, double Cm1, double Cm2, double Ggap)
198 {
199  mAmFirstCell = Am1;
200  mAmSecondCell = Am2;
201  mAmGap = AmGap;
202  mCmFirstCell = Cm1;
203  mCmSecondCell = Cm2;
204  mGGap = Ggap;
205 
206  mUserHasSetBidomainValuesExplicitly = true;
207 }
208 
209 template <unsigned DIM>
210 void ExtendedBidomainProblem<DIM>::SetGgapHeterogeneities ( std::vector<boost::shared_ptr<AbstractChasteRegion<DIM> > >& rGgapHeterogeneityRegions, std::vector<double>& rGgapValues)
211 {
212  if (rGgapHeterogeneityRegions.size() != rGgapValues.size() )
213  {
214  EXCEPTION ("Gap junction heterogeneity areas must be of the same number as the heterogeneity values");
215  }
216  mGgapHeterogeneityRegions = rGgapHeterogeneityRegions;
217  mGgapHeterogenousValues =rGgapValues;
218 }
219 
220 template <unsigned DIM>
222 {
223  mpExtracellularStimulusFactory = pFactory;
224  mUserSuppliedExtracellularStimulus = true;
225 }
226 
227 template<unsigned DIM>
229 {
230  /*
231  * NOTE: The this->mpBoundaryConditionsContainer.get() lines below convert a
232  * boost::shared_ptr to a normal pointer, as this is what the assemblers are
233  * expecting. We have to be a bit careful though as boost could decide to delete
234  * them whenever it feels like as it won't count the assemblers as using them.
235  *
236  * As long as they are kept as member variables here for as long as they are
237  * required in the assemblers it should all work OK.
238  */
239 
240  mpSolver = new ExtendedBidomainSolver<DIM,DIM>( mHasBath,
241  this->mpMesh,
242  mpExtendedBidomainTissue,
243  this->mpBoundaryConditionsContainer.get());
244 
245  try
246  {
247  mpSolver->SetFixedExtracellularPotentialNodes(mFixedExtracellularPotentialNodes);
248  mpSolver->SetRowForAverageOfPhiZeroed(mRowForAverageOfPhiZeroed);
249  }
250  catch (const Exception& e)
251  {
252  delete mpSolver;
253  throw e;
254  }
255 
256  return mpSolver;
257 }
258 
259 template<unsigned DIM>
261 {
262  if (!mUserSuppliedExtracellularStimulus)
263  {
264  delete mpExtracellularStimulusFactory;
265  }
266 }
267 
268 template<unsigned DIM>
270 {
271  for (unsigned i = 0; i < DIM; i++)
272  {
273  mIntracellularConductivitiesSecondCell[i] = conductivities[i];
274  }
275  mUserSpecifiedSecondCellConductivities = true;
276 }
277 
278 template<unsigned DIM>
280 {
281  assert(mFixedExtracellularPotentialNodes.size() == 0);
282  mFixedExtracellularPotentialNodes.resize(nodes.size());
283  for (unsigned i=0; i<nodes.size(); i++)
284  {
285  // the assembler checks that the nodes[i] is less than
286  // the number of nodes in the mesh so this is not done here
287  mFixedExtracellularPotentialNodes[i] = nodes[i];
288  }
289 }
290 
291 template<unsigned DIM>
293 {
294  if (node==0)
295  {
296  mRowForAverageOfPhiZeroed = 2;
297  }
298  else
299  {
300  //Phie is every three lines, starting from zero...
301  mRowForAverageOfPhiZeroed = 3*node - 1;
302  }
303 }
304 
305 template<unsigned DIM>
307 {
308  assert(mpExtendedBidomainTissue!=NULL);
309  return mpExtendedBidomainTissue;
310 }
311 
312 template<unsigned DIM>
314 {
315  if (PetscTools::AmMaster())
316  {
317  std::cout << "Solved to time " << time << "\n" << std::flush;
318  }
319 
320  double V_max_first_cell, V_min_first_cell, V_max_second_cell, V_min_second_cell, phi_e_min, phi_e_max;
321 
322  VecStrideMax( this->mSolution, 0, PETSC_NULL, &V_max_first_cell );
323  VecStrideMin( this->mSolution, 0, PETSC_NULL, &V_min_first_cell );
324 
325  VecStrideMax( this->mSolution, 1, PETSC_NULL, &V_max_second_cell );
326  VecStrideMin( this->mSolution, 1, PETSC_NULL, &V_min_second_cell );
327 
328  VecStrideMax( this->mSolution, 2, PETSC_NULL, &phi_e_max );
329  VecStrideMin( this->mSolution, 2, PETSC_NULL, &phi_e_min );
330 
331  if (PetscTools::AmMaster())
332  {
333  std::cout << " V first cell = " << "[" <<V_min_first_cell << ", " << V_max_first_cell << "]" << ";\n"
334  << " V second cell = " << "[" <<V_min_second_cell << ", " << V_max_second_cell << "]" << ";\n"
335  << " Phi_e = " << "[" <<phi_e_min << ", " << phi_e_max << "]" << ";\n"
336  << std::flush;
337  }
338 }
339 
340 template<unsigned DIM>
342 {
343  if (!extending)
344  {
345  if (this->mNodesToOutput.empty())
346  {
347  // Set writer to output all nodes
348  this->mpWriter->DefineFixedDimension(this->mpMesh->GetNumNodes());
349  }
350 // else
351 // {
352 // // Output only the nodes indicted
353 // this->mpWriter->DefineFixedDimension( this->mNodesToOutput, this->mpMesh->GetNumNodes() );
354 // }
355  //mNodeColumnId = mpWriter->DefineVariable("Node", "dimensionless");
356  mVoltageColumnId_Vm1 = this->mpWriter->DefineVariable("V","mV");
357  mVoltageColumnId_Vm2 = this->mpWriter->DefineVariable("V_2","mV");
358  mVoltageColumnId_Phie = this->mpWriter->DefineVariable("Phi_e","mV");
359  mVariablesIDs.push_back(mVoltageColumnId_Vm1);
360  mVariablesIDs.push_back(mVoltageColumnId_Vm2);
361  mVariablesIDs.push_back(mVoltageColumnId_Phie);
362 
363  // Only used to get an estimate of the # of timesteps below (copied from Abstract class)
365  HeartConfig::Instance()->GetSimulationDuration(),
366  HeartConfig::Instance()->GetPrintingTimeStep());
367  this->mpWriter->DefineUnlimitedDimension("Time", "msecs", stepper.EstimateTimeSteps()+1); // +1 for start and end
368  }
369  else
370  {
371  mVoltageColumnId_Vm1 = this->mpWriter->GetVariableByName("V");
372  mVoltageColumnId_Vm2 = this->mpWriter->GetVariableByName("V_2");
373  mVoltageColumnId_Phie = this->mpWriter->GetVariableByName("Phi_e");
374  }
375  //define any extra variable. NOTE: it must be in the first cell (not the second)
377 
378 }
379 
380 template<unsigned DIM>
381 void ExtendedBidomainProblem<DIM>::WriteOneStep(double time, Vec voltageVec)
382 {
383  this->mpWriter->PutUnlimitedVariable(time);
384 
385  // Create a striped vector
386  Vec ordered_voltages = this->mpMesh->GetDistributedVectorFactory()->CreateVec(3);
387  DistributedVector wrapped_ordered_solution = this->mpMesh->GetDistributedVectorFactory()->CreateDistributedVector(ordered_voltages);
388  DistributedVector wrapped_solution = this->mpMesh->GetDistributedVectorFactory()->CreateDistributedVector(voltageVec);
389 
390  DistributedVector::Stripe V_first_cell_stripe(wrapped_solution,0);
391  DistributedVector::Stripe V_second_cell_stripe(wrapped_solution,1);
392  DistributedVector::Stripe phi_e_stripe(wrapped_solution,2);
393 
394  DistributedVector::Stripe wrapped_ordered_solution_first_stripe(wrapped_ordered_solution,0);
395  DistributedVector::Stripe wrapped_ordered_solution_second_stripe(wrapped_ordered_solution,1);
396  DistributedVector::Stripe wrapped_ordered_solution_third_stripe(wrapped_ordered_solution,2);
397 
398  for (DistributedVector::Iterator index = wrapped_solution.Begin();
399  index != wrapped_solution.End();
400  ++index)
401  {
402  wrapped_ordered_solution_first_stripe[index] = V_first_cell_stripe[index];
403  wrapped_ordered_solution_second_stripe[index] = V_second_cell_stripe[index];
404  wrapped_ordered_solution_third_stripe[index] = phi_e_stripe[index];
405  }
406  wrapped_solution.Restore();
407  wrapped_ordered_solution.Restore();
408 
409  this->mpWriter->PutStripedVector(mVariablesIDs, ordered_voltages);
410  PetscTools::Destroy(ordered_voltages);
411  //write any extra variable. Note that this method in the parent class will
412  //take the extra variable only from the first cell.
415 }
416 
417 template<unsigned DIM>
419 {
421  if (mFixedExtracellularPotentialNodes.empty())
422  {
423  // We're not pinning any nodes.
424  if (mRowForAverageOfPhiZeroed==INT_MAX)
425  {
426  // We're not using the constrain Average phi_e to 0 method, hence use a null space
427  // Check that the KSP solver isn't going to do anything stupid:
428  // phi_e is not bounded, so it'd be wrong to use a relative tolerance
430  {
431  EXCEPTION("Bidomain external voltage is not bounded in this simulation - use KSP *absolute* tolerance");
432  }
433  }
434  }
435 }
436 
437 template<unsigned DIM>
439 {
440  return mHasBath;
441 }
442 
443 template<unsigned DIM>
445 {
446  mHasBath = hasBath;
447 }
448 
449 // Explicit instantiation
450 template class ExtendedBidomainProblem<1>;
451 template class ExtendedBidomainProblem<2>;
452 template class ExtendedBidomainProblem<3>;
453 
454 // Serialization for Boost >= 1.36
void SetExtendedBidomainParameters(double Am1, double Am2, double AmGap, double Cm1, double Cm2, double Ggap)
virtual AbstractDynamicLinearPdeSolver< DIM, DIM, 3 > * CreateSolver()
virtual AbstractCardiacTissue< DIM > * CreateCardiacTissue()
ExtendedBidomainTissue< DIM > * GetExtendedBidomainTissue()
static void Barrier(const std::string callerId="")
Definition: PetscTools.cpp:134
void SetExtracellularStimulusFactory(AbstractStimulusFactory< DIM > *pFactory)
void SetFixedExtracellularPotentialNodes(std::vector< unsigned > fixedExtracellularPotentialNodes)
void GetIntracellularConductivities(c_vector< double, 3 > &rIntraConductivities) const
DistributedVector CreateDistributedVector(Vec vec, bool readOnly=false)
#define EXCEPTION(message)
Definition: Exception.hpp:143
static bool AmMaster()
Definition: PetscTools.cpp:120
virtual void WriteOneStep(double time, Vec voltageVec)
void SetIntracellularConductivitiesForSecondCell(c_vector< double, DIM > conductivities)
void SetGgapHeterogeneities(std::vector< boost::shared_ptr< AbstractChasteRegion< DIM > > > &rGgapHeterogeneityRegions, std::vector< double > &rGgapValues)
void SetNodeForAverageOfPhiZeroed(unsigned node)
virtual void DefineWriterColumns(bool extending)
#define EXPORT_TEMPLATE_CLASS_SAME_DIMS(CLASS)
void SetFixedExtracellularPotentialNodes(std::vector< unsigned > nodes)
unsigned EstimateTimeSteps() const
static void Destroy(Vec &rVec)
Definition: PetscTools.hpp:352
void SetUserSuppliedExtracellularStimulus(bool flag)
static HeartConfig * Instance()
bool GetUseRelativeTolerance() const
std::vector< unsigned > mFixedExtracellularPotentialNodes
void DefineExtraVariablesWriterColumns(bool extending)