Skip to content
Snippets Groups Projects
rungekutta.cc 3.11 KiB
Newer Older
Mark Woolrich's avatar
Mark Woolrich committed
/*  rungekutta.cc

    Mark Woolrich, FMRIB Image Analysis Group

    Copyright (C) 2002 University of Oxford  */

/*  CCOPYRIGHT  */

#include "rungekutta.h"

using namespace std;
using namespace NEWMAT;
Mark Woolrich's avatar
Mark Woolrich committed
namespace MISCMATHS {

void rk(ColumnVector& ret, const ColumnVector& y, const ColumnVector& dy, float x, float h, const Derivative& deriv,const ColumnVector& paramvalues)
Paul McCarthy's avatar
Paul McCarthy committed
{
  Tracer tr("rk");
Mark Woolrich's avatar
Mark Woolrich committed

  float hh=h*0.5;
  float xh=x+hh;
Paul McCarthy's avatar
Paul McCarthy committed

Mark Woolrich's avatar
Mark Woolrich committed
  //first step
  ColumnVector yt=y+hh*dy;
Paul McCarthy's avatar
Paul McCarthy committed

Mark Woolrich's avatar
Mark Woolrich committed
  //second step
  ColumnVector dyt = deriv.evaluate(xh,yt,paramvalues);
  yt=y+hh*dyt;
Paul McCarthy's avatar
Paul McCarthy committed

Mark Woolrich's avatar
Mark Woolrich committed
  //third step
  ColumnVector dym = deriv.evaluate(xh,yt,paramvalues);
  yt=y+h*dym;
  dym=dym+dyt;
Paul McCarthy's avatar
Paul McCarthy committed

Mark Woolrich's avatar
Mark Woolrich committed
  //fourth step
  dyt = deriv.evaluate(x+h,yt,paramvalues);
Paul McCarthy's avatar
Paul McCarthy committed

Mark Woolrich's avatar
Mark Woolrich committed
  //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)
{
Paul McCarthy's avatar
Paul McCarthy committed
  Tracer tr("rkqc");
Mark Woolrich's avatar
Mark Woolrich committed

  float xsav = x;
  ColumnVector dysav = dy;
  ColumnVector ysav = y;
  float h = htry;
  float hdid;
  ColumnVector ytemp;

  while(true)
    {
      // take 2 1/2 step sizes
Paul McCarthy's avatar
Paul McCarthy committed

Mark Woolrich's avatar
Mark Woolrich committed
      // first 1/2 step
      float hh=h*0.5;

      rk(ytemp,ysav,dysav,xsav,hh,deriv,paramvalues);
Paul McCarthy's avatar
Paul McCarthy committed

Mark Woolrich's avatar
Mark Woolrich committed
      // 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;
Mark Woolrich's avatar
Mark Woolrich committed

      // take large step size
      rk(ytemp,ysav,dysav,xsav,h,deriv,paramvalues);
Paul McCarthy's avatar
Paul McCarthy committed

Mark Woolrich's avatar
Mark Woolrich committed
      // eval accuracy
      float errmax = 0.0;
      for(int i=1; i<=y.Nrows(); i++)
	{
	  //errmax=max(abs((y-ytemp)./y));
Paul McCarthy's avatar
Paul McCarthy committed

Mark Woolrich's avatar
Mark Woolrich committed
	  float tmp = fabs((y(i)-ytemp(i))/y(i));
	  if(tmp > errmax) errmax = tmp;
	}

      errmax=errmax/eps;
Paul McCarthy's avatar
Paul McCarthy committed

      if(errmax <=1.0)
Mark Woolrich's avatar
Mark Woolrich committed
	{
	  // step OK, compute step size for next step
	  hdid=h;
Paul McCarthy's avatar
Paul McCarthy committed

Mark Woolrich's avatar
Mark Woolrich committed
	  if(errmax>6e-4)
	    hnext=h*std::exp(-0.2*std::log(errmax));
	  else
	    hnext=4*h;
Paul McCarthy's avatar
Paul McCarthy committed

Mark Woolrich's avatar
Mark Woolrich committed
	  break;
      }
Paul McCarthy's avatar
Paul McCarthy committed
      else
Mark Woolrich's avatar
Mark Woolrich committed
	{
	  // 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)
{
Paul McCarthy's avatar
Paul McCarthy committed
  Tracer tr("runge_kutta");
Mark Woolrich's avatar
Mark Woolrich committed

  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;
Paul McCarthy's avatar
Paul McCarthy committed

Mark Woolrich's avatar
Mark Woolrich committed
  ColumnVector dy;

  for(int k=1; k <= MAXSTEP; k++)
Paul McCarthy's avatar
Paul McCarthy committed
    {
Mark Woolrich's avatar
Mark Woolrich committed
      dy = deriv.evaluate(x,y,paramvalues);

      // store results:
      xp(kout)=x;
      yp.Row(kout)=y;
      hp(kout)=h;
Paul McCarthy's avatar
Paul McCarthy committed

Mark Woolrich's avatar
Mark Woolrich committed
      kout=kout+1;
Paul McCarthy's avatar
Paul McCarthy committed

Mark Woolrich's avatar
Mark Woolrich committed
      // 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;
Paul McCarthy's avatar
Paul McCarthy committed

Mark Woolrich's avatar
Mark Woolrich committed
        xp = xp.Rows(1,kout);
	yp = yp.Rows(1,kout);

	return;
      }
      else
      {
	if(hnext<=hmin) cerr << "step size too small" << endl;
Mark Woolrich's avatar
Mark Woolrich committed
	h=hnext;
  cerr << "too many steps" << endl;