Chaste  Release::2017.1
BackwardEulerIvpOdeSolver.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 
37 #include "BackwardEulerIvpOdeSolver.hpp"
38 #include <cmath>
39 
41  double timeStep,
42  double time,
43  std::vector<double>& rCurrentYValues,
44  std::vector<double>& rCurrentGuess)
45 {
46  std::vector<double> dy(mSizeOfOdeSystem); //For JC to optimize
47  pAbstractOdeSystem->EvaluateYDerivatives(time+timeStep, rCurrentGuess, dy);
48  for (unsigned i=0; i<mSizeOfOdeSystem; i++)
49  {
50  mResidual[i] = rCurrentGuess[i] - timeStep * dy[i] - rCurrentYValues[i];
51  }
52 }
53 
55  double timeStep,
56  double time,
57  std::vector<double>& rCurrentYValues,
58  std::vector<double>& rCurrentGuess)
59 {
60  for (unsigned i=0; i<mSizeOfOdeSystem; i++)
61  {
62  for (unsigned j=0; j<mSizeOfOdeSystem; j++)
63  {
64  mJacobian[i][j] = 0.0;
65  }
66  }
67 
68  if (pAbstractOdeSystem->GetUseAnalyticJacobian() && !mForceUseOfNumericalJacobian)
69  {
70  // The ODE system has an analytic jacobian, so use that
72  = static_cast<AbstractOdeSystemWithAnalyticJacobian*>(pAbstractOdeSystem);
73  p_ode_system->AnalyticJacobian(rCurrentGuess, mJacobian, time, timeStep);
74  }
75  else
76  {
77  ComputeNumericalJacobian(pAbstractOdeSystem,
78  timeStep,
79  time,
80  rCurrentYValues,
81  rCurrentGuess);
82  }
83 }
84 
86 {
87  double fact;
88  for (unsigned i=0; i<mSizeOfOdeSystem; i++)
89  {
90  for (unsigned ii=i+1; ii<mSizeOfOdeSystem; ii++)
91  {
92  fact = mJacobian[ii][i]/mJacobian[i][i];
93  for (unsigned j=i; j<mSizeOfOdeSystem; j++)
94  {
95  mJacobian[ii][j] -= fact*mJacobian[i][j];
96  }
97  mResidual[ii] -= fact*mResidual[i];
98  }
99  }
100  // This needs to int, since a downloop in unsigned won't terminate properly
101  for (int i=mSizeOfOdeSystem-1; i>=0; i--)
102  {
103  mUpdate[i] = mResidual[i];
104  for (unsigned j=i+1; j<mSizeOfOdeSystem; j++)
105  {
106  mUpdate[i] -= mJacobian[i][j]*mUpdate[j];
107  }
108  mUpdate[i] /= mJacobian[i][i];
109  }
110 }
111 
113 {
114  double norm = 0.0;
115  for (unsigned i=0; i<mSizeOfOdeSystem; i++)
116  {
117  if (fabs(pVector[i]) > norm)
118  {
119  norm = fabs(pVector[i]);
120  }
121  }
122  return norm;
123 }
124 
126  double timeStep,
127  double time,
128  std::vector<double>& rCurrentYValues,
129  std::vector<double>& rCurrentGuess)
130 {
131  std::vector<double> residual(mSizeOfOdeSystem);
132  std::vector<double> residual_perturbed(mSizeOfOdeSystem);
133  std::vector<double> guess_perturbed(mSizeOfOdeSystem);
134 
135  double epsilon = mNumericalJacobianEpsilon;
136 
137  ComputeResidual(pAbstractOdeSystem, timeStep, time, rCurrentYValues, rCurrentGuess);
138  for (unsigned i=0; i<mSizeOfOdeSystem; i++)
139  {
140  residual[i] = mResidual[i];
141  }
142 
143  for (unsigned global_column=0; global_column<mSizeOfOdeSystem; global_column++)
144  {
145  for (unsigned i=0; i<mSizeOfOdeSystem; i++)
146  {
147  guess_perturbed[i] = rCurrentGuess[i];
148  }
149 
150  guess_perturbed[global_column] += epsilon;
151 
152  ComputeResidual(pAbstractOdeSystem, timeStep, time, rCurrentYValues, guess_perturbed);
153  for (unsigned i=0; i<mSizeOfOdeSystem; i++)
154  {
155  residual_perturbed[i] = mResidual[i];
156  }
157 
158  // Compute residual_perturbed - residual
159  double one_over_eps = 1.0/epsilon;
160  for (unsigned i=0; i<mSizeOfOdeSystem; i++)
161  {
162  mJacobian[i][global_column] = one_over_eps*(residual_perturbed[i] - residual[i]);
163  }
164  }
165 }
166 
168  double timeStep,
169  double time,
170  std::vector<double>& rCurrentYValues,
171  std::vector<double>& rNextYValues)
172 {
173  // Check the size of the ODE system matches the solvers expected
174  assert(mSizeOfOdeSystem == pAbstractOdeSystem->GetNumberOfStateVariables());
175 
176  unsigned counter = 0;
177 // const double eps = 1e-6 * rCurrentGuess[0]; // Our tolerance (should use min(guess) perhaps?)
178  const double eps = 1e-6; // JonW tolerance
179  double norm = 2*eps;
180 
181  std::vector<double> current_guess(mSizeOfOdeSystem);
182  current_guess.assign(rCurrentYValues.begin(), rCurrentYValues.end());
183 
184  while (norm > eps)
185  {
186  // Calculate Jacobian and mResidual for current guess
187  ComputeResidual(pAbstractOdeSystem, timeStep, time, rCurrentYValues, current_guess);
188  ComputeJacobian(pAbstractOdeSystem, timeStep, time, rCurrentYValues, current_guess);
189 // // Update norm (our style)
190 // norm = ComputeNorm(mResidual);
191 
192  // Solve Newton linear system
194 
195  // Update norm (JonW style)
196  norm = ComputeNorm(mUpdate);
197 
198  // Update current guess
199  for (unsigned i=0; i<mSizeOfOdeSystem; i++)
200  {
201  current_guess[i] -= mUpdate[i];
202  }
203 
204  counter++;
205  assert(counter < 20); // avoid infinite loops
206  }
207  rNextYValues.assign(current_guess.begin(), current_guess.end());
208 }
209 
211 {
212  mSizeOfOdeSystem = sizeOfOdeSystem;
213 
214  // default epsilon
217 
218  // allocate memory
219  mResidual = new double[mSizeOfOdeSystem];
220  mUpdate = new double[mSizeOfOdeSystem];
221 
222  mJacobian = new double*[mSizeOfOdeSystem];
223  for (unsigned i=0; i<mSizeOfOdeSystem; i++)
224  {
225  mJacobian[i] = new double[mSizeOfOdeSystem];
226  }
227 }
228 
230 {
231  // Delete pointers
232  delete[] mResidual;
233  delete[] mUpdate;
234 
235  for (unsigned i=0; i<mSizeOfOdeSystem; i++)
236  {
237  delete[] mJacobian[i];
238  }
239  delete[] mJacobian;
240 }
241 
243 {
244  assert(epsilon > 0);
245  mNumericalJacobianEpsilon = epsilon;
246 }
247 
249 {
251 }
252 
253 
254 // Serialization for Boost >= 1.36
void SetEpsilonForNumericalJacobian(double epsilon)
BackwardEulerIvpOdeSolver(unsigned sizeOfOdeSystem)
virtual void EvaluateYDerivatives(double time, const std::vector< double > &rY, std::vector< double > &rDY)=0
void ComputeJacobian(AbstractOdeSystem *pAbstractOdeSystem, double timeStep, double time, std::vector< double > &rCurrentYValues, std::vector< double > &rCurrentGuess)
virtual void AnalyticJacobian(const std::vector< double > &rSolutionGuess, double **jacobian, double time, double timeStep)=0
void ComputeNumericalJacobian(AbstractOdeSystem *pAbstractOdeSystem, double timeStep, double time, std::vector< double > &rCurrentYValues, std::vector< double > &rCurrentGuess)
#define CHASTE_CLASS_EXPORT(T)
void ComputeResidual(AbstractOdeSystem *pAbstractOdeSystem, double timeStep, double time, std::vector< double > &rCurrentYValues, std::vector< double > &rCurrentGuess)
void CalculateNextYValue(AbstractOdeSystem *pAbstractOdeSystem, double timeStep, double time, std::vector< double > &rCurrentYValues, std::vector< double > &rNextYValues)