📄 runge_kutta.cpp
字号:
# include <cstdlib>
# include <iostream>
# include <iomanip>
# include <cmath>
# include <ctime>
using namespace std;
# include "rkf45.H"
//****************************************************************************80
float r4_abs ( float x )
//****************************************************************************80
//
// Purpose:
//
// R4_ABS returns the absolute value of an R4.
//
// Licensing:
//
// This code is distributed under the GNU LGPL license.
//
// Modified:
//
// 02 April 2005
//
// Author:
//
// John Burkardt
//
// Parameters:
//
// Input, float X, the quantity whose absolute value is desired.
//
// Output, float R4_ABS, the absolute value of X.
//
{
if ( 0.0 <= x )
{
return x;
}
else
{
return ( -x );
}
}
//****************************************************************************80
float r4_epsilon ( )
//****************************************************************************80
//
// Purpose:
//
// R4_EPSILON returns the round off unit for floating arithmetic.
//
// Discussion:
//
// R4_EPSILON is a number R which is a power of 2 with the property that,
// to the precision of the computer's arithmetic,
// 1 < 1 + R
// but
// 1 = ( 1 + R / 2 )
//
// Licensing:
//
// This code is distributed under the GNU LGPL license.
//
// Modified:
//
// 06 May 2003
//
// Author:
//
// John Burkardt
//
// Parameters:
//
// Output, float R4_EPSILON, the floating point round-off unit.
//
{
float r;
r = 1.0;
while ( 1.0 < ( float ) ( 1.0 + r ) )
{
r = r / 2.0;
}
return ( 2.0 * r );
}
//****************************************************************************80
void r4_fehl ( void f ( float t, float y[], float yp[] ), int neqn,
float y[], float t, float h, float yp[], float f1[], float f2[], float f3[],
float f4[], float f5[], float s[] )
//****************************************************************************80
//
// Purpose:
//
// R4_FEHL takes one Fehlberg fourth-fifth order step.
//
// Discussion:
//
// This version of the routine uses FLOAT real arithmetic.
//
// This routine integrates a system of NEQN first order ordinary differential
// equations of the form
// dY(i)/dT = F(T,Y(1:NEQN))
// where the initial values Y and the initial derivatives
// YP are specified at the starting point T.
//
// The routine advances the solution over the fixed step H and returns
// the fifth order (sixth order accurate locally) solution
// approximation at T+H in array S.
//
// The formulas have been grouped to control loss of significance.
// The routine should be called with an H not smaller than 13 units of
// roundoff in T so that the various independent arguments can be
// distinguished.
//
// Licensing:
//
// This code is distributed under the GNU LGPL license.
//
// Modified:
//
// 27 March 2004
//
// Author:
//
// Original FORTRAN77 version by Herman Watts, Lawrence Shampine.
// C++ version by John Burkardt
//
// Reference:
//
// Erwin Fehlberg,
// Low-order Classical Runge-Kutta Formulas with Stepsize Control,
// NASA Technical Report R-315, 1969.
//
// Lawrence Shampine, Herman Watts, S Davenport,
// Solving Non-stiff Ordinary Differential Equations - The State of the Art,
// SIAM Review,
// Volume 18, pages 376-411, 1976.
//
// Parameters:
//
// Input, external F, a user-supplied subroutine to evaluate the
// derivatives Y'(T), of the form:
//
// void f ( double t, double y[], double yp[] )
//
// Input, int NEQN, the number of equations to be integrated.
//
// Input, float Y[NEQN], the current value of the dependent variable.
//
// Input, float T, the current value of the independent variable.
//
// Input, float H, the step size to take.
//
// Input, float YP[NEQN], the current value of the derivative of the
// dependent variable.
//
// Output, float F1[NEQN], F2[NEQN], F3[NEQN], F4[NEQN], F5[NEQN], derivative
// values needed for the computation.
//
// Output, float S[NEQN], the estimate of the solution at T+H.
//
{
float ch;
int i;
ch = h / 4.0;
for ( i = 0; i < neqn; i++ )
{
f5[i] = y[i] + ch * yp[i];
}
f ( t + ch, f5, f1 );
ch = 3.0 * h / 32.0;
for ( i = 0; i < neqn; i++ )
{
f5[i] = y[i] + ch * ( yp[i] + 3.0 * f1[i] );
}
f ( t + 3.0 * h / 8.0, f5, f2 );
ch = h / 2197.0;
for ( i = 0; i < neqn; i++ )
{
f5[i] = y[i] + ch *
( 1932.0 * yp[i]
+ ( 7296.0 * f2[i] - 7200.0 * f1[i] )
);
}
f ( t + 12.0 * h / 13.0, f5, f3 );
ch = h / 4104.0;
for ( i = 0; i < neqn; i++ )
{
f5[i] = y[i] + ch *
(
( 8341.0 * yp[i] - 845.0 * f3[i] )
+ ( 29440.0 * f2[i] - 32832.0 * f1[i] )
);
}
f ( t + h, f5, f4 );
ch = h / 20520.0;
for ( i = 0; i < neqn; i++ )
{
f1[i] = y[i] + ch *
(
( -6080.0 * yp[i]
+ ( 9295.0 * f3[i] - 5643.0 * f4[i] )
)
+ ( 41040.0 * f1[i] - 28352.0 * f2[i] )
);
}
f ( t + h / 2.0, f1, f5 );
//
// Ready to compute the approximate solution at T+H.
//
ch = h / 7618050.0;
for ( i = 0; i < neqn; i++ )
{
s[i] = y[i] + ch *
(
( 902880.0 * yp[i]
+ ( 3855735.0 * f3[i] - 1371249.0 * f4[i] ) )
+ ( 3953664.0 * f2[i] + 277020.0 * f5[i] )
);
}
return;
}
//****************************************************************************80
float r4_max ( float x, float y )
//****************************************************************************80
//
// Purpose:
//
// R4_MAX returns the maximum of two R4's.
//
// Licensing:
//
// This code is distributed under the GNU LGPL license.
//
// Modified:
//
// 10 January 2002
//
// Author:
//
// John Burkardt
//
// Parameters:
//
// Input, float X, Y, the quantities to compare.
//
// Output, float R4_MAX, the maximum of X and Y.
//
{
if ( y < x )
{
return x;
}
else
{
return y;
}
}
//****************************************************************************80
float r4_min ( float x, float y )
//****************************************************************************80
//
// Purpose:
//
// R4_MIN returns the minimum of two R4's.
//
// Licensing:
//
// This code is distributed under the GNU LGPL license.
//
// Modified:
//
// 09 May 2003
//
// Author:
//
// John Burkardt
//
// Parameters:
//
// Input, float X, Y, the quantities to compare.
//
// Output, float R4_MIN, the minimum of X and Y.
//
{
if ( y < x )
{
return y;
}
else
{
return x;
}
}
//****************************************************************************80
int r4_rkf45 ( void f ( float t, float y[], float yp[] ), int neqn,
float y[], float yp[], float *t, float tout, float *relerr, float abserr,
int flag )
//****************************************************************************80
//
// Purpose:
//
// R4_RKF45 carries out the Runge-Kutta-Fehlberg method.
//
// Discussion:
//
// This version of the routine uses FLOAT real arithmetic.
//
// This routine is primarily designed to solve non-stiff and mildly stiff
// differential equations when derivative evaluations are inexpensive.
// It should generally not be used when the user is demanding
// high accuracy.
//
// This routine integrates a system of NEQN first-order ordinary differential
// equations of the form:
//
// dY(i)/dT = F(T,Y(1),Y(2),...,Y(NEQN))
//
// where the Y(1:NEQN) are given at T.
//
// Typically the subroutine is used to integrate from T to TOUT but it
// can be used as a one-step integrator to advance the solution a
// single step in the direction of TOUT. On return, the parameters in
// the call list are set for continuing the integration. The user has
// only to call again (and perhaps define a new value for TOUT).
//
// Before the first call, the user must
//
// * supply the subroutine F(T,Y,YP) to evaluate the right hand side;
// and declare F in an EXTERNAL statement;
//
// * initialize the parameters:
// NEQN, Y(1:NEQN), T, TOUT, RELERR, ABSERR, FLAG.
// In particular, T should initially be the starting point for integration,
// Y should be the value of the initial conditions, and FLAG should
// normally be +1.
//
// Normally, the user only sets the value of FLAG before the first call, and
// thereafter, the program manages the value. On the first call, FLAG should
// normally be +1 (or -1 for single step mode.) On normal return, FLAG will
// have been reset by the program to the value of 2 (or -2 in single
// step mode), and the user can continue to call the routine with that
// value of FLAG.
//
// (When the input magnitude of FLAG is 1, this indicates to the program
// that it is necessary to do some initialization work. An input magnitude
// of 2 lets the program know that that initialization can be skipped,
// and that useful information was computed earlier.)
//
// The routine returns with all the information needed to continue
// the integration. If the integration reached TOUT, the user need only
// define a new TOUT and call again. In the one-step integrator
// mode, returning with FLAG = -2, the user must keep in mind that
// each step taken is in the direction of the current TOUT. Upon
// reaching TOUT, indicated by the output value of FLAG switching to 2,
// the user must define a new TOUT and reset FLAG to -2 to continue
// in the one-step integrator mode.
//
// In some cases, an error or difficulty occurs during a call. In that case,
// the output value of FLAG is used to indicate that there is a problem
// that the user must address. These values include:
//
// * 3, integration was not completed because the input value of RELERR, the
// relative error tolerance, was too small. RELERR has been increased
// appropriately for continuing. If the user accepts the output value of
// RELERR, then simply reset FLAG to 2 and continue.
//
// * 4, integration was not completed because more than MAXNFE derivative
// evaluations were needed. This is approximately (MAXNFE/6) steps.
// The user may continue by simply calling again. The function counter
// will be reset to 0, and another MAXNFE function evaluations are allowed.
//
// * 5, integration was not completed because the solution vanished,
// making a pure relative error test impossible. The user must use
// a non-zero ABSERR to continue. Using the one-step integration mode
// for one step is a good way to proceed.
//
// * 6, integration was not completed because the requested accuracy
// could not be achieved, even using the smallest allowable stepsize.
// The user must increase the error tolerances ABSERR or RELERR before
// continuing. It is also necessary to reset FLAG to 2 (or -2 when
// the one-step integration mode is being used). The occurrence of
// FLAG = 6 indicates a trouble spot. The solution is changing
// rapidly, or a singularity may be present. It often is inadvisable
// to continue.
//
// * 7, it is likely that this routine is inefficient for solving
// this problem. Too much output is restricting the natural stepsize
// choice. The user should use the one-step integration mode with
// the stepsize determined by the code. If the user insists upon
// continuing the integration, reset FLAG to 2 before calling
// again. Otherwise, execution will be terminated.
//
// * 8, invalid input parameters, indicates one of the following:
// NEQN <= 0;
// T = TOUT and |FLAG| /= 1;
// RELERR < 0 or ABSERR < 0;
// FLAG == 0 or FLAG < -2 or 8 < FLAG.
//
// Licensing:
//
// This code is distributed under the GNU LGPL license.
//
// Modified:
//
// 27 March 2004
//
// Author:
//
// Original FORTRAN77 version by Herman Watts, Lawrence Shampine.
// C++ version by John Burkardt.
//
// Reference:
//
// Erwin Fehlberg,
// Low-order Classical Runge-Kutta Formulas with Stepsize Control,
// NASA Technical Report R-315, 1969.
//
// Lawrence Shampine, Herman Watts, S Davenport,
// Solving Non-stiff Ordinary Differential Equations - The State of the Art,
// SIAM Review,
// Volume 18, pages 376-411, 1976.
//
// Parameters:
//
// Input, external F, a user-supplied subroutine to evaluate the
// derivatives Y'(T), of the form:
//
// void f ( float t, float y[], float yp[] )
//
// Input, int NEQN, the number of equations to be integrated.
//
// Input/output, float Y[NEQN], the current solution vector at T.
//
// Input/output, float YP[NEQN], the derivative of the current solution
// vector at T. The user should not set or alter this information!
//
// Input/output, float *T, the current value of the independent variable.
//
// Input, float TOUT, the output point at which solution is desired.
// TOUT = T is allowed on the first call only, in which case the routine
// returns with FLAG = 2 if continuation is possible.
//
// Input, float *RELERR, ABSERR, the relative and absolute error tolerances
// for the local error test. At each step the code requires:
// abs ( local error ) <= RELERR * abs ( Y ) + ABSERR
// for each component of the local error and the solution vector Y.
// RELERR cannot be "too small". If the routine believes RELERR has been
// set too small, it will reset RELERR to an acceptable value and return
// immediately for user action.
//
// Input, int FLAG, indicator for status of integration. On the first call,
// set FLAG to +1 for normal use, or to -1 for single step mode. On
// subsequent continuation steps, FLAG should be +2, or -2 for single
// step mode.
//
// Output, int RKF45_S, indicator for status of integration. A value of 2
// or -2 indicates normal progress, while any other value indicates a
// problem that should be addressed.
//
{
# define MAXNFE 3000
static float abserr_save = -1.0;
float ae;
float dt;
float ee;
float eeoet;
float eps;
float esttol;
float et;
float *f1;
float *f2;
float *f3;
float *f4;
float *f5;
static int flag_save = -1000;
static float h = -1.0;
bool hfaild;
float hmin;
int i;
static int init = -1000;
int k;
static int kflag = -1000;
static int kop = -1;
int mflag;
static int nfe = -1;
bool output;
float relerr_min;
static float relerr_save = -1.0;
static float remin = 1.0E-12;
float s;
float scale;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -