OPAL (Object Oriented Parallel Accelerator Library)  2.2.0
OPAL
rk.cpp
Go to the documentation of this file.
1 /* rk.C
2 
3  Runge-Kutta with adaptive stepsize control
4  NUMERICAL RECIPES IN C: THE ART OF SCIENTIFIC COMPUTING (ISBN 0-521-43108-5)
5 
6  Project: Beam Envelope Tracker (BET)
7 
8  Revision history
9  Date Description Programmer
10  ------------ -------------------------------------------- --------------
11  07-03-06 Created Rene Bakker
12 
13  Last Revision:
14  $Id: rk.C 29 2007-04-14 17:03:18Z l_bakker $
15 */
16 
17 #include <stdlib.h>
18 #include <stddef.h>
19 #include <math.h>
20 
22 #include "Algorithms/bet/math/rk.h"
23 
24 /* Internal functions
25  ========================================================================= */
26 static double maxarg1, maxarg2;
27 #define FMAX(a,b) (maxarg1=(a),maxarg2=(b),(maxarg1) > (maxarg2) ?\
28  (maxarg1) : (maxarg2))
29 
30 /* vector
31  allocate an array of doubles [0..n-1] and check memory */
32 static double *vector(int n) {
33  double *b;
34 
35  b = (double *) malloc(sizeof(double) * n);
36  if(!b) {
37  //writeBetError(1,errModeAll,errFatal,"Insufficient memory malloc %d bytes (rk.C)",sizeof(double)*n);
38  writeBetError("Insufficient memory malloc in rk.C");
39  }
40  return b;
41 } /* vector */
42 
43 static double **matrix(int n1, int n2)
44 /* allocate a double matrix with subscript range [0..n1-1][0..n2-1] */
45 {
46  int i;
47  double **m;
48 
49  /* allocate pointers to rows */
50  m = (double **) malloc(sizeof(double *) * (n1 + 1));
51  if(!m) {
52  // writeBetError(1,errModeAll,errFatal,"Allocation failure1 matrix(%d,%d) in rk.C\n",n1,n2);
53  writeBetError("Allocation failure in matrix rk.C");
54  }
55 
56  for(i = 0; i < n1; i++) {
57  m[i] = (double *) malloc(sizeof(double) * n2);
58  if(!m[i]) {
59  // writeBetError(1,errModeAll,errFatal,"Allocation failure2 matrix(%d,%d) in rk.C at i=%d\n",n1,n2,i);
60  writeBetError("allocation failure in matrix rk.C");
61  }
62  }
63  m[n1] = NULL;
64  return m;
65 } /* matrix */
66 
67 static void free_matrix(double **m)
68 /* free a double matrix allocated by matrix() */
69 {
70  int i = 0;
71 
72  while(m[i] != NULL) {
73  free(m[i++]);
74  }
75  free(m);
76 } /* free_matrix */
77 
78 
79 /* Global variables + init for odeint
80  ================================== */
81 static int kmax = 0; // do not store intermediate results for inspection
82 static int kount;
83 static double
84 *xp = NULL, // must only be set if kmax > 0
85  **yp = NULL, // must only be set if kmax > 0
86  dxsav;
87 
88 static void rkSetBuffer(int nvar) {
89  if(nvar < 1) {
90  //writeBetError(1,errModeAll,errFatal,"odeint nvar (%d) < 1 (impossible)",nvar);
91  writeBetError("odeint nvar < 1");
92  }
93  if(kmax > 0) {
94  if(xp) free(xp);
95  xp = vector(kmax);
96 
97  if(yp) free_matrix(yp);
98  yp = matrix(nvar, kmax);
99  }
100 } /* rkSetBuffer */
101 
102 /* rkck
103  Given values for n variables y[0..n-1] and their derivatives dydx[0..n-1]
104  known at x, use the fifth-order Cash-Karp Runge-Kutta method
105  to advance the solution over an interval h and return the incremented
106  variables as yout[0..n-1].
107 
108  Also return an estimate of the local truncation error in yout
109  using the embedded fourth-order method.
110 
111  The user supplies the routine derivs(x,y,dydx),
112  which returns derivatives dydx at x.
113 */
114 void rkck
115 (
116  double y[],
117  double dydx[],
118  int n,
119  double x,
120  double h,
121  double yout[],
122  double yerr[],
123  void (*derivs)(double, double [], double [])) {
124  int i;
125  static double a2 = 0.2, a3 = 0.3, a4 = 0.6, a5 = 1.0, a6 = 0.875, b21 = 0.2,
126  b31 = 3.0 / 40.0, b32 = 9.0 / 40.0, b41 = 0.3, b42 = -0.9, b43 = 1.2,
127  b51 = -11.0 / 54.0, b52 = 2.5, b53 = -70.0 / 27.0, b54 = 35.0 / 27.0,
128  b61 = 1631.0 / 55296.0, b62 = 175.0 / 512.0, b63 = 575.0 / 13824.0,
129  b64 = 44275.0 / 110592.0, b65 = 253.0 / 4096.0, c1 = 37.0 / 378.0,
130  c3 = 250.0 / 621.0, c4 = 125.0 / 594.0, c6 = 512.0 / 1771.0,
131  dc5 = -277.0 / 14336.0;
132  double dc1 = c1 - 2825.0 / 27648.0, dc3 = c3 - 18575.0 / 48384.0,
133  dc4 = c4 - 13525.0 / 55296.0, dc6 = c6 - 0.25;
134  double *ak2, *ak3, *ak4, *ak5, *ak6, *ytemp;
135 
136  ak2 = vector(n);
137  ak3 = vector(n);
138  ak4 = vector(n);
139  ak5 = vector(n);
140  ak6 = vector(n);
141  ytemp = vector(n);
142 
143  for(i = 0; i < n; i++)
144  ytemp[i] = y[i] + b21 * h * dydx[i];
145  (*derivs)(x + a2 * h, ytemp, ak2);
146  for(i = 0; i < n; i++)
147  ytemp[i] = y[i] + h * (b31 * dydx[i] + b32 * ak2[i]);
148  (*derivs)(x + a3 * h, ytemp, ak3);
149  for(i = 0; i < n; i++)
150  ytemp[i] = y[i] + h * (b41 * dydx[i] + b42 * ak2[i] + b43 * ak3[i]);
151  (*derivs)(x + a4 * h, ytemp, ak4);
152  for(i = 0; i < n; i++)
153  ytemp[i] = y[i] + h * (b51 * dydx[i] + b52 * ak2[i] + b53 * ak3[i] + b54 * ak4[i]);
154  (*derivs)(x + a5 * h, ytemp, ak5);
155  for(i = 0; i < n; i++)
156  ytemp[i] = y[i] + h * (b61 * dydx[i] + b62 * ak2[i] + b63 * ak3[i] + b64 * ak4[i] + b65 * ak5[i]);
157  (*derivs)(x + a6 * h, ytemp, ak6);
158  for(i = 0; i < n; i++)
159  yout[i] = y[i] + h * (c1 * dydx[i] + c3 * ak3[i] + c4 * ak4[i] + c6 * ak6[i]);
160  for(i = 0; i < n; i++)
161  yerr[i] = h * (dc1 * dydx[i] + dc3 * ak3[i] + dc4 * ak4[i] + dc5 * ak5[i] + dc6 * ak6[i]);
162 
163  free(ytemp);
164  free(ak6);
165  free(ak5);
166  free(ak4);
167  free(ak3);
168  free(ak2);
169 } /* rkck */
170 
171 
172 #define SAFETY 0.9
173 #define PGROW -0.2
174 #define PSHRNK -0.25
175 #define ERRCON 1.89e-4
176 // The value ERRCON equals (5/SAFETY) raised to the power (1/PGROW), see use below.
177 
178 /* rkqs
179  Fifth-order Runge-Kutta step with monitoring of local truncation error
180  to ensure accuracy and adjust stepsize.
181  Input are:
182  - the dependent variable vector y[0..n-1]
183  - its derivative dydx[0..n-1]
184  at the starting value of the independent variable x.
185 
186  Also input are:
187  - the stepsize to be attempted htry
188  - the required accuracy eps
189  - the vector yscal[0..n-1] against which the error is scaled.
190 
191  RETURNS 1 on error and 0 otherwise
192 
193  On output, y and x are replaced by their new values,
194  hdid is the stepsize that was actually accomplished,
195  and hnext is the estimated next stepsize.
196 
197  derivs is the user-supplied routine that computes the
198  right-hand side derivatives.
199 */
200 
201 static int rkqs
202 (
203  double y[], double dydx[], int n, double *x, double htry, double eps,
204  double yscal[], double *hdid, double *hnext,
205  void (*derivs)(double, double [], double [])) {
206  int i, nLoop;
207  double errmax, h, xnew, *yerr, *ytemp;
208 
209  yerr = vector(n);
210  ytemp = vector(n);
211  h = htry;
212  nLoop = 0;
213  for(;;) {
214  ++nLoop;
215  rkck(y, dydx, n, *x, h, ytemp, yerr, derivs);
216  errmax = 0.0;
217  for(i = 0; i < n; i++) errmax = FMAX(errmax, fabs(yerr[i] / yscal[i]));
218  errmax /= eps;
219  if(errmax > 1.0) {
220  h = SAFETY * h * pow(errmax, PSHRNK);
221  if(h < 0.1 * h) h *= 0.1;
222  xnew = (*x) + h;
223  if(xnew == *x) {
224  //writeBetError(1,errModeAll,errWarning,
225  // "stepsize underflow in rkqs (rk.C) h = %le, n = %d",
226  // h,nLoop);
227  writeBetError("stepsize underflow rkgs");
228  free(ytemp);
229  free(yerr);
230  return 1;
231  }
232  continue;
233  } else {
234  if(errmax > ERRCON) *hnext = SAFETY * h * pow(errmax, PGROW);
235  else *hnext = 5.0 * h;
236  *x += (*hdid = h);
237  for(i = 0; i < n; i++) y[i] = ytemp[i];
238  break;
239  }
240  }
241  free(ytemp);
242  free(yerr);
243  return 0;
244 } /* rkqs */
245 
246 #undef SAFETY
247 #undef PGROW
248 #undef PSHRNK
249 #undef ERRCON
250 #undef NRANSI
251 
252 #undef FMAX
253 
254 /* External functions
255  ========================================================================= */
256 
257 /* rk4()
258  Given values for the variables y[0..n-1] use the fourth-order Runge-Kutta
259  method to advance the solution over an interval h and return the
260  incremented variables in y[0..n-1]. The user supplies the routine
261  derivs(x,y,dydx), which returns derivatives dydx at x.t
262 */
263 void rk4(double y[], int n, double x, double h,
264  void (*derivs)(double, double [], double [])) {
265  int i;
266  double xh, hh, h6, *dydx, *dym, *dyt, *yt;
267 
268  dydx = vector(n);
269  dym = vector(n);
270  dyt = vector(n);
271  yt = vector(n);
272 
273  (*derivs)(x, y, dydx);
274  hh = h * 0.5;
275  h6 = h / 6.0;
276  xh = x + hh;
277  for(i = 0; i < n; i++) yt[i] = y[i] + hh * dydx[i];
278  (*derivs)(xh, yt, dyt);
279  for(i = 0; i < n; i++) yt[i] = y[i] + hh * dyt[i];
280  (*derivs)(xh, yt, dym);
281  for(i = 0; i < n; i++) {
282  yt[i] = y[i] + h * dym[i];
283  dym[i] += dyt[i];
284  }
285  (*derivs)(x + h, yt, dyt);
286  for(i = 0; i < n; i++)
287  y[i] += h6 * (dydx[i] + dyt[i] + 2.0 * dym[i]);
288  free(yt);
289  free(dyt);
290  free(dym);
291  free(dydx);
292 } /* rk4() */
293 
294 
295 #define MAXSTP 10000
296 #define TINY 1.0e-30
297 
298 #define SIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
299 
300 
301 /* odeint
302  Runge-Kutta driver with adaptive stepsize control.
303 
304  Integrate starting values ystart[0..nvar-1]
305  from x1 to x2 with accuracy eps,
306 
307  storing intermediate results in global variables if kmax > 0
308  (see above).
309  h1 should be set as a guessed first stepsize,
310  hmin as the minimum allowed stepsize (can be zero).
311 
312  On output:
313  - nok and nbad are the number of good and bad (but retried and fixed)
314  steps taken
315  - ystart is replaced by values at the end of the integration interval.
316  - RETURNS 0 on success and 1 on error
317 
318  derivs is the user-supplied routine for calculating the right-hand side
319  derivative.
320 
321  rkqs is the name of the stepper routine (see above).
322 */
323 int odeint(
324  double ystart[], // initial condition
325  int nvar, // number of equations
326  double x1,
327  double x2,
328  double eps,
329  double h1,
330  double hmin,
331  int *nok,
332  int *nbad,
333  void (*derivs)(double, double [], double [])) {
334  int nstp, i;
335  double xsav = 0.0, x, hnext, hdid, h;
336  double *yscal, *y, *dydx;
337 
338  if(kmax > 0) { // set buffer
339  rkSetBuffer(nvar);
340  }
341 
342  yscal = vector(nvar);
343  y = vector(nvar);
344  dydx = vector(nvar);
345  x = x1;
346  h = SIGN(h1, x2 - x1);
347  *nok = (*nbad) = kount = 0;
348  for(i = 0; i < nvar; i++) y[i] = ystart[i];
349  if(kmax > 0) xsav = x - dxsav * 2.0;
350  for(nstp = 0; nstp < MAXSTP; nstp++) {
351  (*derivs)(x, y, dydx);
352  for(i = 0; i < nvar; i++) {
353  yscal[i] = fabs(y[i]) + fabs(dydx[i] * h) + TINY;
354  }
355  if(kmax > 0 && kount < kmax - 1 && fabs(x - xsav) > fabs(dxsav)) {
356  xp[++kount] = x;
357  for(i = 0; i < nvar; i++) yp[i][kount] = y[i];
358  xsav = x;
359  }
360  if((x + h - x2) * (x + h - x1) > 0.0) h = x2 - x;
361  if(rkqs(y, dydx, nvar, &x, h, eps, yscal, &hdid, &hnext, derivs) == 1) {
362  // error message already written
363  free(dydx);
364  free(y);
365  free(yscal);
366  return 1;
367  }
368  if(hdid == h) ++(*nok);
369  else ++(*nbad);
370  if((x - x2) * (x2 - x1) >= 0.0) {
371  for(i = 0; i < nvar; i++) ystart[i] = y[i];
372  if(kmax) {
373  xp[++kount] = x;
374  for(i = 0; i < nvar; i++) yp[i][kount] = y[i];
375  }
376  free(dydx);
377  free(y);
378  free(yscal);
379  return 0;
380  }
381  if(fabs(hnext) <= hmin) {
382  // writeBetError(1,errModeAll,errWarning,"Step size too small in odeint (%le < %le)",
383  // hnext,hmin);
384  writeBetError("Step size too small in odeint");
385  return 1;
386  }
387  h = hnext;
388  }
389  //writeBetError(1,errModeAll,errWarning,"Too many steps in routine odeint (%d)",MAXSTP);
390  writeBetError("Too many steps in routine odeint");
391  return 1;
392 } /* odeint */
393 
394 
396  if(max < 1) {
397  if(xp) free(xp);
398  xp = NULL;
399  if(yp) free_matrix(yp);
400  yp = NULL;
401 
402  kmax = 0;
403  } else {
404  kmax = max;
405  }
406 } /* setActiveBuffer */
407 
408 void rkPrintBuffer(FILE *f) {
409  int i, j;
410 
411  fprintf(f, "odeint output buffer, kmax = %d/%d\n", kount, kmax);
412 
413  fprintf(f, "%-4s %-10s", "k", "xp");
414  j = 0;
415  while(yp[j]) fprintf(f, " [%2d] ", j++);
416  fprintf(f, "\n");
417 
418  for(i = 0; i < kount; i++) {
419  fprintf(f, "%4d %10.4f", i, xp[i]);
420  j = 0;
421  while(yp[j]) fprintf(f, " %10.4f", yp[j++][i]);
422  fprintf(f, "\n");
423  }
424 } /* rkPrintBuffer */
425 
426 #undef MAXSTP
427 #undef TINY
428 #undef NRANSI
429 
430 #undef SIGN
431 
432 
void writeBetError(std::string err)
Definition: BetError.cpp:36
#define PGROW
Definition: rk.cpp:173
#define SIGN(a, b)
Definition: rk.cpp:298
PETE_TUTree< FnFabs, typename T::PETE_Expr_t > fabs(const PETE_Expr< T > &l)
Definition: PETE.h:815
T::PETE_Expr_t::PETE_Return_t max(const PETE_Expr< T > &expr, NDIndex< D > &loc)
Definition: ReductionLoc.h:123
void rkck(double y[], double dydx[], int n, double x, double h, double yout[], double yerr[], void(*derivs)(double, double[], double[]))
Definition: rk.cpp:115
#define ERRCON
Definition: rk.cpp:175
#define SAFETY
Definition: rk.cpp:172
void rk4(double y[], int n, double x, double h, void(*derivs)(double, double[], double[]))
Definition: rk.cpp:263
void rkActivateBuffer(int max)
Definition: rk.cpp:395
#define MAXSTP
Definition: rk.cpp:295
#define FMAX(a, b)
Definition: rk.cpp:27
Tps< T > pow(const Tps< T > &x, int y)
Integer power.
Definition: TpsMath.h:76
void rkPrintBuffer(FILE *f)
Definition: rk.cpp:408
int odeint(double ystart[], int nvar, double x1, double x2, double eps, double h1, double hmin, int *nok, int *nbad, void(*derivs)(double, double[], double[]))
Definition: rk.cpp:323
#define TINY
Definition: rk.cpp:296
#define PSHRNK
Definition: rk.cpp:174