/*
*
* This file is part of the Virtual Leaf.
*
* The Virtual Leaf is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Virtual Leaf is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Virtual Leaf. If not, see .
*
* Copyright 2010 Roeland Merks.
*
*/
#include
#include
#include
#include "rungekutta.h"
#include "warning.h"
#include "maxmin.h"
static const std::string _module_id("$Id$");
// The value Errcon equals (5/Safety) raised to the power (1/PGrow), see use below.
/*
static float maxarg1,maxarg2;
#define FMAX(a,b) (maxarg1=(a),maxarg2=(b),(maxarg1) > (maxarg2) ?\
(maxarg1) : (maxarg2))
static float minarg1,minarg2;
#define FMIN(a,b) (minarg1=(a),minarg2=(b),(minarg1) < (minarg2) ?\
(minarg1) : (minarg2))
#define SIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
*/
const double RungeKutta::Safety = 0.9;
const double RungeKutta::PGrow = -0.2;
const double RungeKutta::Pshrnk = -0.25;
const double RungeKutta::Errcon = 1.89e-4;
const double RungeKutta::Maxstp = 10000;
const double RungeKutta::Tiny = 1.0e-30;
void RungeKutta::rkqs(double *y, double *dydx, int n, double *x, double htry, double eps,
double *yscal, double *hdid, double *hnext)
/* Fifth-order Runge-Kutta step with monitoring of local truncation error to ensure accuracy and
adjust stepsize. Input are the dependent variable vector y[1..n] and its derivative dydx[1..n]
at the starting value of the independent variable x. Also input are the stepsize to be attempted
htry, the required accuracy eps, and the vector yscal[1..n] against which the error is
scaled. On output, y and x are replaced by their new values, hdid is the stepsize that was
actuallyac complished, and hnext is the estimated next stepsize. derivs is the user-supplied
routine that computes the right-hand side derivatives. */
{
int i;
double errmax,h,htemp,xnew,*yerr,*ytemp;
yerr=new double[n];
ytemp=new double[n];
h=htry; // Set stepsize to the initial trial value.
for (;;) {
rkck(y,dydx,n,*x,h,ytemp,yerr); // Take a step.
errmax=0.0; //Evaluate accuracy.
for (i=0;i= 0.0 ? FMAX(htemp,0.1*h) : FMIN(htemp,0.1*h));
//No more than a factor of 10.
xnew=(*x)+h;
if (xnew == *x) MyWarning::error("stepsize underflow in rkqs, with h = %f and htry = %f",h, htry);
}
if (errmax > Errcon) {
*hnext=Safety*h*pow(errmax,PGrow);
//std::cerr << "hnext = " << *hnext << std::endl;
}
else *hnext=5.0*h; //No more than a factor of 5 increase.
*x += (*hdid=h);
for (i=0;i 0) xsav=x-dxsav*2.0; //Assures storage of first step.
for (nstp=0;nstp 0 && kount < kmax-1 && fabs(x-xsav) > fabs(dxsav)) {
xp[kount]=x; //Store intermediate results.
for (i=0;i 0.0) h=x2-x;// If stepsize can overshoot, decrease.
rkqs(y,dydx,nvar,&x,h,eps,yscal,&hdid,&hnext);
if (hdid == h) ++(*nok); else ++(*nbad);
if ((x-x2)*(x2-x1) >= 0.0) { //Are we done?
for (i=0;i