# This is an adaptive 4th-order Runge-Kutta integrator.
ode4 = function( ydot; t0; tf; y0; output; tol; h; yscale ) {
local( t; y; dydt; scale; hh; yt; ys );
local( yout; tout; output_cnt; output_max );
local( TINY; rk4; errmax );
TINY = 1e-4;
rk4 = ode4.rk4;
# Convert args, if necessary.
t0 = scalar( t0 );
tf = scalar( tf );
y0 = vector( y0 );
# Supply defaults.
if ( tol == NULL ) { tol = 0.001; else tol = scalar( tol ); }
if ( h == NULL ) { h = abs( tf - t0 ) / 1000.0; else h = scalar( h ); }
# Initialize.
t = t0;
h = h * ( tf - t0 ) / abs( tf - t0 );
y = y0;
scale = yscale;
# We'll try to save a little time here by accumulating the
# output before appending it.
if ( output == NULL ) {
yout = dense([y]');
else
yout = dense([output(t;y)]');
}
yout = yout, fill( yout.nr, 99; 0 );
tout = t, fill( 99; 0 );
output_cnt = 1;
output_max = 100;
while ( (t-tf)*(tf-t0) < 0.0 ) {
dydt = unlabel( ydot( t; y ) );
if ( yscale == NULL ) { scale = abs(y) + abs(h*dydt) + TINY; }
# If the step can overshoot end, cut the stepsize.
if ( (t+h-tf)*(t+h-t0) > 0.0 ) { h = tf - t; }
# Take a step
ys = y;
while ( 1 ) {
# Take two half steps.
hh = 0.5 * h;
yt = rk4( ydot; t; hh; ys; dydt );
y = rk4( ydot; t+hh; hh; yt; unlabel( ydot( t+hh; yt ) ) );
# Check for step too small.
if ( t+h == t ) {
message( "run time error: Step size too small." );
yt = 0.0;
errmax = 1.0;
tf = t;
else
# Take the full step.
yt = y - rk4( ydot; t; h; ys; dydt );
# Evaluate accuracy.
errmax = norm( abs( yt ) / scale; "inf" ) / tol;
}
# Success?
if ( errmax <= 1.0 ) { break; }
# Try again. Reduce step size, but by no more than
# a factor of about 50.
if ( errmax > 4e6 ) {
h = 0.02*h;
else
h = 0.9*h*errmax^-0.25;
}
}
t += h;
# Take care of 5th order.
y += yt/15.0;
# Save output at the current point.
if ( ( output_cnt += 1 ) > output_max ) {
yout = yout, fill( yout.nr,100; 0.0 );
tout = tout, fill( 100; 0 );
output_max += 100;
}
tout[output_cnt] = t;
if ( output == NULL ) {
yout[;output_cnt] = [y]';
else
yout[;output_cnt] = [output( t; y )]';
}
# Determine next step size.
if ( errmax > 6e-4 ) {
h = 0.9*h*errmax^-0.2;
else
h = 4*h;
}
}
yout = yout[;1:output_cnt];
tout = tout[1:output_cnt];
yout.cid = tout;
return yout;
};
ode4.rk4 = function( ydot; t; h; y; dydt ) {
local( k1; k2; k3; k4 );
k1 = h * dydt;
k2 = h * unlabel( ydot( t+h/2; y+k1/2 ) );
k3 = h * unlabel( ydot( t+h/2; y+k2/2 ) );
k4 = h * unlabel( ydot( t+h; y+k3 ) );
return y + ( k1 + 2.0*k2 + 2.0*k3 + k4 ) / 6.0;
};
syntax highlighted by Code2HTML, v. 0.9.1