// rk4_adaptive.cpp /* Function to advance set of coupled first-order o.d.e.s by single step using adaptive fourth-order Runge-Kutta scheme x ... independent variable y ... array of dependent variables h ... step-length t_err ... actual truncation error per step acc ... desired truncation error per step S ... step-length cannot change by more than this factor from step to step rept ... number of step recalculations maxrept ... maximum allowable number of step recalculations h_min ... minimum allowable step-length h_max ... maximum allowable step-length flag ... controls manner in which truncation error is calculated Requires right-hand side routine void rhs_eval (double x, Array<double,1> y, Array<double,1>& dydx) which evaluates derivatives of y (w.r.t. x) in array dydx. Function advances equations by single step whilst attempting to maintain constant truncation error per step of acc: flag = 0 ... error is absolute flag = 1 ... error is relative flag = 2 ... error is mixed If step-length falls below h_min then routine aborts */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <blitz/array.h> using namespace blitz; void rk4_fixed (double&, Array<double,1>&, void (*)(double, Array<double,1>, Array<double,1>&), double); void rk4_adaptive (double& x, Array<double,1>& y, void (*rhs_eval)(double, Array<double,1>, Array<double,1>&), double& h, double& t_err, double acc, double S, int& rept, int maxrept, double h_min, double h_max, int flag) { // Array y assumed to be of extent n, where n is no. of coupled // equations int n = y.extent(0); // Declare local arrays Array<double,1> y0(n), y1(n); // Declare repetition counter static int count = 0; // Save initial data double x0 = x; y0 = y; // Take full step rk4_fixed (x, y, rhs_eval, h); // Save data y1 = y; // Restore initial data x = x0; y = y0; // Take two half-steps rk4_fixed (x, y, rhs_eval, h/2.); rk4_fixed (x, y, rhs_eval, h/2.); // Calculate truncation error t_err = 0.; double err, err1, err2; if (flag == 0) { // Use absolute truncation error for (int i = 0; i < n; i++) { err = fabs (y(i) - y1(i)); t_err = (err > t_err) ? err : t_err; } } else if (flag == 1) { // Use relative truncation error for (int i = 0; i < n; i++) { err = fabs ((y(i) - y1(i)) / y(i)); t_err = (err > t_err) ? err : t_err; } } else { // Use mixed truncation error for (int i = 0; i < n; i++) { err1 = fabs ((y(i) - y1(i)) / y(i)); err2 = fabs (y(i) - y1(i)); err = (err1 < err2) ? err1 : err2; t_err = (err > t_err) ? err : t_err; } } // Prevent small truncation error from rounding to zero if (t_err == 0.) t_err = 1.e-15; // Calculate new step-length double h_est = h * pow (fabs (acc / t_err), 0.2); // Prevent step-length from changing by more than factor S if (h_est / h > S) h *= S; else if (h_est / h < 1. / S) h /= S; else h = h_est; // Prevent step-length from exceeding h_max h = (fabs(h) > h_max) ? h_max * h / fabs(h) : h; // Abort if step-length falls below h_min if (fabs(h) < h_min) { printf ("Error - |h| < hmin\n"); exit (1); } // If truncation error acceptable take step if ((t_err <= acc) || (count >= maxrept)) { rept = count; count = 0; } // If truncation error unacceptable repeat step else { count++; x = x0; y = y0; rk4_adaptive (x, y, rhs_eval, h, t_err, acc, S, rept, maxrept, h_min, h_max, flag); } return; }