Implement an embedded Runge-Kutta stepper rkstepX
(where X describes the order of the imbedded method used,
for example X=12 ("one two", not "twelve") for the midpoint-Euler method)
of your choice, which advances the solution to the equation
dy/dt = f(t,y) ,
(where y and f are vectors) by a given steph,
and estimates the error.
The interface could be like this,
void rkstepX(
double t, double h, vector* y,
void f(double t, vector* y, vector* dydt),
vector* yh, vector* err
)
where vector*y contains the current values
of the vector-function {yi(t)}, and the
user supplied right-hand-side function void f(double t,
vector*y, vector*dydt) fills-in the values
dydt[k]=fk(t,y).
The function rkstepX has to estimate the values
yk(t+h) and store them in the array
vector*yh and also estimate the errors
δyk
and store them in the array vector*err.
Implement an adaptive-step-size driver routine wchich
advances the solution from the given value of t to t=b (by calling
your rkstepX routine with appropriate step-sizes)
keeping the specified relative, eps, and absolute,
acc, precision.
The interface could be like this,
void driver(
double* t, double b, double* h, vector*y, double acc, double eps,
void stepper(
double t, double h, vector*y,
void f(double t,vector*y,vector*dydt),
vector*yh, vector*err
),
void f(double t,double*y,double*dydt),
)
In the beginning
double*t contains the startpoint a;
double*h contains an estimate of the initial stepsize;
double*y contains y(a).
double*t equals the endpoint b;
double*h contains the last accepted stepsize;
double*y contains y(b).
Demonstrate that your routines work as intended by solving some interesting systems of ordinary differential equations.
Modify your driver such that it stores the points
{ti,y1(ti),...,yn(ti)}
it calculated along the way in, for example, a matrix
[t,y1,y2,...,yn]
provided by the user (or in any other convenient object).
Use the same driver as in part-A/B, but implement a new stepper that uses the higher-order method from part-A/B and estimates the error using the Runge's principle (making a full step and two half-steps).
Compare it with the your embedded method from exercise-A. Comparing here means counting the number of right-hand-side evaluations each of the methods used to achieve the same tolerance. The method with fewest evaluations wins. (Of course if the routine failed to reach the given tolerance, it is a failure -- ikke bestået.)
Compare your implementations with your favourite library routines.
A definite integral
I = ∫abf(x)dx
can be
reformulated as an ODE,
y'=f(x), y(a)=0 : I = y(b),
which can be
solved with your adaptive ODE solver.
Implement this idea in a subroutine which calculates definite integrals based on your favourite ODE routine (to be later compared with your dedicated adaptive integrators).
Calculate some interesting integrals.