Chaste Commit::9dfedaa0870fc7cfa8911a7ba21eba441bdba6b4
RungeKuttaFehlbergIvpOdeSolver.cpp
1/*
2
3Copyright (c) 2005-2026, University of Oxford.
4All rights reserved.
5
6University of Oxford means the Chancellor, Masters and Scholars of the
7University of Oxford, having an administrative office at Wellington
8Square, Oxford OX1 2JD, UK.
9
10This file is part of Chaste.
11
12Redistribution and use in source and binary forms, with or without
13modification, 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
23THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34*/
35
36#include "RungeKuttaFehlbergIvpOdeSolver.hpp"
37#include <algorithm>
38#include <cmath>
39#include <cfloat>
40#include <iostream>
41#include "Exception.hpp"
42
43/*
44 * PROTECTED FUNCTIONS =========================================================
45 */
46
48 AbstractOdeSystem* pOdeSystem,
49 std::vector<double>& rYValues,
50 std::vector<double>& rWorkingMemory,
51 double startTime,
52 double endTime,
53 double maxTimeStep,
54 double minTimeStep,
55 double tolerance,
56 bool outputSolution)
57{
58 const unsigned number_of_variables = pOdeSystem->GetNumberOfStateVariables();
59 mError.resize(number_of_variables);
60 mk1.resize(number_of_variables);
61 mk2.resize(number_of_variables);
62 mk3.resize(number_of_variables);
63 mk4.resize(number_of_variables);
64 mk5.resize(number_of_variables);
65 mk6.resize(number_of_variables);
66 myk2.resize(number_of_variables);
67 myk3.resize(number_of_variables);
68 myk4.resize(number_of_variables);
69 myk5.resize(number_of_variables);
70 myk6.resize(number_of_variables);
71
72 double current_time = startTime;
73 double time_step = maxTimeStep;
74 bool got_to_end = false;
75 bool accurate_enough = false;
76 unsigned number_of_time_steps = 0;
77
78 if (outputSolution)
79 { // Write out ICs
80 rSolution.rGetTimes().push_back(current_time);
81 rSolution.rGetSolutions().push_back(rYValues);
82 }
83
84 // should never get here if this bool has been set to true;
86 while (!got_to_end)
87 {
88 //std::cout << "New timestep\n" << std::flush;
89 while (!accurate_enough)
90 {
91 accurate_enough = true; // assume it is OK until we check and find otherwise
92
93 // Function that calls the appropriate one-step solver
94 CalculateNextYValue(pOdeSystem,
95 time_step,
96 current_time,
97 rYValues,
98 rWorkingMemory);
99
100 // Find the maximum error in this vector
101 double max_error = *std::max_element(mError.begin(), mError.end());
102
103 if (max_error > tolerance)
104 {// Reject the step-size and do it again.
105 accurate_enough = false;
106 //std::cout << "Approximation rejected\n" << std::flush;
107 }
108 else
109 {
110 // step forward the time since step has now been made
111 current_time = current_time + time_step;
112 //std::cout << "Approximation accepted with time step = "<< time_step << "\n" << std::flush;
113 //std::cout << "max_error = " << max_error << " tolerance = " << tolerance << "\n" << std::flush;
114 if (outputSolution)
115 { // Write out ICs
116 //std::cout << "In solver Time = " << current_time << " y = " << rWorkingMemory[0] << "\n" << std::flush;
117 rSolution.rGetTimes().push_back(current_time);
118 rSolution.rGetSolutions().push_back(rWorkingMemory);
119 number_of_time_steps++;
120 }
121 }
122
123 // Set a new step size based on the accuracy here
124 AdjustStepSize(time_step, max_error, tolerance, maxTimeStep, minTimeStep);
125 }
126
127 // For the next timestep check the step doesn't go past the end...
128 if (current_time + time_step > endTime)
129 { // Allow a smaller timestep for the final step.
130 time_step = endTime - current_time;
131 }
132
133 if (pOdeSystem->CalculateStoppingEvent(current_time,
134 rWorkingMemory) == true)
135 {
136 mStoppingTime = current_time;
138 }
139
140 if (mStoppingEventOccurred || current_time>=endTime)
141 {
142 got_to_end = true;
143 }
144
145 // Approximation accepted - put it in rYValues
146 rYValues.assign(rWorkingMemory.begin(), rWorkingMemory.end());
147 accurate_enough = false; // for next loop.
148 //std::cout << "Finished Time Step\n" << std::flush;
149 }
150 rSolution.SetNumberOfTimeSteps(number_of_time_steps);
151}
152
154 double timeStep,
155 double time,
156 std::vector<double>& rCurrentYValues,
157 std::vector<double>& rNextYValues)
158{
159 const unsigned num_equations = pAbstractOdeSystem->GetNumberOfStateVariables();
160
161
162 std::vector<double>& dy = rNextYValues; // re-use memory (not that it makes much difference here!)
163
164 pAbstractOdeSystem->EvaluateYDerivatives(time, rCurrentYValues, dy);
165
166 for (unsigned i=0; i<num_equations; i++)
167 {
168 mk1[i] = timeStep*dy[i];
169 myk2[i] = rCurrentYValues[i] + 0.25*mk1[i];
170 }
171
172 pAbstractOdeSystem->EvaluateYDerivatives(time + 0.25*timeStep, myk2, dy);
173 for (unsigned i=0; i<num_equations; i++)
174 {
175 mk2[i] = timeStep*dy[i];
176 myk3[i] = rCurrentYValues[i] + 0.09375*mk1[i] + 0.28125*mk2[i];
177 }
178
179 pAbstractOdeSystem->EvaluateYDerivatives(time + 0.375*timeStep, myk3, dy);
180 for (unsigned i=0; i<num_equations; i++)
181 {
182 mk3[i] = timeStep*dy[i];
183 myk4[i] = rCurrentYValues[i] + m1932o2197*mk1[i] - m7200o2197*mk2[i]
184 + m7296o2197*mk3[i];
185 }
186
187 pAbstractOdeSystem->EvaluateYDerivatives(time+m12o13*timeStep, myk4, dy);
188 for (unsigned i=0; i<num_equations; i++)
189 {
190 mk4[i] = timeStep*dy[i];
191 myk5[i] = rCurrentYValues[i] + m439o216*mk1[i] - 8*mk2[i]
192 + m3680o513*mk3[i]- m845o4104*mk4[i];
193 }
194
195 pAbstractOdeSystem->EvaluateYDerivatives(time+timeStep, myk5, dy);
196 for (unsigned i=0; i<num_equations; i++)
197 {
198 mk5[i] = timeStep*dy[i];
199 myk6[i] = rCurrentYValues[i] - m8o27*mk1[i] + 2*mk2[i] - m3544o2565*mk3[i]
200 + m1859o4104*mk4[i] - 0.275*mk5[i];
201 }
202
203 pAbstractOdeSystem->EvaluateYDerivatives(time+0.5*timeStep, myk6, dy);
204 for (unsigned i=0; i<num_equations; i++)
205 {
206 mk6[i] = timeStep*dy[i];
207 mError[i] = (1/timeStep)*fabs(m1o360*mk1[i] - m128o4275*mk3[i]
208 - m2197o75240*mk4[i] + 0.02*mk5[i]+ m2o55*mk6[i]);
209 rNextYValues[i] = rCurrentYValues[i] + m25o216*mk1[i] + m1408o2565*mk3[i]
210 + m2197o4104*mk4[i] - 0.2*mk5[i];
211 }
212}
213
215 const double& rError,
216 const double& rTolerance,
217 const double& rMaxTimeStep,
218 const double& rMinTimeStep)
219{
220 // Work out scaling factor delta for the step size
221 double delta = pow(rTolerance/(2.0*rError), 0.25);
222
223 // Maximum adjustment is *0.1 or *4
224 if (delta <= 0.1)
225 {
226 rCurrentStepSize *= 0.1;
227 }
228 else if (delta >= 4.0)
229 {
230 rCurrentStepSize *= 4.0;
231 }
232 else
233 {
234 rCurrentStepSize *= delta;
235 }
236
237 if (rCurrentStepSize > rMaxTimeStep)
238 {
239 rCurrentStepSize = rMaxTimeStep;
240 }
241
242 if (rCurrentStepSize < rMinTimeStep)
243 {
244 std::cout << "rCurrentStepSize = " << rCurrentStepSize << "\n" << std::flush;
245 std::cout << "rMinTimeStep = " << rMinTimeStep << "\n" << std::flush;
246
247 EXCEPTION("RKF45 Solver: Ode needs a smaller timestep than the set minimum\n");
248 }
249
250}
251
252
253/*
254 * PUBLIC FUNCTIONS=============================================================
255 */
256
258 : m1932o2197(1932.0/2197.0), // you need the .0 s - caused me no end of trouble.
259 m7200o2197(7200.0/2197.0),
260 m7296o2197(7296.0/2197.0),
261 m12o13(12.0/13.0),
262 m439o216(439.0/216.0),
263 m3680o513(3680.0/513.0),
264 m845o4104(845.0/4104.0),
265 m8o27(8.0/27.0),
266 m3544o2565(3544.0/2565.0),
267 m1859o4104(1859.0/4104.0),
268 m1o360(1.0/360.0),
269 m128o4275(128.0/4275.0),
270 m2197o75240(2197.0/75240.0),
271 m2o55(2.0/55.0),
272 m25o216(25.0/216.0),
273 m1408o2565(1408.0/2565.0),
274 m2197o4104(2197.0/4104.0)
275{
276}
277
279 std::vector<double>& rYValues,
280 double startTime,
281 double endTime,
282 double timeStep,
283 double tolerance)
284{
285 assert(rYValues.size()==pOdeSystem->GetNumberOfStateVariables());
286 assert(endTime > startTime);
287 assert(timeStep > 0.0);
288
290 if (pOdeSystem->CalculateStoppingEvent(startTime, rYValues) == true)
291 {
292 EXCEPTION("(Solve with sampling) Stopping event is true for initial condition");
293 }
294 // Allocate working memory
295 std::vector<double> working_memory(rYValues.size());
296 // And solve...
297 OdeSolution solutions;
298 //solutions.SetNumberOfTimeSteps((unsigned)(10.0*(startTime-endTime)/timeStep));
299 bool return_solution = true;
300 InternalSolve(solutions, pOdeSystem, rYValues, working_memory, startTime, endTime, timeStep, 1e-5, tolerance, return_solution);
301 return solutions;
302}
303
305 std::vector<double>& rYValues,
306 double startTime,
307 double endTime,
308 double timeStep)
309{
310 assert(rYValues.size()==pOdeSystem->GetNumberOfStateVariables());
311 assert(endTime > startTime);
312 assert(timeStep > 0.0);
313
315 if (pOdeSystem->CalculateStoppingEvent(startTime, rYValues) == true)
316 {
317 EXCEPTION("(Solve without sampling) Stopping event is true for initial condition");
318 }
319 // Allocate working memory
320 std::vector<double> working_memory(rYValues.size());
321 // And solve...
322 OdeSolution not_required_solution;
323 bool return_solution = false;
324 InternalSolve(not_required_solution, pOdeSystem, rYValues, working_memory, startTime, endTime, timeStep, 1e-4, 1e-5, return_solution);
325}
326
327
328// Serialization for Boost >= 1.36
#define EXCEPTION(message)
#define CHASTE_CLASS_EXPORT(T)
virtual void EvaluateYDerivatives(double time, const std::vector< double > &rY, std::vector< double > &rDY)=0
virtual bool CalculateStoppingEvent(double time, const std::vector< double > &rY)
void SetNumberOfTimeSteps(unsigned numTimeSteps)
std::vector< std::vector< double > > & rGetSolutions()
std::vector< double > & rGetTimes()
void AdjustStepSize(double &rCurrentStepSize, const double &rError, const double &rTolerance, const double &rMaxTimeStep, const double &rMinTimeStep)
void InternalSolve(OdeSolution &rSolution, AbstractOdeSystem *pAbstractOdeSystem, std::vector< double > &rCurrentYValues, std::vector< double > &rWorkingMemory, double startTime, double endTime, double maxTimeStep, double minTimeStep, double tolerance, bool outputSolution)
OdeSolution Solve(AbstractOdeSystem *pAbstractOdeSystem, std::vector< double > &rYValues, double startTime, double endTime, double timeStep, double ignoredSamplingTime)
void CalculateNextYValue(AbstractOdeSystem *pAbstractOdeSystem, double timeStep, double time, std::vector< double > &rCurrentYValues, std::vector< double > &rNextYValues)