.Simulation
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
Solver.h
1 #ifndef __SOLVER_H__
2 #define __SOLVER_H__
3 
4 #include <algorithm>
5 #include <cassert>
6 #include <cmath>
7 #include <float.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 
11 namespace MDSimulation
12 {
13  #define MAX_ORDER 2
14 
15  using std::max;
16  using std::min;
17 
18  inline double MAX(const double a, const double b)
19  {
20  return a > b ? a : b;
21  }
22 
23  inline double MIN(const double a, const double b)
24  {
25  return a > b ? b : a;
26  }
27 
33  template <class Function>
34  class Solver
35  {
36  public:
37  // order - number of dependent variables.
38  // Starting values must be in the 0th elements of arrays x and y;
39  Solver(Function& f, int order, double* x, double* y);
40 
41  // void DerivativeFunc(double x, const double* y, double* dydx) is the external
42  // routine that computes the derivatives (stored in dydx) from x and y.
43  //
44  // xMax - final value for x;
45  // accuracy - computational acuracy;
46  // step - initial guess for the step size (by absolute value);
47  // steps - max number of points in arrays x and y.
48  // If steps = 0, no intermediate results will be stored and the starting
49  // values will be replaced with the final ones;
50  // minStep - smallest step size allowed (by absolute value);
51  // maxStep - largest step size allowed (by absolute value);
52  // xMin - minimum interval at which to record points.
53 
54  // double (*DerivativeFunc)(double, const double*, double*)
55  int RK4(double xMax, double accuracy,
56  double step, int steps, double minStep = 0,
57  double maxStep = DBL_MAX, double xMin = 0);
58 
59  // The return value for the last RK4 call
60  inline int GetSteps() const
61  {
62  return CurrentStep;
63  }
64 
65  // Returns suggested stepsize for the next iteration
66  inline double GetNextStep() const
67  {
68  return NextStep;
69  }
70 
71  // Returns the stepsize used for the last iteration
72  inline double GetLastStep() const
73  {
74  return LastStep;
75  }
76 
77  // Numerical error types
78  enum Error
79  {
80  STEPSIZE_UNDER_HMIN,
81  STEPSIZE_UNDERFLOW
82  };
83 
84  private:
85  //derp
86  int derp;
87 
88  // Number of independent variables
89  int Order;
90 
91  // Statistics
92  int GoodSteps;
93  int BadSteps;
94 
95  int CurrentStep;
96  // double (*Derivatives)(double, const double*, double*);
97  Function& Derivatives;
98 
99  // These variables are for communication with rkqs
100  double x;
101  double Accuracy;
102  double NextStep;
103  double LastStep;
104  double Step;
105 
106  double yscal[MAX_ORDER];
107 
108  // X is the storage for the independent variable's values;
109  // space must be allocated by the caller.
110  //
111  // Y is the storage for the dependent variable;
112  // space must be allocated by the caller.
113  double* y;
114  double* X;
115  double* Y;
116  double dydx[MAX_ORDER];
117 
118  // Output solution
119  double* yOut;
120 
121  // These variables are used by SingleStep
122  double yError[MAX_ORDER];
123  double ak2[MAX_ORDER];
124  double ak3[MAX_ORDER];
125  double ak4[MAX_ORDER];
126  double ak5[MAX_ORDER];
127  double ak6[MAX_ORDER];
128  double ytemp[MAX_ORDER];
129 
130  void SingleStep();
131  void rkqs();
132  };
133 
134  // Constructor
135  template <class Function>
136  Solver<Function>::Solver(Function& f, int order, double* x, double* y) :
137  Derivatives(f)
138  {
139  assert(order <= MAX_ORDER);
140  Order = order;
141 
142  X = x;
143  Y = y;
144  NextStep = 0;
145  derp = 0;
146  }
147 
148  // Single Runge-Kutta step
149  template <class Function>
150  void Solver<Function>::SingleStep()
151  {
152  // Debugging?
153  if (derp == 3000)
154  {
155  printf("derp = 3000\n");
156  }
157 
158  // Runge-Kutta constants
159  const double a2 = 0.2,
160  a3 = 0.3,
161  a4 = 0.6,
162  a5 = 1.0,
163  a6 = 0.875,
164 
165  b21 = 0.2,
166  b31 = 3.0/40.0,
167  b32 = 9.0/40.0,
168  b41 = 0.3,
169  b42 = -0.9,
170  b43 = 1.2,
171  b51 = -11.0/54.0,
172  b52 = 2.5,
173  b53 = -70.0/27.0,
174  b54 = 35.0/27.0,
175  b61 = 1631.0/55296.0,
176  b62 = 175.0/512.0,
177  b63 = 575.0/13824.0,
178  b64 = 44275.0/110592.0,
179  b65 = 253.0/4096.0,
180 
181  c1 = 37.0/378.0,
182  c3 = 250.0/621.0,
183  c4 = 125.0/594.0,
184  c6 = 512.0/1771.0,
185 
186  dc1 = c1 - 2825.0/27648.0,
187  dc3 = c3 - 18575.0/48384.0,
188  dc4 = c4 -13525.0/55296.0,
189  dc5 = -277.00/14336.0,
190  dc6=c6-0.25;
191 
192  double ytemp[MAX_ORDER];
193 
194  for ( int i = 0; i < Order; i++ )
195  { ytemp[i] = y[i] + b21*LastStep*dydx[i]; }
196 
197  Derivatives(x + a2*LastStep, ytemp, ak2);
198 
199  for ( int i = 0; i < Order; i++ )
200  { ytemp[i] = y[i] + LastStep*(b31*dydx[i] + b32*ak2[i]); }
201 
202  Derivatives(x + a3*LastStep, ytemp, ak3);
203 
204  for ( int i = 0; i < Order; i++ )
205  { ytemp[i] = y[i] + LastStep*(b41*dydx[i] + b42*ak2[i] + b43*ak3[i]); }
206 
207  Derivatives(x + a4*LastStep, ytemp, ak4);
208 
209  for ( int i = 0; i < Order; i++ )
210  { ytemp[i] = y[i] + LastStep*(b51*dydx[i] + b52*ak2[i] + b53*ak3[i] + b54*ak4[i]); }
211 
212  Derivatives(x + a5*LastStep, ytemp, ak5);
213 
214  for ( int i = 0; i < Order; i++ )
215  { ytemp[i] = y[i] + LastStep*(b61*dydx[i] + b62*ak2[i] + b63*ak3[i] + b64*ak4[i] + b65*ak5[i]); }
216 
217  Derivatives(x + a6*LastStep, ytemp, ak6);
218 
219  // Output solution
220  for ( int i = 0; i < Order; i++ )
221  { yOut[i] = y[i] + LastStep*(c1*dydx[i] + c3*ak3[i] + c4*ak4[i] + c6*ak6[i]); }
222 
223  // Error
224  for ( int i = 0; i < Order; i++ )
225  { yError[i] = LastStep*(dc1*dydx[i] + dc3*ak3[i] + dc4*ak4[i] + dc5*ak5[i] + dc6*ak6[i]); }
226  }
227 
228  template <class Function>
229  void Solver<Function>::rkqs()
230  {
231  derp++;
232  const double SAFETY = 0.9, PGROW = -0.2, PSHRNK = -0.25, ERRCON = 1.89E-4;
233  double maxError, tempStep, xnew;
234 
235  LastStep = Step;
236  for ( ;; )
237  {
238  SingleStep();
239  maxError = 0.0;
240 
241  for ( int i = 0; i < Order; i++ )
242  {
243  maxError = max(maxError, fabs(yError[i] / yscal[i]));
244  }
245 
246  maxError /= Accuracy;
247 
248  if ( maxError <= 1.0 ) { break; }
249 
250  tempStep = SAFETY*LastStep*pow(maxError,PSHRNK);
251  LastStep = (LastStep >= 0.0 ? max(tempStep, 0.1*LastStep)
252  : min(tempStep, 0.1*LastStep));
253 
254  xnew = x + LastStep;
255 
256  if (xnew == x)
257  {
258  throw Error(STEPSIZE_UNDER_HMIN);
259  }
260  }
261 
262  if ( maxError > ERRCON )
263  { NextStep = SAFETY*LastStep*pow(maxError, PGROW); }
264  else
265  { NextStep = 5.0*LastStep; }
266 
267  x += LastStep;
268  }
269 
270  template <class Function>
271  int Solver<Function>::RK4(double xMax, double accuracy, double step, int steps,
272  double minStep, double maxStep, double xMin)
273  {
274  const double TINY = 1E-30;
275 
276  Accuracy = accuracy;
277 
278  double xsav = x = X[0];
279 
280  y = Y;
281  double xStart = x;
282 
283  // Step up or step down?
284  Step = (xMax - x) > 0 ? step : -step;
285 
286  // Reset step counters
287  GoodSteps = BadSteps = 0;
288 
289  // Non-zero step?
290  if ( steps != 0 )
291  {
292  CurrentStep = 0;
293  // This is where we will write calculated data
294  yOut = y + Order;
295  }
296  else
297  {
298  // Do nothing if the step size is zero
299  CurrentStep = -1;
300  yOut = y;
301  }
302 
303  for ( ;; )
304  {
305  // Finished integrating because we are outside the interval? Should
306  // NEVER happen when we calculate RP because our interval is VERY
307  // large
308  bool isFinished = xMax > xStart ? (x >= xMax) : (x <= xMax);
309 
310  // Debugging?
311  if ( derp == 2228 )
312  {
313  printf("derp = 2228\n");
314  }
315 
316  if ( fabs(x - xsav) > xMin || isFinished )
317  {
318  yOut += Order;
319  X[++CurrentStep] = xsav = x;
320  if ( CurrentStep == steps || isFinished )
321  { return CurrentStep; }
322  }
323 
324  Derivatives(x, y, dydx);
325 
326  for ( int i = 0; i < Order; i++ )
327  {
328  yscal[i] = fabs(y[i]) + fabs(Step*dydx[i]) + TINY;
329  }
330 
331  double hleft = xMax - x;
332  if ( fabs(Step) > fabs(hleft) ) { Step = hleft; }
333 
334  rkqs();
335  y = yOut;
336 
337  // Keep track of good vs. bad steps
338  if ( LastStep == Step )
339  { GoodSteps++; }
340  else
341  { BadSteps++; }
342 
343  // Converging too slowly - step too small
344  if ( fabs(NextStep) <= minStep )
345  { throw Error(STEPSIZE_UNDER_HMIN); }
346 
347  // Determine next step
348  if ( NextStep > 0 )
349  { Step = MIN(NextStep, maxStep); }
350  else
351  { Step = MAX(NextStep, -maxStep); }
352  }
353  }
354 }
355 
356 #endif /* __SOLVER_H__ */
The Solver class is templatized by the function it will take.
Definition: Solver.h:34
const IntVector X(1, 0, 0)
Unit Vectors for each direction.