-
Mark Jenkinson authoredMark Jenkinson authored
rungekutta.cc 3.14 KiB
/* rungekutta.cc
Mark Woolrich, FMRIB Image Analysis Group
Copyright (C) 2002 University of Oxford */
/* CCOPYRIGHT */
#include "rungekutta.h"
using namespace std;
namespace MISCMATHS {
void rk(ColumnVector& ret, const ColumnVector& y, const ColumnVector& dy, float x, float h, const Derivative& deriv,const ColumnVector& paramvalues)
{
Tracer tr("rk");
float hh=h*0.5;
float xh=x+hh;
//first step
ColumnVector yt=y+hh*dy;
//second step
ColumnVector dyt = deriv.evaluate(xh,yt,paramvalues);
yt=y+hh*dyt;
//third step
ColumnVector dym = deriv.evaluate(xh,yt,paramvalues);
yt=y+h*dym;
dym=dym+dyt;
//fourth step
dyt = deriv.evaluate(x+h,yt,paramvalues);
//addup
ret = y+h*(dy+dyt+2*dym)/6;
}
void rkqc(ColumnVector& y, float& x, float& hnext, ColumnVector& dy, float htry, float eps, const Derivative& deriv,const ColumnVector& paramvalues)
{
Tracer tr("rkqc");
float xsav = x;
ColumnVector dysav = dy;
ColumnVector ysav = y;
float h = htry;
float hdid;
ColumnVector ytemp;
while(true)
{
// take 2 1/2 step sizes
// first 1/2 step
float hh=h*0.5;
rk(ytemp,ysav,dysav,xsav,hh,deriv,paramvalues);
// second 1/2 step
x=xsav+hh;
dy = deriv.evaluate(x,ytemp,paramvalues);
rk(y,ytemp,dysav,xsav,hh,deriv,paramvalues);
x=xsav+h;
if(x==xsav) cerr << "step size too small" << endl;
// take large step size
rk(ytemp,ysav,dysav,xsav,h,deriv,paramvalues);
// eval accuracy
float errmax = 0.0;
for(int i=1; i<=y.Nrows(); i++)
{
//errmax=max(abs((y-ytemp)./y));
float tmp = fabs((y(i)-ytemp(i))/y(i));
if(tmp > errmax) errmax = tmp;
}
errmax=errmax/eps;
if(errmax <=1.0)
{
// step OK, compute step size for next step
hdid=h;
if(errmax>6e-4)
hnext=h*std::exp(-0.2*std::log(errmax));
else
hnext=4*h;
break;
}
else
{
// step too large,
h=h*std::exp(-0.25*std::log(errmax));
}
}
y = y+(y-ytemp)/15;
}
void runge_kutta(Matrix& yp, ColumnVector& xp, ColumnVector& hp, const ColumnVector& ystart, float x1, float x2, float eps, float hmin, const Derivative& deriv,const ColumnVector& paramvalues)
{
Tracer tr("runge_kutta");
int MAXSTEP=1000;
ColumnVector y = ystart;
float x=x1;
xp.ReSize(MAXSTEP,1);
xp = 0;
xp(1) =x1;
float h=hp(1);
hp.ReSize(MAXSTEP,1);
hp = 0;
yp.ReSize(MAXSTEP,y.Nrows());
yp = 0;
int kout=1;
ColumnVector dy;
for(int k=1; k <= MAXSTEP; k++)
{
dy = deriv.evaluate(x,y,paramvalues);
// store results:
xp(kout)=x;
yp.Row(kout)=y;
hp(kout)=h;
kout=kout+1;
// stop overshoot of step past x2:
if((x+h-x2)*(x+h-x1)>0) h=x2-x;
float hnext = 0.0;
rkqc(y,x,hnext,dy,h,eps,deriv,paramvalues);
if((x-x2)*(x2-x1) >= 0.0)
{
xp(kout)=x;
yp.Row(kout)=y;
hp(kout)=h;
//kout=kout+1;
xp = xp.Rows(1,kout);
yp = yp.Rows(1,kout);
return;
}
else
{
if(hnext<=hmin) cerr << "step size too small" << endl;
h=hnext;
}
}
cerr << "too many steps" << endl;
}
}