Home
Manual
Packages
Global Index
Keywords
Quick Reference
|
/*
RKUTTA.I
4th order Runge-Kutta integrator (rk_integrate, rkutta)
Also Bulirsch-Stoer integrator (bs_integrate, bstoer)
After routines in Numerical Recipes by Press et.al.
$Id$
*/
/* Copyright (c) 1995. The Regents of the University of California.
All rights reserved. */
/* ------------------------------------------------------------------------ */
func rk_integrate (derivative, y1, rk_x, epsilon, dx1)
/* DOCUMENT y= rk_integrate(derivative, y1, x, epsilon, dx1)
integrates dydx= DERIVATIVE(y,x) beginning at (X(1),Y1) and
going to X(0) with fractional error EPSILON. The result is
the value of y at each value in the list X. If non-nil, DX1
will be used as initial guess for the first step size.
Otherwise, X(2)-X(1) will be the first step size guess.
The list of X values must be monotone -- strictly increasing
or strictly decreasing; the Runge-Kutta step sizes are selected
adaptively until the next X value would be passed, when the
step size is adjusted to complete the step exactly.
The external variable rk_maxits (default 10000) is the
maximum number of steps rk_integrate will take.
If a function rk_yscale(y,dydx,x,dx) exists, it is used
to compute an appropriate yscale to give the EPSILON error
criterion meaning. Otherwise, yscale is taken to be:
abs(y)+abs(dydx*dx)+1.e-30
Based on odeint from Numerical Recipes (Press, et.al.).
If the function you are trying to integrate is very
smooth, and your X values are fairly far apart, bs_integrate
may work better than rk_integrate.
SEE ALSO: rkutta, bs_integrate, rk_maxits, rk_minstep,
rk_maxstep, rk_ngood, rk_nbad, rkdumb, rk4
*/
{
if (numberof(rk_x)<2) return array(y1, dimsof(rk_x));
local rk_y;
if (is_void(dx1)) dx1= rk_x(2)-rk_x(1);
rk_nstore= -1;
rkutta, derivative, y1, rk_x(1), rk_x(0), epsilon, dx1;
return rk_y;
}
func rkutta (derivative, y0,x0, x1,epsilon, dx0)
/* DOCUMENT y1= rkutta(derivative, y0,x0, x1,epsilon, dx0)
integrates dydx= DERIVATIVE(y,x) beginning at (X0,Y0) and
going to X1 with fractional error EPSILON. The result is
the value of y at X1. DX0 will be used as the initial guess
for a step size.
If the external variable rk_nstore is >0, rk_y and rk_x
will contain up to rk_nstore intermediate values after the
call to rkutta. Consider using rk_integrate if you need
this feature; using rk_nstore gives you the results at
intermediate values which will tend to be closer where
the Runge-Kutta step size was smaller, while rk_integrate
forces you to specify precisely which x values you want.
The external variable rk_maxits (default 10000) is the
maximum number of steps rkutta will take. The variable
rk_minstep (default 0.0) is the minimum step size. The
variable rk_maxstep (default 1.e35) is the maximum step
size, which you may need if you are storing intermediate
values (particularly with bstoer).
If a function rk_yscale(y,dydx,x,dx) exists, it is used
to compute an appropriate yscale to give the EPSILON error
criterion meaning. Otherwise, yscale is taken to be:
abs(y)+abs(dydx*dx)+1.e-30
Based on odeint from Numerical Recipes (Press, et.al.).
If the function you are trying to integrate is very
smooth, bstoer will probably work better than rkutta.
SEE ALSO: rk_integrate, bstoer, rk_nstore, rk_maxits,
rk_minstep, rk_maxstep, rk_ngood, rk_nbad, rkdumb, rk4
*/
{
extern rk_x, rk_y, rk_ngood, rk_nbad;
rk_ngood= rk_nbad= 0;
if (rk_nstore > 0) {
if (rk_nstore<2) rk_nstore= 2;
rk_x= array(double(x0), rk_nstore);
rk_y= array(0.+y0, rk_nstore);
s= [1, 1, 1]; /* see rk_store function */
} else if (rk_nstore < 0) {
x0= rk_x(1);
x1= rk_x(0);
if (anyof(rk_x(dif)*(x1-x0) <= 0.0))
error, "given rk_x must be monotonic";
rk_y= array(0.+y0, numberof(rk_x));
s= 2;
}
dxsign= sign(x1-x0);
dx= double(abs(dx0)*dxsign);
x= double(x0);
y= 0.+y0;
for (n=1 ; n<=rk_maxits ; ++n) {
dydx= derivative(y, x);
if (!is_void(rk_yscale)) yscale= rk_yscale(y,dydx,x,dx);
else yscale= abs(y)+abs(dydx*dx)+1.e-30;
if (abs(dx) > rk_maxstep) dx= dxsign*rk_maxstep;
if (dxsign*(x+dx-x1) > 0.0) dx= x1-x;
if (rk_nstore<0 &&
dxsign*(x+dx-rk_x(s)) > 0.0) dx= rk_x(s)-x;
local dxdid, dxnxt;
y= rkqc(y,dydx, x,dx, derivative, epsilon,yscale, dxdid,dxnxt);
x+= dxdid;
if (dxdid == dx) ++rk_ngood;
else ++rk_nbad;
if (rk_nstore>0) s= rk_store(y,x,s);
else if (rk_nstore<0 &&
dxsign*(x-rk_x(s))>=0.0) rk_y(..,s++)= y;
all_done= (dxsign*(x-x1) >= 0.0);
if (all_done) break;
if (abs(dxnxt) < abs(rk_minstep))
error, "required step less than rk_minstep";
dx= dxnxt;
}
if (rk_nstore>0) {
if (rk_x(s(3)) != x) {
s(2)= 1; /* always store final value */
s= rk_store(y,x,s);
}
rk_y= rk_y(..,1:s(3));
rk_x= rk_x(1:s(3));
}
if (!all_done) error, "exceeded rk_maxits iterations";
return y;
}
local rk_nstore , rk_maxits, rk_minstep, rk_maxstep, rk_ngood, rk_nbad;
/* DOCUMENT rk_nstore, rk_maxits, rk_minstep, rk_maxstep,
rk_ngood, rk_nbad
rk_nstore maximum number of y values rkutta (bstoer) will store
after rkutta (bstoer) call, rk_y and rk_x contain stored values
The other variables are inputs or outputs for rkutta, bstoer,
rk_integrate, or bs_integrate:
rk_maxits maximum number of steps (default 10000)
rk_minstep minimum step size (default 0.0)
rk_maxstep maximum step size (default 1.e35)
rk_ngood number of good steps taken
rk_nbad number of failed (but repaired) steps taken
*/
rk_maxits= 10000;
rk_minstep= 0.0;
rk_maxstep= 1.0e35;
rk_nstore= 0;
func rk_store (y,x,s)
{
/* s= [step number, step increment, store index] */
i= ++s(1);
if (! ((i-1)%s(2))) {
i= ++s(3);
if (i > rk_nstore) {
y2= rk_y(..,1:0:2);
x2= rk_x(1:0:2);
i= numberof(x2);
rk_y(..,1:i)= y2;
rk_x(1:i)= x2;
s(3)= ++i;
s(2)*= 2;
}
rk_y(..,i)= y;
rk_x(i)= x;
}
return s;
}
func rkdumb (derivative, y0,x0, x1,nsteps, nosave=)
/* DOCUMENT y_of_x= rkdumb(derivative, y0,x0, x1,nsteps)
integrates dydx= DERIVATIVE(y,x) beginning at (X0,Y0) and
going to X1 in NSTEPS 4th order Runge-Kutta steps. The
result is dimsof(Y0)-by-(NSTEPS+1) values of y at the points
span(X0, X1, NSTEPS+1).
If the nosave= keyword is non-zero, the returned value will
simply be the final y value.
*/
{
dx= (x1-x0)/nsteps;
++nsteps;
if (!nosave) y= array(0.+y0, nsteps);
for (i=2 ; i<=nsteps ; ++i) {
y0= rk4(y0,derivative(y0,x0), x0,dx, derivative);
x0+= dx;
if (!nosave) y(..,i)= y0;
}
return nosave? y0 : y;
}
func rkqc (y,dydx, x,dx, derivative, epsilon,yscale, &dxdid,&dxnxt)
{
x0= x;
y0= y;
for (;;) {
x= x0+dx;
if (x==x0) error, "integration step crash";
/* first take two half steps... */
dx2= 0.5*dx;
x2= x0+dx2;
y2= rk4(y0,dydx, x0,dx2, derivative);
y2= rk4(y2,derivative(y2,x2), x2,dx2, derivative);
/* ...then compare with one full step... */
y1= rk4(y0,dydx, x0,dx, derivative);
/* ...to estimate error */
y1= y2-y1;
err= max(abs(y1/yscale))/epsilon;
if (err <= 1.0) {
dxdid= dx;
dxnxt= (err>6.e-4)? 0.9*dx*err^-0.20 : 4.*dx;
break;
}
dx*= 0.9*err^-0.25;
}
return y2 + y1/15.;
}
func rk4 (y,dydx, x,dx, derivative)
/* DOCUMENT y_at_x_plus_dx= rk4(y,dydx, x,dx, derivative)
takes a single 4th order Runge-Kutta step from X to X+DX.
DERIVATIVE(y,x) is a function returning dydx; the input DYDX
is DERIVATIVE(y,x) at the input (X,Y). This fourth evaluation
of DERIVATIVE must be performed by the caller of rk4.
*/
{
dx2= 0.5*dx;
x2= x+dx2;
dydxp= derivative(y+dydx*dx2, x2); /* slope at 1st trial midpoint */
dydxm= derivative(y+dydxp*dx2, x2); /* slope at 2nd trial midpoint */
dydxp+= dydxm;
dydxm= derivative(y+dydxm*dx, x+dx); /* slope at trial endpoint */
return y + (dydx+dydxp+dydxp+dydxm)*(dx/6.0);
}
/* ------------------------------------------------------------------------ */
func bs_integrate (derivative, y1, rk_x, epsilon, dx1)
/* DOCUMENT y= bs_integrate(derivative, y1, x, epsilon, dx1)
Bulirsch-Stoer integrator, otherwise identical to rk_integrate
routine. All of the options for rk_integrate work here as well.
Based on odeint from Numerical Recipes (Press, et.al.).
If the function you are trying to integrate is not very
smooth, or your X values are closely spaced, rk_integrate
will probably work better than bs_integrate.
SEE ALSO: bstoer, rk_integrate, rk_maxits, rk_minstep,
rk_maxstep, rk_ngood, rk_nbad, rkdumb, rk4
*/
{
if (numberof(rk_x)<2) return array(y1, dimsof(rk_x));
local rk_y;
if (is_void(dx1)) dx1= rk_x(2)-rk_x(1);
rk_nstore= -1;
bstoer, derivative, y1, rk_x(1), rk_x(0), epsilon, dx1;
return rk_y;
}
func bstoer (derivative, y0,x0, x1,epsilon, dx0)
/* DOCUMENT y1= bstoer(derivative, y0,x0, x1,epsilon, dx0)
Bulirsch-Stoer integrator, otherwise identical to rkutta routine.
All of the options for rkutta (rk_nstore, etc.) work here as well.
If the function you are trying to integrate is not very
smooth, rkutta will probably work better than bstoer.
SEE ALSO: rkutta, rk_nstore, rk_maxits, rk_minstep, rk_maxstep,
rk_ngood, rk_nbad
*/
{
extern _rzextr_x, _rzextr_d;
rkqc= bsstep;
_rzextr_x= array(0.0, numberof(_bs_nseq));
_rzextr_d= array(0.+y0, 7);
return rkutta(derivative, y0,x0, x1,epsilon, dx0);
}
func bsstep (y,dydx, x,dx, derivative, epsilon,yscale, &dxdid,&dxnxt)
{
x0= x;
y0= y;
for (;;) {
for (i=1 ; i<=numberof(_bs_nseq) ; ++i) {
n= _bs_nseq(i);
y= rzextr(i, (dx/n)^2, mod_midpt(y0,dydx, x0,dx, derivative, n),
yerr, 7);
err= max(abs(yerr/yscale))/epsilon;
if (err < 1.0) {
dxdid= dx;
if (i==7) dxnxt= 0.95*dx;
else if (i==6) dxnxt= 1.2*dx;
else dxnxt= (16./*_bs_nseq(6)*/*dx)/n;
return y;
}
}
/* step failed, claimed to be unusual */
dx*= 0.0625; /* related to numberof(_bs_nseq) */
if (x+dx == x) error, "integration step crash";
}
}
_bs_nseq= [2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96];
func mod_midpt (y,dydx, x,dx, derivative, nstep)
{
dx/= nstep;
ym= y;
y+= dydx*dx;
x+= dx;
dydx= derivative(y,x);
dx2= 2.*dx;
for (--nstep ; nstep ; --nstep) {
swap= ym + dydx*dx2;
ym= y;
y= swap;
x+= dx;
dydx= derivative(y,x);
}
return 0.5*(ym+y+dydx*dx);
}
func rzextr (iest, xest, yest, &yerr, nuse)
{
_rzextr_x(iest)= xest;
if (iest==1) {
_rzextr_d(..,1)= yest;
yerr= yest;
return yest;
} else {
m1= ((iest<nuse)? iest : nuse) - 1;
fx= _rzextr_x(iest-1 : iest-m1 : -1)/xest; /* no more than nuse-1 */
yy= yest;
v= _rzextr_d(..,1);
_rzextr_d(..,1)= c= yy;
for (k=1 ; k<=m1 ; ++k) {
b1= fx(k)*v;
b= b1-c;
ok= double(b!=0.0);
bad= 1.0-ok;
b= ((c-v)/(b+bad))*ok;
ddy= c*b + bad*v;
c= b1*b + bad*c;
if (k!=m1) v= _rzextr_d(..,k+1);
_rzextr_d(..,k+1)= ddy;
yy+= ddy;
}
yerr= ddy;
return yy;
}
}
/* ------------------------------------------------------------------------ */
|