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