| | 86 | nr_rkck(double y[], double dydx[], int n, double x, double h, |
| | 87 | double yout[], double yerr[], |
| | 88 | void (*derivs)(double, double[], double[])) |
| | 89 | // Given value for n variables y[1..n] and their derivatives |
| | 90 | // dydx[1..n] known at x, use the fifth-order Cash-Karp Runge-Kutta |
| | 91 | // method to advance the solution over an interval h and return the |
| | 92 | // incremented variables as yout[1..n]. Also return an estimate of |
| | 93 | // the local truncation error in yout using the embedded fourth-order |
| | 94 | // method. The user supplies the routine derivs(x, y, dydx), which |
| | 95 | // returns derivatives dydx at x. |
| | 96 | { |
| | 97 | int i; |
| | 98 | static const double a2=0.2, a3=0.3, a4=0.6, a5=1.0, a6=0.875, b21=0.2, |
| | 99 | b31=3.0/40.0, b32=9.0/40.0, b41=0.3, b42=-0.9, b43=1.2, |
| | 100 | b51=-11.0/54.0, b52=2.5, b53=-70.0/27.0, b54=35.0/27.0, |
| | 101 | b61=1631.0/55296.0, b62=175.0/512.0, b63=575.0/13824.0, |
| | 102 | b64=44275.0/110592.0, b65=253.0/4096.0, c1=37.0/378.0, |
| | 103 | c3=250.0/621.0, c4=125.0/594.0, c6=512.0/1771.0, |
| | 104 | dc5 = -277.0/14336.0; |
| | 105 | static cdonst double dc1=c1-2825.0/27648.0, dc3=c3-18575.0/48384.0, |
| | 106 | dc4=c4-13525.0/55296.0, dc6=c6-0.25; |
| | 107 | double *ak2, *ak3, *ak4, *ak5, *ak6, *ytemp; |
| | 108 | |
| | 109 | ak2 = new double[n+1]; |
| | 110 | ak3 = new double[n+1]; |
| | 111 | ak4 = new double[n+1]; |
| | 112 | ak5 = new double[n+1]; |
| | 113 | ak6 = new double[n+1]; |
| | 114 | ytemp = new double[n+1]; |
| | 115 | |
| | 116 | for (i = 1; i <= n; i++) // First step. |
| | 117 | ytemp[i]=y[i]+b21*h*dydx[i]; |
| | 118 | |
| | 119 | (*derivs)(x+a2*h, ytemp, ak2); // Second step. |
| | 120 | for (i = 1; i <= n; i++) |
| | 121 | ytemp[i]=y[i]+h*(b31*dydx[i]+b32*ak2[i]); |
| | 122 | |
| | 123 | (*derivs)(x+a3*h, ytemp, ak3); // Third step. |
| | 124 | for (i = 1; i <= n; i++) |
| | 125 | ytemp[i]=y[i]+h*(b41*dydx[i]+b42*ak2[i]+b43*ak3[i]); |
| | 126 | |
| | 127 | (*derivs)(x+a4*h, ytemp, ak4); // Fourth step. |
| | 128 | for (i = 1; i <= n; i++) |
| | 129 | ytemp[i]=y[i]+h*(b51*dydx[i]+b52*ak2[i]+b53*ak3[i]+b54*ak4[i]); |
| | 130 | |
| | 131 | (*derivs)(x+a5*h, ytemp, ak5); // Fifth step. |
| | 132 | for (i = 1; i <= n; i++) |
| | 133 | ytemp[i]=y[i]+h*(b61*dydx[i]+b62*ak2[i]+b63*ak3[i]+b64*ak4[i]+b65*ak5[i]); |
| | 134 | |
| | 135 | (*derivs)(x+a6*h, ytemp, ak6); // Sixth step. |
| | 136 | for (i = 1; i <= n; i++) // Accumulate increments with proper weights. |
| | 137 | yout[i]=y[i]+h*(c1*dydx[i]+c3*ak3[i]+c4*ak4[i]+c6*ak6[i]); |
| | 138 | for (i = 1; i <= n; i++) |
| | 139 | // Estimate error as difference between fourth and fifth order methods. |
| | 140 | yerr[i]=h*(dc1*dydx[i]+dc3*ak3[i]+dc4*ak4[i]+dc5*ak5[i]+dc6*ak6[i]); |
| | 141 | |
| | 142 | delete[] ytemp; |
| | 143 | delete[] ak6; |
| | 144 | delete[] ak5; |
| | 145 | delete[] ak4; |
| | 146 | delete[] ak3; |
| | 147 | delete[] ak2; |
| | 148 | } |
| | 149 | |
| | 150 | void |
| 109 | | // two half-steps |
| 110 | | hh = 0.5*h; |
| 111 | | rk4(ysav, dysav, n, xsav, hh, ytemp, derivs); |
| 112 | | *x = xsav + hh; |
| 113 | | (*derivs)(*x, ytemp, dydx); |
| 114 | | rk4(ytemp, dydx, n, *x, hh, y, derivs); |
| 115 | | |
| 116 | | // the full-step |
| 117 | | *x = xsav + h; |
| 118 | | if (*x == xsav) |
| 119 | | throw "Step size too small in routine rkqc"; |
| 120 | | rk4(ysav, dysav, n, xsav, h, ytemp, derivs); |
| 121 | | |
| 122 | | // calculate precision |
| 123 | | errmax = 0.0; |
| 124 | | for (i = 1; i <= n; ++i) { |
| 125 | | ytemp[i] = y[i] - ytemp[i]; |
| 126 | | temp = fabs(ytemp[i] / yscal[i]); |
| 127 | | if (errmax < temp) errmax = temp; |
| | 179 | nr_rkck(y, dydx, n, *x, h, ytemp, yerr, derivs); // Take a step. |
| | 180 | errmax = 0.0; // Evaluate accuracy. |
| | 181 | for (i = 1; i <= n; i++) |
| | 182 | errmax = FMAX(errmax, fabs(yerr[i]/yscal[i])); |
| | 183 | errmax /= eps; // Scale relative to required tolerance. |
| | 184 | if (errmax <= 1.0) break; // Step succeeded. Compute size of next step. |
| | 185 | htemp = SAFETY*h*pow(errmax, PSHRNK); |
| | 186 | // Truncation error too large, reduce stepsize. |
| | 187 | h = (h >= 0.0 ? FMAX(htemp, 0.1*h) : FMIN(htemp, 0.1*h)); |
| | 188 | // No more than a factor of 10. |
| | 189 | xnew = (*x)+h; |
| | 190 | if (xnew == *x) |
| | 191 | throw std::runtime_error("stepsize underflow in nr_rkqs"); |
| | 192 | } |
| | 193 | |
| | 194 | if (errmax > ERRCON) *hnext = SAFETY*h*pow(errmax, PGROW); |
| | 195 | else *hnext = 5.0*h; // No more than a factor of 5 increase. |
| | 196 | *x += (*hdid=h); |
| | 197 | for (i = 1; i <= n; i++) y[i] = ytemp[i]; |
| | 198 | |
| | 199 | delete[] ytemp; |
| | 200 | delete[] yerr; |
| | 201 | } |
| | 202 | |
| | 203 | const int MAXSTP = 10000; |
| | 204 | const double TINY = 1.e-30; |
| | 205 | |
| | 206 | // User storage for intermediate results. Preset kmax and dxsav in |
| | 207 | // the calling program. If kmax != 0 results are stored at |
| | 208 | // approximate intervals dxsav in the arrays xp[1..kount], |
| | 209 | // yp[1..nvar][1..kount], where kount is output by odeint. Defining |
| | 210 | // declarations for these variables, with memory allocations |
| | 211 | // xp[1..kmax] and yp[1..nvar][1..kmax] for the arrays, should be in |
| | 212 | // the calling program. |
| | 213 | struct nr_odeint_data |
| | 214 | { |
| | 215 | int kmax, kount; |
| | 216 | double *xp, **yp, dxsav; |
| | 217 | }; |
| | 218 | |
| | 219 | void |
| | 220 | nr_odeint(double ystart[], int nvar, double x1, double x2, double eps, |
| | 221 | double h1, double hmin, int* nok, int* nbad, nr_odeint_data* data, |
| | 222 | void (*derivs)(double, double[], double[]), |
| | 223 | void (*rkqs)(double[], double[], int, double*, double, |
| | 224 | double, double[], double*, double*, |
| | 225 | void (*)(double, double[], double[]))) |
| | 226 | // Runge-Kutta driver with adaptive stepsize control. Integrate |
| | 227 | // starting values ystart[1..nvar] from x1 to x2 with accuracy eps, |
| | 228 | // storing intermediate results into the variable data. h1 should be |
| | 229 | // set as a guessed first stepsize, hmin as the minimum allowed |
| | 230 | // stepsize (can be zero). On output nok and nbad are the number of |
| | 231 | // good and bad (but retried and fixed) steps taken, and ystart is |
| | 232 | // replaced by values at the end of the integration interval. derivs |
| | 233 | // is the user-supplied routine for calculating the right-hand side |
| | 234 | // derivative, while rkqs is the name of the stepper routine to be |
| | 235 | // used. |
| | 236 | { |
| | 237 | int nstp, i; |
| | 238 | double xsav, x, hnext, hdid, h; |
| | 239 | double *yscal, *y, *dydx; |
| | 240 | |
| | 241 | yscal = new double[nvar+1]; |
| | 242 | y = new double[nvar+1]; |
| | 243 | dydx = new double[nvar+1]; |
| | 244 | |
| | 245 | x = x1; |
| | 246 | h = SIGN(h1, x2-x1); |
| | 247 | *nok = (*nbad) = data->kount = 0; |
| | 248 | for (i = 1; i <= nvar; ++i) y[i] = ystart[i]; |
| | 249 | if (data->kmax > 0) |
| | 250 | xsav = x - data->dxsav*2.0; // Assures storage of first step. |
| | 251 | for (nstp = 1; nstp <= MAXSTP; nstp++) { // Take at most MAXSTP steps. |
| | 252 | (*derivs)(x, y, dydx); |
| | 253 | for (i = 1; i <= nvar; i++) |
| | 254 | // Scaling used to monitor accuracy. This general-purpose |
| | 255 | // choice can be modified if need be. |
| | 256 | yscal[i] = fabs(y[i])+fabs(dydx[i]*h)+TINY; |
| | 257 | if (data->kmax > 0 && data->kount < data->kmax-1 && |
| | 258 | fabs(x-xsav) > fabs(data->dxsav)) { |
| | 259 | data->xp[++data->kount] = x; // Store intermediate results. |
| | 260 | for (i = 1; i <= nvar; i++) |
| | 261 | data->yp[i][data->kount] = y[i]; |
| | 262 | xsav = x; |