Show
Ignore:
Timestamp:
02/19/08 20:41:04 (5 years ago)
Author:
mrkn
Message:

lang/cplusplus/boost-supplement/branches/numeric-ode/: libs/numeric/ode/test_runge_kutta_rk4.cpp (nr_rkck, nr_rkqs, nr_odeint): These functions are implemented based onf Numerical Recipes in C, 2nd ed. and nr_rkqc has been needed.

Location:
lang/cplusplus/boost-supplement/branches/numeric-ode
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • lang/cplusplus/boost-supplement/branches/numeric-ode/ChangeLog

    r6884 r6936  
     12008-02-19  Kenta Murata  <mrkn@gmail.com> 
     2 
     3        * libs/numeric/ode/test_runge_kutta_rk4.cpp 
     4        (nr_rkck, nr_rkqs, nr_odeint): These functions are implemented 
     5        based onf Numerical Recipes in C, 2nd ed. and nr_rkqc has been 
     6        needed. 
     7 
    182008-02-18  Kenta Murata  <mrkn@gmail.com> 
    29 
  • lang/cplusplus/boost-supplement/branches/numeric-ode/libs/numeric/ode/test_runge_kutta_rk4.cpp

    r6884 r6936  
    88#include <boost/test/floating_point_comparison.hpp> 
    99#include <boost/tuple/tuple.hpp> 
     10 
     11#include <stdexcept> 
    1012 
    1113typedef boost::numeric::ublas::vector<double> dvector; 
     
    6769    nr_rk4(v, dv, nvar, x, h, vout, derivs); 
    6870    if ((double)(x+h) == x) 
    69       throw "Step size too small in routine rkdumb"; 
     71      throw std::runtime_error("Step size too small in routine rkdumb"); 
    7072    x += h; 
    7173    xx[k+1] = x; 
     
    8284 
    8385void 
     86nr_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 
     150void 
    84151nr_rkqc(double y[], double dydx[], int n, double* x, double htry, 
    85152        double eps, double yscal[], double* hdid, double* hnext, 
    86         void (*derivs)(double, double*, double*)) 
    87 { 
     153        void (*derivs)(double, double[], double[])) 
     154// Fifth-order Runge-Kutta step with monitoring of local truncation 
     155// error to ensure accuracy and adjust stepsize.  Input are the 
     156// dependent variable vector y[1..n] and its derivative dydx[1..n] at 
     157// the starting value of the independent variable x.  Also input are 
     158// the stepsize to be attempted htry, the required accuracy eps, and 
     159// the vector yscal[1..n] against which the error is scaled.  On 
     160// output, y and x are replaced by their new values, hdid is the 
     161// stepsize that was actually accomplished, and hnext is the estimated 
     162// next stepsize.  derivs is the user-supplied routine that computes 
     163// the right-hand side derivatives. 
     164{ 
     165  static const double SAFETY = 0.9; 
    88166  static const double PGROW  = -0.20; 
    89167  static const double PSHRNK = -0.25; 
    90   static const double FCOR   = 0.06666666; // 1.0 / 15.0 
    91   static const double SAFETY = 0.9; 
    92   static const double ERRCON = 6.0e-4; // == (4.0/SAFETY)**(1/PGROW) 
     168 
     169  // The value ERRCON equals (5/SAFETY) raised to the power (1/PGROW). 
     170  static const double ERRCON = 1.89e-4; 
    93171 
    94172  int i; 
    95   double xsav, hh, h, temp, errmax; 
    96   double *dysav, *ysav, *ytemp; 
    97  
    98   dysav = new double[n+1]; 
    99   ysav  = new double[n+1]; 
     173  double errmax, h, htemp, xnew, *yerr, *ytemp; 
     174 
     175  yerr = new double[n+1]; 
    100176  ytemp = new double[n+1]; 
    101  
    102   xsav = *x; 
    103   for (i = 1; i <= n; ++i) { 
    104     ysav[i] = y[i]; 
    105     dysav[i] = dydx[i]; 
    106   } 
    107   h = htry; 
     177  h = htry;                // Set stepsize to the initial trial value. 
    108178  for (;;) { 
    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 
     203const int MAXSTP = 10000; 
     204const 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. 
     213struct nr_odeint_data  
     214{ 
     215  int kmax, kount; 
     216  double *xp, **yp, dxsav; 
     217}; 
     218 
     219void 
     220nr_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; 
    128263    } 
    129     errmax /= eps;              // scale for allowing error 
    130     if (errmax <= 1.0) { 
    131       *hdid = h; 
    132       *hnext = (errmax > ERRCON ? 
    133                 SAFETY*h*exp(PGROW*log(errmax)) : 4.0*h); 
    134       break; 
     264    if ((x + h - x2)*(x+h-x1) > 0.0) h = x2 - x; // If stepsize can 
     265                                                 // overshoot, 
     266                                                 // decrese. 
     267    (*rkqs)(y, dydx, nvar, &x, h, eps, yscal, &hdid, &hnext, derivs); 
     268    if (hdid == h) ++(*nok); else ++(*nbad); 
     269    if ((x-x2)*(x2-x1) >= 0.0) { // Are we done? 
     270      for (i = 1; i <= nvar; i++) ystart[i] = y[i]; 
     271      if (data->kmax) { 
     272        data->xp[++data->kount] = x; // Save final step. 
     273        for (i = 1; i <= nvar; i++) 
     274          data->yp[i][data->kount] = y[i]; 
     275      } 
     276      delete[] dydx; 
     277      delete[] y; 
     278      delete[] yscal; 
     279      return;                   // Normal exit. 
    135280    } 
    136     h = SAFETY*h*exp(PSHRNK*log(errmax)); 
    137   } 
    138   for (i = 1; i <= n; ++i) 
    139     y[i] += ytemp[i]*FCOR; 
    140  
    141   delete[] ytemp; 
    142   delete[] ysav; 
    143   delete[] dysav; 
    144 } 
    145  
     281    if (fabs(hnext) <= hmin) 
     282      throw std::runtime_error("Step size too small in nr_odeint"); 
     283    h = hnext; 
     284  } 
     285  throw std::runtime_error("Too many steps in routine nr_odeint"); 
    146286} 
    147287