/* * * 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