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