libpspm
rkck45.tpp
Go to the documentation of this file.
1 // Fifth-order Runge-Kutta step with monitoring of local truncation error to ensure accuracy and
2 // adjust stepsize. Input are
3 // y[...], dydx[...] -- the dependent variable vector and its derivative at the starting value of the independent variable x.
4 // htry, hdid -- the stepsize to be attempted htry, and the stepsize that was actually accomplished hdid
5 // hnext -- the estimated next stepsize,
6 // derivs -- the user-supplied routine that computes the right-hand side derivatives.
7 //
8 // Also used in the class are the required accuracy eps, and the vector yscal[...] against which the error is scaled.
9 // On output, y and x are replaced by their new values
10 template <class functor>
11 void RKCK45::RKStep(container& y, container& dydx, double& x, double htry,
12  double& hdid, double& hnext, functor& derivs){
13 
14  container yerr(y.size()), ytemp(y.size()); // TODO: Should these also be made class members to prevent reallocation?
15  double errmax;
16  double h=htry; // Set stepsize to the initial trial value.
17  for (;;) { // infinite loop
18  RKTry(y, dydx, x, h, ytemp, yerr, derivs);// Take a step.
19 
20  errmax=0.0; // Evaluate accuracy.
21  for (int i=0; i<y.size(); i++) errmax=std::max(errmax, fabs(yerr[i]/yscal[i]));
22  //errmax /= eps; // Scale relative to required tolerance.
23  //std:: cout << "x = " << x << ", errmax = " << errmax << "\n";
24  if (errmax <= 1.1) break; // Step succeeded. Compute size of next step.
25  double htemp=h*fmax(SAFETY*pow(errmax,PSHRNK), 0.2); // Truncation error too large, reduce stepsize, max by a factor of 0.2.
26  h=htemp; //(h >= 0.0 ? std::max(htemp,0.1*h) : std::min(htemp,0.1*h)); // No more than a factor of 10.
27  if ((x+h) == x) std::cerr<<"stepsize underflow in rkqs"<<std::endl;
28  }
29  if (errmax < 0.5) hnext=h*fmin(SAFETY*pow(errmax,PGROW), 5); // Step is too small, increase it next time, no more than 5 times
30  else hnext=h;
31  //cout << "hnext = " << hnext << "\n";
32  hdid=h;
33  x += h;
34  for (int i=0; i<y.size();i++) y[i] = ytemp[i];
35 }
36 
37 // Given values for variables y[...] and their derivatives dydx[...] known at x, use
38 // the fifth-order Cash-Karp Runge-Kutta method to advance the solution over an interval h
39 // and return the incremented variables as yout[..]. Also return an estimate of the local
40 // truncation error in yout using the embedded fourth-order method. The user supplies the routine
41 // derivs(x,y,dydx), which returns derivatives dydx at x.
42 template <class functor>
43 void RKCK45::RKTry(container& y, container& dydx, double& x, double h, container& yout, container& yerr, functor& derivs){
44  //std::cout << "try:\n";
45  // a0 a1 a2 a3 a4 a5
46  static constexpr double as[] = {0, 0.2, 0.3, 0.6, 1.0, 0.875};
47  // c0 c1 c2 c3 c4 c5
48  static constexpr double cs[] = {37.0/378.0, 0.0, 250.0/621.0, 125.0/594.0, 0.0, 512.0/1771.0};
49  // dc0 dc1 dc2 dc3 dc4 dc5
50  static constexpr double dc[] = {cs[0]-2825.0/27648.0, 0.0, cs[2]-18575.0/48384.0, cs[3]-13525.0/55296.0, -277.00/14336.0, cs[5]-0.25};
51  static constexpr double bs[6][6] =
52  {{0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
53  {0.2, 0.0, 0.0, 0.0, 0.0, 0.0},
54  {3.0/40.0, 9.0/40.0, 0.0, 0.0, 0.0, 0.0},
55  {0.3, -0.9, 1.2, 0.0, 0.0, 0.0},
56  {-11.0/54.0, 2.5, -70.0/27.0, 35.0/27.0, 0.0, 0.0},
57  {1631.0/55296.0, 175.0/512.0, 575.0/13824.0, 44275.0/110592.0, 253.0/4096.0, 0.0}};
58 
59  int N=y.size();
60 
61  container& k0 = dydx; // just different name for the same variable
62 
63  // First step.
64  for (int i=0; i<N; i++) yt[i] = y[i] + h*bs[1][0]*k0[i];
65  derivs(x+as[1]*h, yt.begin(), k1.begin(), nullptr);
66  ++nfe;
67 
68  // Second step.
69  for (int i=0; i<N; i++) yt[i] = y[i] + h*(bs[2][0]*k0[i]+bs[2][1]*k1[i]);
70  derivs(x+as[2]*h,yt.begin(),k2.begin(), nullptr);
71  ++nfe;
72 
73  // Third step.
74  for (int i=0; i<N; i++) yt[i] = y[i] + h*(bs[3][0]*k0[i]+bs[3][1]*k1[i]+bs[3][2]*k2[i]);
75  derivs(x+as[3]*h,yt.begin(),k3.begin(), nullptr);
76  ++nfe;
77 
78  // Fourth step.
79  for (int i=0; i<N; i++) yt[i] = y[i] + h*(bs[4][0]*k0[i]+bs[4][1]*k1[i]+bs[4][2]*k2[i]+bs[4][3]*k3[i]);
80  derivs(x+as[4]*h,yt.begin(),k4.begin(), nullptr);
81  ++nfe;
82 
83  // Fifth step.
84  for (int i=0;i<N;i++) yt[i] = y[i] + h*(bs[5][0]*k0[i]+bs[5][1]*k1[i]+bs[5][2]*k2[i]+bs[5][3]*k3[i]+bs[5][4]*k4[i]);
85  derivs(x+as[5]*h,yt.begin(),k5.begin(), nullptr);
86  ++nfe;
87 
88  // Sixth step.
89  // Accumulate increments with proper weights.
90  for (int i=0;i<N;i++) yout[i] = y[i] + h*(cs[0]*k0[i]+cs[2]*k2[i]+cs[3]*k3[i]+cs[5]*k5[i]);
91 
92  // Estimate error as difference between fourth and fifth order methods.
93  for (int i=0; i<N; i++) yerr[i] = h*(dc[0]*dydx[i]+dc[2]*k2[i]+dc[3]*k3[i]+dc[4]*k4[i]+dc[5]*k5[i]);
94 }
95 
96 // The main function which takes next RK step with predefined accuracy
97 // returns bool: true if time becomes larger of equal to t_stop, otherwise false
98 // arguments:
99 // 1) x -- current time (independent variable), which will be changes for a step when successful
100 // 2) y -- current solution (dependent variable)
101 // 3) derivs -- function which evaluates derivatives
102 template <class functor>
103 void RKCK45::Step(double& x, container& y, functor& derivs, double hmax, double hmin){
104 
105  // resize the container if necessary
106  if (y.size() != sys_size) resize(y.size());
107 
108  // Calculates derivatives for the new step
109  //cout << "\n>> init derivs ~~~\n";
110  derivs(xt,y.begin(),dydx.begin(), nullptr);
111  ++nfe;
112  // good way of determining desired accuracy
113  for (int i=0; i<y.size(); i++) yscal[i] = eps_abs + eps_rel*(a_y * fabs(y[i]) + a_dydt * fabs(dydx[i]*ht)); // fabs(y[i]) + fabs(dydx[i]*ht) + 1e-3;
114  double hnext, hdid;
115  // Cals the Runge-Kutta rountine
116  // takes: y -- dependent variable, dydx -- derivative at the beginning
117  // ht -- trial setpsize, hdid --
118  ht = std::min(ht, hmax);
119  ht = std::max(ht, hmin);
120  RKStep(y, dydx, xt, ht, hdid, hnext, derivs); // Make one RK step
121  if (hdid == ht) ++nok; else ++nbad; // good or bad step
122  ht = hnext;
123  x = xt;
124  //return x<t_stop;
125 }
126 
127  //template <class functor>
128  //void Step_RK4(double &x, double h, container& y, functor& derivs){
132  //if (y.size() != sys_size) resize(y.size());
133 
134  //double h2=h*0.5;
135  //double xh = x + h2;
136  //derivs(x, y, k1); // First step : evaluating k1
137  //for (int i=0; i<y.size(); i++) yt[i] = y[i] + h2*k1[i];// Preparing second step by ty <- y + k1/2
138  //derivs(xh, yt, k2); // Second step : evaluating k2
139  //for (int i=0; i<y.size(); i++) yt[i] = y[i] + h2*k2[i];// Preparing third step by yt <- y + k2/2
140  //derivs(xh, yt, k3); // Third step : evaluating k3
141  //for (int i=0; i<y.size(); i++) yt[i] = y[i] + h*k3[i];// Preparing fourth step yt <- y + k3
142  //derivs(x+h, yt, k4); // Final step : evaluating k4
143  //for (int i=0; i<y.size(); i++) y[i] += h/6.0*(k1[i]+2.0*(k2[i]+k3[i])+k4[i]);
144 
145  //x += h;
146  //}
147 
148 template <class functor, class AfterStep>
149 void RKCK45::Step_to(double t_stop, double& x, container& y, functor& derivs, AfterStep &after_step){
150  while (x < t_stop){
151  Step(x,y,derivs, t_stop-x);
152  after_step(x, y.begin());
153  }
154 }
155 
156 
157 
158 
159 
int nok
Definition: rkck45.h:59
double a_dydt
Definition: rkck45.h:54
static constexpr double PGROW
Definition: rkck45.h:50
container yt
Definition: rkck45.h:63
container k3
Definition: rkck45.h:63
double eps_rel
Definition: rkck45.h:57
int nbad
Definition: rkck45.h:59
container yscal
Definition: rkck45.h:55
void RKStep(container &y, container &dydx, double &x, double htry, double &hdid, double &hnext, functor &derivs)
Definition: rkck45.tpp:11
double h()
Definition: rkck45.h:94
int nfe
Definition: rkck45.h:61
double ht
Definition: rkck45.h:56
container k4
Definition: rkck45.h:63
void resize(int new_size)
Definition: rkck45.cpp:10
static constexpr double PSHRNK
Definition: rkck45.h:51
double eps_abs
Definition: rkck45.h:57
static constexpr double SAFETY
Definition: rkck45.h:49
void Step_to(double t_stop, double &x, container &y, functor &derivs, AfterStep &after_step)
Definition: rkck45.tpp:149
double xt
Definition: rkck45.h:58
container k1
Definition: rkck45.h:63
double a_y
Definition: rkck45.h:53
container k2
Definition: rkck45.h:63
void Step(double &x, container &y, functor &derivs, double hmax=1e20, double hmin=1e-6)
Definition: rkck45.tpp:103
void RKTry(container &y, container &dydx, double &x, double h, container &yout, container &yerr, functor &derivs)
Definition: rkck45.tpp:43
container k5
Definition: rkck45.h:63
double t_stop
Definition: rkck45.h:58
container dydx
Definition: rkck45.h:60
std::vector< double > container
Definition: rkck45.h:32
int sys_size
Definition: rkck45.h:65