Chaste  Release::2017.1
AbstractExtendedBidomainSolver.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 #include "AbstractExtendedBidomainSolver.hpp"
37 
38 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
40 {
41  // The base class method that calls this function will only call it with a null linear system
42  assert(this->mpLinearSystem == NULL);
43 
44  // linear system created here
46 
47  if (HeartConfig::Instance()->GetUseAbsoluteTolerance())
48  {
49 #ifdef TRACE_KSP
50  std::cout << "Using absolute tolerance: " << mpConfig->GetAbsoluteTolerance() << "\n";
51 #endif
52  this->mpLinearSystem->SetAbsoluteTolerance(mpConfig->GetAbsoluteTolerance());
53  }
54  else
55  {
56 #ifdef TRACE_KSP
57  std::cout << "Using relative tolerance: " << mpConfig->GetRelativeTolerance() << "\n";
58 #endif
59  this->mpLinearSystem->SetRelativeTolerance(mpConfig->GetRelativeTolerance());
60  }
61 
62  this->mpLinearSystem->SetKspType(HeartConfig::Instance()->GetKSPSolver());
63  this->mpLinearSystem->SetPcType(HeartConfig::Instance()->GetKSPPreconditioner());
64 
65  if (mRowForAverageOfPhiZeroed == INT_MAX)
66  {
67  // not applying average(phi)=0 constraint, so matrix is symmetric
68  this->mpLinearSystem->SetMatrixIsSymmetric(true);
69  }
70  else
71  {
72  //Turn off preallocation so that we can have one dense row in the matrix.
73  PetscMatTools::TurnOffVariableAllocationError(this->mpLinearSystem->rGetLhsMatrix());
74  // applying average(phi)=0 constraint, so matrix is not symmetric
75  this->mpLinearSystem->SetMatrixIsSymmetric(false);
76  }
77 }
78 
79 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
81 {
82  mpExtendedBidomainTissue->SolveCellSystems(existingSolution, PdeSimulationTime::GetTime(), PdeSimulationTime::GetNextTime());
83 }
84 
85 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
87 {
88  double sqrrt_num_nodes = sqrt( this->mpMesh->GetNumNodes());
89  double normalised_basis_value = 1.0 / sqrrt_num_nodes;
90 
91  Vec nullbasis;
92  DistributedVectorFactory* p_factory = this->mpMesh->GetDistributedVectorFactory();
93  nullbasis=p_factory->CreateVec(3);
94  DistributedVector dist_null_basis = p_factory->CreateDistributedVector(nullbasis);
95  DistributedVector::Stripe null_basis_stripe_0(dist_null_basis,0);
96  DistributedVector::Stripe null_basis_stripe_1(dist_null_basis,1);
97  DistributedVector::Stripe null_basis_stripe_2(dist_null_basis,2);
98  for (DistributedVector::Iterator index = dist_null_basis.Begin();
99  index != dist_null_basis.End();
100  ++index)
101  {
102  null_basis_stripe_0[index] = 0.0;
103  null_basis_stripe_1[index] = 0.0;
104  null_basis_stripe_2[index] = normalised_basis_value;
105  }
106  dist_null_basis.Restore();
107  return nullbasis;
108 }
109 
110 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
112 {
113  if (!(this->mpBoundaryConditions->HasDirichletBoundaryConditions()))
114  {
115  // We're not pinning any nodes.
116  if (mRowForAverageOfPhiZeroed==INT_MAX)
117  {
118  // We're not using the 'Average phi_e = 0' method, hence use a null space
119  if (!mNullSpaceCreated)
120  {
121  // No null space set up, so create one and pass it to the linear system
122  Vec nullbasis[] = {GenerateNullBasis()};
123 
124  this->mpLinearSystem->SetNullBasis(nullbasis, 1);
125 
126  PetscTools::Destroy(nullbasis[0]);
127  mNullSpaceCreated = true;
128  }
129  }
130  else // mRowForAverageOfPhiZeroed!=INT_MAX, i.e. we're using the 'Average phi_e = 0' method
131  {
132  // CG (default solver) won't work since the system isn't symmetric anymore. Switch to GMRES
133  this->mpLinearSystem->SetKspType("gmres"); // Switches the solver
134  mpConfig->SetKSPSolver("gmres", true); // Makes sure this change will be reflected in the XML file written to disk at the end of the simulation.
135  //(If the user doesn't have gmres then the "true" warns the user about the switch)
136 
137  // Set average phi_e to zero
138  unsigned matrix_size = this->mpLinearSystem->GetSize();
139  if (!this->mMatrixIsAssembled)
140  {
141 
142  // Set the mRowForAverageOfPhiZeroed-th matrix row to 0 0 1 0 0 1 ...
143  std::vector<unsigned> row_for_average;
144  row_for_average.push_back(mRowForAverageOfPhiZeroed);
145  this->mpLinearSystem->ZeroMatrixRowsWithValueOnDiagonal(row_for_average, 0.0);
146  for (unsigned col_index=0; col_index<matrix_size; col_index++)
147  {
148  if (((col_index-2)%3 == 0) && (col_index>1))//phi_e column indices are 2,5,8,11 etc....
149  {
150  this->mpLinearSystem->SetMatrixElement(mRowForAverageOfPhiZeroed, col_index, 1);
151  }
152 
153  }
154  this->mpLinearSystem->FinaliseLhsMatrix();
155  }
156  // Set the mRowForAverageOfPhiZeroed-th rhs vector row to 0
157  this->mpLinearSystem->SetRhsVectorElement(mRowForAverageOfPhiZeroed, 0);
158 
159  this->mpLinearSystem->FinaliseRhsVector();
160  }
161  }
162  CheckCompatibilityCondition();
163 }
164 
165 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
167 {
168  if (this->mpBoundaryConditions->HasDirichletBoundaryConditions() || mRowForAverageOfPhiZeroed!=INT_MAX )
169  {
170  // not a singular system, no compability condition
171  return;
172  }
173 
174 #ifndef NDEBUG
175  DistributedVector distributed_rhs=this->mpMesh->GetDistributedVectorFactory()->CreateDistributedVector(this->mpLinearSystem->rGetRhsVector());
176  DistributedVector::Stripe distributed_rhs_phi_entries(distributed_rhs,2); // stripe number 2 -> phi_e
177 
178  double local_sum=0;
179  for (DistributedVector::Iterator index = distributed_rhs.Begin();
180  index!= distributed_rhs.End();
181  ++index)
182  {
183  local_sum += distributed_rhs_phi_entries[index];
184  }
185 
186  double global_sum;
187  int mpi_ret = MPI_Allreduce(&local_sum, &global_sum, 1, MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD);
188  assert(mpi_ret == MPI_SUCCESS);
189 
190  if (fabs(global_sum)>1e-6) // magic number! sum should really be a sum of zeros and exactly zero though anyway (or a-a+b-b+c-c.. etc in the case of electrodes)
191  {
192  // LCOV_EXCL_START
193  // shouldn't ever reach this line but useful to have the error printed out if you do
194  std::cout << "Sum of b_{every 3 items} = " << global_sum << " (should be zero for compatibility)\n";
195  EXCEPTION("Linear system does not satisfy compatibility constraint!");
196  // LCOV_EXCL_STOP
197  }
198 #endif
199 }
200 
201 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
203  bool bathSimulation,
207  : AbstractDynamicLinearPdeSolver<ELEMENT_DIM,SPACE_DIM,3>(pMesh),
208  mBathSimulation(bathSimulation),
209  mpExtendedBidomainTissue(pTissue),
210  mpBoundaryConditions(pBcc)
211 {
212  assert(pTissue != NULL);
213  assert(pBcc != NULL);
214 
215  mNullSpaceCreated = false;
216 
217  // important!
218  this->mMatrixIsConstant = true;
219 
220  mRowForAverageOfPhiZeroed = INT_MAX; //this->mpLinearSystem->GetSize() - 1;
222 
223  mpExtendedBidomainAssembler = NULL; // can't initialise until know what dt is
224 }
225 
226 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
228 {
229 }
230 
231 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
233  std::vector<unsigned> fixedExtracellularPotentialNodes)
234 {
235  for (unsigned i=0; i<fixedExtracellularPotentialNodes.size(); i++)
236  {
237  if (fixedExtracellularPotentialNodes[i] >= this->mpMesh->GetNumNodes() )
238  {
239  EXCEPTION("Fixed node number must be less than total number nodes");
240  }
241  }
242 
243  mFixedExtracellularPotentialNodes = fixedExtracellularPotentialNodes;
244 
245  // We will need to recalculate this when HasDirichletBoundaryConditions() is called.
247 
248  for (unsigned i=0; i<mFixedExtracellularPotentialNodes.size(); i++)
249  {
250  if (this->mpMesh->GetDistributedVectorFactory()->IsGlobalIndexLocal(mFixedExtracellularPotentialNodes[i]))
251  {
253 
254  //Throws if node is not owned locally
255  Node<SPACE_DIM>* p_node = this->mpMesh->GetNode(mFixedExtracellularPotentialNodes[i]);
256 
257  //the "false" passed in tells not to check that it is a boundary node (since we might have grounded electrodes within the tissue)
258  GetBoundaryConditions()->AddDirichletBoundaryCondition(p_node, p_boundary_condition, 2, false);
259  }
260  }
261 }
262 
263 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
265 {
266  // Row should be every 3 entries, starting from zero...
267  if (((row-2)%3 != 0) && row!=INT_MAX)
268  {
269  EXCEPTION("Row for applying the constraint 'Average of phi_e = zero' should be every 3 rows");
270  }
271 
273 }
274 
275 // Explicit instantiation
AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > * mpMesh
ExtendedBidomainAssembler< ELEMENT_DIM, SPACE_DIM > * mpExtendedBidomainAssembler
Definition: Node.hpp:58
void SetFixedExtracellularPotentialNodes(std::vector< unsigned > fixedExtracellularPotentialNodes)
void SetRowForAverageOfPhiZeroed(unsigned rowMeanPhiEZero)
static void TurnOffVariableAllocationError(Mat matrix)
DistributedVector CreateDistributedVector(Vec vec, bool readOnly=false)
virtual void InitialiseForSolve(Vec initialSolution=nullptr)
#define EXCEPTION(message)
Definition: Exception.hpp:143
virtual void FinaliseLinearSystem(Vec existingSolution)
BoundaryConditionsContainer< ELEMENT_DIM, SPACE_DIM, 3 > * mpBoundaryConditions
void PrepareForSetupLinearSystem(Vec existingSolution)
void AddDirichletBoundaryCondition(const Node< SPACE_DIM > *pBoundaryNode, const AbstractBoundaryCondition< SPACE_DIM > *pBoundaryCondition, unsigned indexOfUnknown=0, bool checkIfBoundaryNode=true)
static double GetNextTime()
AbstractExtendedBidomainSolver(bool bathSimulation, AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > *pMesh, ExtendedBidomainTissue< SPACE_DIM > *pTissue, BoundaryConditionsContainer< ELEMENT_DIM, SPACE_DIM, 3 > *pBcc)
BoundaryConditionsContainer< ELEMENT_DIM, SPACE_DIM, 3 > * GetBoundaryConditions()
static void Destroy(Vec &rVec)
Definition: PetscTools.hpp:352
std::vector< unsigned > mFixedExtracellularPotentialNodes
static HeartConfig * Instance()
static double GetTime()