Chaste  Build::
FiniteDifferenceSolver.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 "LinearSystem.hpp"
37 #include "ReplicatableVector.hpp"
38 #include "VesselSegment.hpp"
39 #include "FiniteDifferenceSolver.hpp"
40 #include "LinearSteadyStateDiffusionReactionPde.hpp"
41 #include "SimplePetscNonlinearSolver.hpp"
42 #include "BaseUnits.hpp"
43 
44 // Nonlinear solve method interfaces, needed later.
45 template<unsigned DIM>
46 PetscErrorCode HyrbidFiniteDifference_ComputeResidual(SNES snes, Vec solution_guess, Vec residual, void* pContext);
47 #if ( PETSC_VERSION_MAJOR==3 && PETSC_VERSION_MINOR>=5 )
48 template<unsigned DIM>
49 PetscErrorCode HyrbidFiniteDifference_ComputeJacobian(SNES snes, Vec input, Mat jacobian, Mat preconditioner, void* pContext);
50 #else
51 template<unsigned DIM>
52 PetscErrorCode HyrbidFiniteDifference_ComputeJacobian(SNES snes,Vec input,Mat* pJacobian ,Mat* pPreconditioner,MatStructure* pMatStructure ,void* pContext);
53 #endif
54 
55 template<unsigned DIM>
58  mpBoundaryConditions(),
59  mUpdateBoundaryConditionsEachSolve(true),
60  mBoundaryConditionsSet(false)
61 {
62 
63 }
64 
65 template<unsigned DIM>
66 boost::shared_ptr<FiniteDifferenceSolver<DIM> > FiniteDifferenceSolver<DIM>::Create()
67 {
68  MAKE_PTR(FiniteDifferenceSolver<DIM>, pSelf);
69  return pSelf;
70 }
71 
72 template<unsigned DIM>
74 {
75 
76 }
77 
78 template<unsigned DIM>
79 boost::shared_ptr<std::vector<std::pair<bool, units::quantity<unit::concentration> > > > FiniteDifferenceSolver<DIM>::GetRGBoundaryConditions()
80 {
81  return mpBoundaryConditions;
82 }
83 
84 template<unsigned DIM>
86 {
88 }
89 
90 template<unsigned DIM>
92 {
93  // Set up the grid and PDE
94  if(!this->mpRegularGrid)
95  {
96  EXCEPTION("This solver needs a regular grid to be set before calling Setup.");
97  }
98 
99  if(!this->mpPde and !this->mpNonLinearPde)
100  {
101  EXCEPTION("This solver needs a PDE to be set before calling Setup.");
102  }
103 
104  if(this->CellPopulationIsSet())
105  {
106  this->mpRegularGrid->SetCellPopulation(*(this->mpCellPopulation), this->mCellPopulationReferenceLength);
107  }
108 
109  if(this->mpNetwork)
110  {
111  this->mpRegularGrid->SetVesselNetwork(this->mpNetwork);
112  }
113 
114  if(this->mpPde)
115  {
116  this->mpPde->SetRegularGrid(this->mpRegularGrid);
117  }
118  else
119  {
120  this->mpNonLinearPde->SetRegularGrid(this->mpRegularGrid);
121  }
122 
123  // Set up the boundary conditions. Use a different description from normal DiscreteContinuum BCs for efficiency.
124  mpBoundaryConditions = boost::shared_ptr<std::vector<std::pair<bool, units::quantity<unit::concentration> > > > (new std::vector<std::pair<bool, units::quantity<unit::concentration> > >(this->mpRegularGrid->GetNumberOfPoints()));
125  for(unsigned idx=0; idx<this->mpRegularGrid->GetNumberOfPoints(); idx++)
126  {
127  (*mpBoundaryConditions)[idx] = std::pair<bool, units::quantity<unit::concentration> >(false, 0.0*this->mReferenceConcentration);
128  }
129  for(unsigned bound_index=0; bound_index<this->mBoundaryConditions.size(); bound_index++)
130  {
131  this->mBoundaryConditions[bound_index]->SetRegularGrid(this->mpRegularGrid);
132  }
133 
134  // Set up the vtk solution grid
136 
137  // Update the source strengths and boundary conditions;
138  Update();
139 
140  this->IsSetupForSolve = true;
141 }
142 
143 template<unsigned DIM>
145 {
146  // Update the PDE source strengths
147  if(this->mpPde)
148  {
149  this->mpPde->UpdateDiscreteSourceStrengths();
150  }
151  else
152  {
153  this->mpNonLinearPde->UpdateDiscreteSourceStrengths();
154  }
155 
156  // Update the boundary conditions
158  {
159  for(unsigned bound_index=0; bound_index<this->mBoundaryConditions.size(); bound_index++)
160  {
161  this->mBoundaryConditions[bound_index]->UpdateRegularGridBoundaryConditions(mpBoundaryConditions);
162  }
163  mBoundaryConditionsSet = true;
164  }
165 }
166 
167 template<unsigned DIM>
169 {
170  // Set up the system
171  unsigned number_of_points = this->mpRegularGrid->GetNumberOfPoints();
172  unsigned extents_x = this->mpRegularGrid->GetExtents()[0];
173  unsigned extents_y = this->mpRegularGrid->GetExtents()[1];
174  unsigned extents_z = this->mpRegularGrid->GetExtents()[2];
175 
176  units::quantity<unit::time> reference_time = BaseUnits::Instance()->GetReferenceTimeScale();
177  units::quantity<unit::length> spacing = this->mpRegularGrid->GetSpacing();
178 
179  double diffusion_term = 0.0;
180  if(this->mpPde)
181  {
182  diffusion_term = (this->mpPde->ComputeIsotropicDiffusionTerm() / (spacing * spacing))*reference_time;
183  }
184  else
185  {
186  diffusion_term = (this->mpNonLinearPde->ComputeIsotropicDiffusionTerm() / (spacing * spacing))*reference_time;
187  }
188 
189  LinearSystem linear_system(number_of_points, 7);
190  for (unsigned i = 0; i < extents_z; i++) // Z
191  {
192  for (unsigned j = 0; j < extents_y; j++) // Y
193  {
194  for (unsigned k = 0; k < extents_x; k++) // X
195  {
196  unsigned grid_index = this->mpRegularGrid->Get1dGridIndex(k, j, i);
197 
198  linear_system.AddToMatrixElement(grid_index, grid_index, this->mpPde->ComputeLinearInUCoeffInSourceTerm(grid_index)*reference_time - 6.0 * diffusion_term);
199 
200  // Assume no flux on domain boundaries by default
201  // No flux at x bottom
202  if (k > 0)
203  {
204  linear_system.AddToMatrixElement(grid_index, grid_index - 1, diffusion_term);
205  }
206  else
207  {
208  linear_system.AddToMatrixElement(grid_index, grid_index, diffusion_term);
209  }
210 
211  // No flux at x top
212  if (k < extents_x - 1)
213  {
214  linear_system.AddToMatrixElement(grid_index, grid_index + 1, diffusion_term);
215  }
216  else
217  {
218  linear_system.AddToMatrixElement(grid_index, grid_index, diffusion_term);
219  }
220 
221  // No flux at y bottom
222  if (j > 0)
223  {
224  linear_system.AddToMatrixElement(grid_index, grid_index - extents_x, diffusion_term);
225  }
226  else
227  {
228  linear_system.AddToMatrixElement(grid_index, grid_index, diffusion_term);
229  }
230 
231  // No flux at y top
232  if (j < extents_y - 1)
233  {
234  linear_system.AddToMatrixElement(grid_index, grid_index + extents_x, diffusion_term);
235  }
236  else
237  {
238  linear_system.AddToMatrixElement(grid_index, grid_index, diffusion_term);
239  }
240 
241  // No flux at z bottom
242  if (i > 0)
243  {
244  linear_system.AddToMatrixElement(grid_index, grid_index - extents_x * extents_y, diffusion_term);
245  }
246  else
247  {
248  linear_system.AddToMatrixElement(grid_index, grid_index, diffusion_term);
249  }
250 
251  // No flux at z top
252  if (i < extents_z - 1)
253  {
254  linear_system.AddToMatrixElement(grid_index, grid_index + extents_x * extents_y, diffusion_term);
255  }
256  else
257  {
258  linear_system.AddToMatrixElement(grid_index, grid_index, diffusion_term);
259  }
260  linear_system.SetRhsVectorElement(grid_index, -this->mpPde->ComputeConstantInUSourceTerm(grid_index)*(reference_time/this->mReferenceConcentration));
261  }
262  }
263  }
264 
265  // Apply the boundary conditions
266  std::vector<unsigned> bc_indices;
267  for(unsigned idx=0; idx<this->mpRegularGrid->GetNumberOfPoints(); idx++)
268  {
269  if((*mpBoundaryConditions)[idx].first)
270  {
271  bc_indices.push_back(idx);
272  linear_system.SetRhsVectorElement(idx, (*mpBoundaryConditions)[idx].second/this->mReferenceConcentration);
273  }
274  }
275  linear_system.ZeroMatrixRowsWithValueOnDiagonal(bc_indices, 1.0);
276 
277  // Solve the linear system
278  linear_system.AssembleFinalLinearSystem();
279  ReplicatableVector soln_repl(linear_system.Solve());
280 
281  // Populate the solution vector
282  std::vector<units::quantity<unit::concentration> > concs = std::vector<units::quantity<unit::concentration> >(number_of_points,
283  0.0*this->mReferenceConcentration);
284  for (unsigned row = 0; row < number_of_points; row++)
285  {
286  concs[row] = soln_repl[row]*this->mReferenceConcentration;
287  }
288 
289  this->UpdateSolution(concs);
290 
291  if (this->mWriteSolution)
292  {
293  this->Write();
294  }
295 }
296 
297 template<unsigned DIM>
299 {
300  if(!this->IsSetupForSolve)
301  {
302  Setup();
303  }
304 
305  if(this->mpPde)
306  {
307  DoLinearSolve();
308  }
309  else
310  {
311  // Set up initial Guess
312  unsigned number_of_points = this->mpRegularGrid->GetNumberOfPoints();
313  Vec initial_guess=PetscTools::CreateAndSetVec(number_of_points, 1.0);
314 
315  SimplePetscNonlinearSolver solver_petsc;
316  int length = 7;
317  Vec answer_petsc = solver_petsc.Solve(&HyrbidFiniteDifference_ComputeResidual<DIM>,
318  &HyrbidFiniteDifference_ComputeJacobian<DIM>, initial_guess, length, this);
319 
320  ReplicatableVector soln_repl(answer_petsc);
321 
322  // Populate the solution vector
323  this->mConcentrations = std::vector<units::quantity<unit::concentration> >(number_of_points,
324  0.0*this->mReferenceConcentration);
325  for (unsigned row = 0; row < number_of_points; row++)
326  {
327  this->mConcentrations[row] = soln_repl[row]*this->mReferenceConcentration;
328  }
329 
330  this->UpdateSolution(this->mConcentrations);
331 
332  if (this->mWriteSolution)
333  {
334  this->Write();
335  }
336  }
337 }
338 
339 template<unsigned DIM>
340 PetscErrorCode HyrbidFiniteDifference_ComputeResidual(SNES snes, Vec solution_guess, Vec residual, void* pContext)
341 {
343 
344  unsigned extents_x = solver->GetGrid()->GetExtents()[0];
345  unsigned extents_y = solver->GetGrid()->GetExtents()[1];
346  unsigned extents_z = solver->GetGrid()->GetExtents()[2];
347 
348  units::quantity<unit::time> reference_time = BaseUnits::Instance()->GetReferenceTimeScale();
349  units::quantity<unit::length> spacing = solver->GetGrid()->GetSpacing();
350  double diffusion_term = (solver->GetNonLinearPde()->ComputeIsotropicDiffusionTerm() / (spacing * spacing))*reference_time;
351 
352  // It used to be possible to work directly with the solution_guess Vec, but now it seems to give read only errors.
353  // Copy the vector for now.
354  ReplicatableVector soln_guess_repl(solution_guess);
355 
356  // Get the residual vector
357  PetscVecTools::Zero(residual);
358  for (unsigned i = 0; i < extents_z; i++) // Z
359  {
360  for (unsigned j = 0; j < extents_y; j++) // Y
361  {
362  for (unsigned k = 0; k < extents_x; k++) // X
363  {
364  unsigned grid_index = solver->GetGrid()->Get1dGridIndex(k, j, i);
365  double grid_guess = soln_guess_repl[grid_index];
366  units::quantity<unit::concentration> scale_grid_guess = grid_guess*solver->GetReferenceConcentration();
367 
368  PetscVecTools::AddToElement(residual, grid_index, grid_guess * (- 6.0 * diffusion_term) +
369  solver->GetNonLinearPde()->ComputeNonlinearSourceTerm(grid_index, scale_grid_guess)*(reference_time/solver->GetReferenceConcentration()));
370 
371  // Assume no flux on domain boundaries by default
372  // No flux at x bottom
373  if (k > 0)
374  {
375  double neighbour_guess = soln_guess_repl[grid_index-1];
376  PetscVecTools::AddToElement(residual, grid_index, neighbour_guess * diffusion_term);
377  }
378  else
379  {
380  PetscVecTools::AddToElement(residual, grid_index, diffusion_term * grid_guess);
381  }
382 
383  // No flux at x top
384  if (k < extents_x - 1)
385  {
386  double neighbour_guess = soln_guess_repl[grid_index+1];
387  PetscVecTools::AddToElement(residual, grid_index, diffusion_term * neighbour_guess);
388  }
389  else
390  {
391  PetscVecTools::AddToElement(residual, grid_index, diffusion_term * grid_guess);
392  }
393 
394  // No flux at y bottom
395  if (j > 0)
396  {
397  double neighbour_guess = soln_guess_repl[grid_index-extents_x];
398  PetscVecTools::AddToElement(residual, grid_index, diffusion_term* neighbour_guess);
399  }
400  else
401  {
402  PetscVecTools::AddToElement(residual, grid_index, diffusion_term * grid_guess);
403  }
404 
405  // No flux at y top
406  if (j < extents_y - 1)
407  {
408  double neighbour_guess = soln_guess_repl[grid_index+extents_x];
409  PetscVecTools::AddToElement(residual, grid_index, diffusion_term* neighbour_guess);
410  }
411  else
412  {
413  PetscVecTools::AddToElement(residual, grid_index, diffusion_term * grid_guess);
414  }
415 
416  // No flux at z bottom
417  if (i > 0)
418  {
419  double neighbour_guess = soln_guess_repl[grid_index - extents_x * extents_y];
420  PetscVecTools::AddToElement(residual, grid_index, diffusion_term*neighbour_guess);
421  }
422  else
423  {
424  PetscVecTools::AddToElement(residual, grid_index, diffusion_term * grid_guess);
425  }
426 
427  // No flux at z top
428  if (i < extents_z - 1)
429  {
430  double neighbour_guess = soln_guess_repl[grid_index + extents_x * extents_y];
431  PetscVecTools::AddToElement(residual, grid_index, diffusion_term * neighbour_guess);
432  }
433  else
434  {
435  PetscVecTools::AddToElement(residual, grid_index, diffusion_term * grid_guess);
436  }
437  }
438  }
439  }
440 
441  PetscVecTools::Finalise(residual);
442 
443  // Dirichlet Boundary conditions
444  std::vector<unsigned> bc_indices;
445  for(unsigned idx=0; idx<solver->GetGrid()->GetNumberOfPoints(); idx++)
446  {
447  if((*(solver->GetRGBoundaryConditions()))[idx].first)
448  {
449  PetscVecTools::SetElement(residual, idx, 0.0);
450  }
451  }
452  PetscVecTools::Finalise(residual);
453 
454  return 0;
455 }
456 
457 #if ( PETSC_VERSION_MAJOR==3 && PETSC_VERSION_MINOR>=5 )
458 template<unsigned DIM>
459 PetscErrorCode HyrbidFiniteDifference_ComputeJacobian(SNES snes, Vec input, Mat jacobian, Mat preconditioner, void* pContext)
460 {
461 #else
462 template<unsigned DIM>
463 PetscErrorCode HyrbidFiniteDifference_ComputeJacobian(SNES snes, Vec input, Mat* pJacobian, Mat* pPreconditioner, MatStructure* pMatStructure, void* pContext)
464 {
465  Mat jacobian = *pJacobian;
466 #endif
468  PetscMatTools::Zero(jacobian);
469  PetscMatTools::SwitchWriteMode(jacobian);
470 
471  ReplicatableVector input_repl(input);
472 
473  unsigned extents_x = solver->GetGrid()->GetExtents()[0];
474  unsigned extents_y = solver->GetGrid()->GetExtents()[1];
475  unsigned extents_z = solver->GetGrid()->GetExtents()[2];
476 
477  units::quantity<unit::time> reference_time = BaseUnits::Instance()->GetReferenceTimeScale();
478  units::quantity<unit::length> spacing = solver->GetGrid()->GetSpacing();
479 
480  double diffusion_term = (solver->GetNonLinearPde()->ComputeIsotropicDiffusionTerm() / (spacing * spacing))*reference_time;
481 
482  // Get the residual vector
483  for (unsigned i = 0; i < extents_z; i++) // Z
484  {
485  for (unsigned j = 0; j < extents_y; j++) // Y
486  {
487  for (unsigned k = 0; k < extents_x; k++) // X
488  {
489  unsigned grid_index = solver->GetGrid()->Get1dGridIndex(k, j, i);
490  double grid_guess = input_repl[grid_index];
491  units::quantity<unit::concentration> scale_grid_guess = grid_guess*solver->GetReferenceConcentration();
492 
493  PetscMatTools::AddToElement(jacobian, grid_index, grid_index, - 6.0 * diffusion_term +
494  solver->GetNonLinearPde()->ComputeNonlinearSourceTermPrime(grid_index, scale_grid_guess)*reference_time);
495 
496  // Assume no flux on domain boundaries by default
497  // No flux at x bottom
498  if (k > 0)
499  {
500  PetscMatTools::AddToElement(jacobian, grid_index, grid_index - 1, diffusion_term);
501  }
502  else
503  {
504  PetscMatTools::AddToElement(jacobian, grid_index, grid_index, diffusion_term);
505  }
506 
507  // No flux at x top
508  if (k < extents_x - 1)
509  {
510  PetscMatTools::AddToElement(jacobian, grid_index, grid_index + 1, diffusion_term);
511  }
512  else
513  {
514  PetscMatTools::AddToElement(jacobian, grid_index, grid_index, diffusion_term);
515  }
516 
517  // No flux at y bottom
518  if (j > 0)
519  {
520  PetscMatTools::AddToElement(jacobian, grid_index, grid_index - extents_x, diffusion_term);
521  }
522  else
523  {
524  PetscMatTools::AddToElement(jacobian, grid_index, grid_index, diffusion_term);
525  }
526 
527  // No flux at y top
528  if (j < extents_y - 1)
529  {
530  PetscMatTools::AddToElement(jacobian, grid_index, grid_index + extents_x, diffusion_term);
531  }
532  else
533  {
534  PetscMatTools::AddToElement(jacobian, grid_index, grid_index, diffusion_term);
535  }
536 
537  // No flux at z bottom
538  if (i > 0)
539  {
540  PetscMatTools::AddToElement(jacobian, grid_index, grid_index - extents_x * extents_y, diffusion_term);
541  }
542  else
543  {
544  PetscMatTools::AddToElement(jacobian, grid_index,grid_index, diffusion_term);
545  }
546 
547  // No flux at z top
548  if (i < extents_z - 1)
549  {
550  PetscMatTools::AddToElement(jacobian, grid_index, grid_index + extents_x * extents_y, diffusion_term);
551  }
552  else
553  {
554  PetscMatTools::AddToElement(jacobian, grid_index, grid_index, diffusion_term);
555  }
556  }
557  }
558  }
559 
560  // Apply the boundary conditions
561  std::vector<unsigned> bc_indices;
562  for(unsigned idx=0; idx<solver->GetGrid()->GetNumberOfPoints(); idx++)
563  {
564  if((*(solver->GetRGBoundaryConditions()))[idx].first)
565  {
566  bc_indices.push_back(idx);
567  }
568  }
569 
570  PetscMatTools::ZeroRowsWithValueOnDiagonal(jacobian, bc_indices, 1.0);
571 
572  PetscMatTools::Finalise(jacobian);
573  return 0;
574 }
575 
576 
577 // Explicit instantiation
578 template class FiniteDifferenceSolver<2>;
579 template class FiniteDifferenceSolver<3>;
boost::shared_ptr< std::vector< std::pair< bool, units::quantity< unit::concentration > > > > GetRGBoundaryConditions()
boost::shared_ptr< std::vector< std::pair< bool, units::quantity< unit::concentration > > > > mpBoundaryConditions
boost::shared_ptr< AbstractDiscreteContinuumLinearEllipticPde< DIM, DIM > > mpPde
units::quantity< unit::time > GetReferenceTimeScale()
Definition: BaseUnits.cpp:72
void UpdateBoundaryConditionsEachSolve(bool doUpdate)
static boost::shared_ptr< FiniteDifferenceSolver< DIM > > Create()
AbstractCellPopulation< DIM > * mpCellPopulation
std::vector< units::quantity< unit::concentration > > mConcentrations
std::vector< boost::shared_ptr< DiscreteContinuumBoundaryCondition< DIM > > > mBoundaryConditions
boost::shared_ptr< AbstractDiscreteContinuumNonLinearEllipticPde< DIM, DIM > > GetNonLinearPde()
boost::shared_ptr< AbstractDiscreteContinuumNonLinearEllipticPde< DIM, DIM > > mpNonLinearPde
static BaseUnits * Instance()
Definition: BaseUnits.cpp:53
boost::shared_ptr< VesselNetwork< DIM > > mpNetwork
units::quantity< unit::concentration > mReferenceConcentration
units::quantity< unit::concentration > GetReferenceConcentration()
units::quantity< unit::length > mCellPopulationReferenceLength