📄 runge_kutta.cpp
字号:
float tol;
float toln;
float ypk;
//
// Check the input parameters.
//
eps = r4_epsilon ( );
if ( neqn < 1 )
{
return 8;
}
if ( (*relerr) < 0.0 )
{
return 8;
}
if ( abserr < 0.0 )
{
return 8;
}
if ( flag == 0 || 8 < flag || flag < -2 )
{
return 8;
}
mflag = abs ( flag );
//
// Is this a continuation call?
//
if ( mflag != 1 )
{
if ( *t == tout && kflag != 3 )
{
return 8;
}
//
// FLAG = -2 or +2:
//
if ( mflag == 2 )
{
if ( kflag == 3 )
{
flag = flag_save;
mflag = abs ( flag );
}
else if ( init == 0 )
{
flag = flag_save;
}
else if ( kflag == 4 )
{
nfe = 0;
}
else if ( kflag == 5 && abserr == 0.0 )
{
exit ( 1 );
}
else if ( kflag == 6 && (*relerr) <= relerr_save && abserr <= abserr_save )
{
exit ( 1 );
}
}
//
// FLAG = 3, 4, 5, 6, 7 or 8.
//
else
{
if ( flag == 3 )
{
flag = flag_save;
if ( kflag == 3 )
{
mflag = abs ( flag );
}
}
else if ( flag == 4 )
{
nfe = 0;
flag = flag_save;
if ( kflag == 3 )
{
mflag = abs ( flag );
}
}
else if ( flag == 5 && 0.0 < abserr )
{
flag = flag_save;
if ( kflag == 3 )
{
mflag = abs ( flag );
}
}
//
// Integration cannot be continued because the user did not respond to
// the instructions pertaining to FLAG = 5, 6, 7 or 8.
//
else
{
exit ( 1 );
}
}
}
//
// Save the input value of FLAG.
// Set the continuation flag KFLAG for subsequent input checking.
//
flag_save = flag;
kflag = 0;
//
// Save RELERR and ABSERR for checking input on subsequent calls.
//
relerr_save = (*relerr);
abserr_save = abserr;
//
// Restrict the relative error tolerance to be at least
//
// 2*EPS+REMIN
//
// to avoid limiting precision difficulties arising from impossible
// accuracy requests.
//
relerr_min = 2.0 * r4_epsilon ( ) + remin;
//
// Is the relative error tolerance too small?
//
if ( (*relerr) < relerr_min )
{
(*relerr) = relerr_min;
kflag = 3;
return 3;
}
dt = tout - *t;
//
// Initialization:
//
// Set the initialization completion indicator, INIT;
// set the indicator for too many output points, KOP;
// evaluate the initial derivatives
// set the counter for function evaluations, NFE;
// estimate the starting stepsize.
//
f1 = new float[neqn];
f2 = new float[neqn];
f3 = new float[neqn];
f4 = new float[neqn];
f5 = new float[neqn];
if ( mflag == 1 )
{
init = 0;
kop = 0;
f ( *t, y, yp );
nfe = 1;
if ( *t == tout )
{
return 2;
}
}
if ( init == 0 )
{
init = 1;
h = r4_abs ( dt );
toln = 0.0;
for ( k = 0; k < neqn; k++ )
{
tol = (*relerr) * r4_abs ( y[k] ) + abserr;
if ( 0.0 < tol )
{
toln = tol;
ypk = r4_abs ( yp[k] );
if ( tol < ypk * pow ( h, 5 ) )
{
h = ( float ) pow ( ( double ) ( tol / ypk ), 0.2 );
}
}
}
if ( toln <= 0.0 )
{
h = 0.0;
}
h = r4_max ( h, 26.0 * eps * r4_max ( r4_abs ( *t ), r4_abs ( dt ) ) );
if ( flag < 0 )
{
flag_save = -2;
}
else
{
flag_save = 2;
}
}
//
// Set stepsize for integration in the direction from T to TOUT.
//
h = r4_sign ( dt ) * r4_abs ( h );
//
// Test to see if too may output points are being requested.
//
if ( 2.0 * r4_abs ( dt ) <= r4_abs ( h ) )
{
kop = kop + 1;
}
//
// Unnecessary frequency of output.
//
if ( kop == 100 )
{
kop = 0;
delete [] f1;
delete [] f2;
delete [] f3;
delete [] f4;
delete [] f5;
return 7;
}
//
// If we are too close to the output point, then simply extrapolate and return.
//
if ( r4_abs ( dt ) <= 26.0 * eps * r4_abs ( *t ) )
{
*t = tout;
for ( i = 0; i < neqn; i++ )
{
y[i] = y[i] + dt * yp[i];
}
f ( *t, y, yp );
nfe = nfe + 1;
delete [] f1;
delete [] f2;
delete [] f3;
delete [] f4;
delete [] f5;
return 2;
}
//
// Initialize the output point indicator.
//
output = false;
//
// To avoid premature underflow in the error tolerance function,
// scale the error tolerances.
//
scale = 2.0 / (*relerr);
ae = scale * abserr;
//
// Step by step integration.
//
for ( ; ; )
{
hfaild = false;
//
// Set the smallest allowable stepsize.
//
hmin = 26.0 * eps * r4_abs ( *t );
//
// Adjust the stepsize if necessary to hit the output point.
//
// Look ahead two steps to avoid drastic changes in the stepsize and
// thus lessen the impact of output points on the code.
//
dt = tout - *t;
if ( 2.0 * r4_abs ( h ) <= r4_abs ( dt ) )
{
}
else
//
// Will the next successful step complete the integration to the output point?
//
{
if ( r4_abs ( dt ) <= r4_abs ( h ) )
{
output = true;
h = dt;
}
else
{
h = 0.5 * dt;
}
}
//
// Here begins the core integrator for taking a single step.
//
// The tolerances have been scaled to avoid premature underflow in
// computing the error tolerance function ET.
// To avoid problems with zero crossings, relative error is measured
// using the average of the magnitudes of the solution at the
// beginning and end of a step.
// The error estimate formula has been grouped to control loss of
// significance.
//
// To distinguish the various arguments, H is not permitted
// to become smaller than 26 units of roundoff in T.
// Practical limits on the change in the stepsize are enforced to
// smooth the stepsize selection process and to avoid excessive
// chattering on problems having discontinuities.
// To prevent unnecessary failures, the code uses 9/10 the stepsize
// it estimates will succeed.
//
// After a step failure, the stepsize is not allowed to increase for
// the next attempted step. This makes the code more efficient on
// problems having discontinuities and more effective in general
// since local extrapolation is being used and extra caution seems
// warranted.
//
// Test the number of derivative function evaluations.
// If okay, try to advance the integration from T to T+H.
//
for ( ; ; )
{
//
// Have we done too much work?
//
if ( MAXNFE < nfe )
{
kflag = 4;
delete [] f1;
delete [] f2;
delete [] f3;
delete [] f4;
delete [] f5;
return 4;
}
//
// Advance an approximate solution over one step of length H.
//
r4_fehl ( f, neqn, y, *t, h, yp, f1, f2, f3, f4, f5, f1 );
nfe = nfe + 5;
//
// Compute and test allowable tolerances versus local error estimates
// and remove scaling of tolerances. The relative error is
// measured with respect to the average of the magnitudes of the
// solution at the beginning and end of the step.
//
eeoet = 0.0;
for ( k = 0; k < neqn; k++ )
{
et = r4_abs ( y[k] ) + r4_abs ( f1[k] ) + ae;
if ( et <= 0.0 )
{
delete [] f1;
delete [] f2;
delete [] f3;
delete [] f4;
delete [] f5;
return 5;
}
ee = r4_abs
( ( -2090.0 * yp[k]
+ ( 21970.0 * f3[k] - 15048.0 * f4[k] )
)
+ ( 22528.0 * f2[k] - 27360.0 * f5[k] )
);
eeoet = r4_max ( eeoet, ee / et );
}
esttol = r4_abs ( h ) * eeoet * scale / 752400.0;
if ( esttol <= 1.0 )
{
break;
}
//
// Unsuccessful step. Reduce the stepsize, try again.
// The decrease is limited to a factor of 1/10.
//
hfaild = true;
output = false;
if ( esttol < 59049.0 )
{
s = 0.9 / ( float ) pow ( ( double ) esttol, 0.2 );
}
else
{
s = 0.1;
}
h = s * h;
if ( r4_abs ( h ) < hmin )
{
kflag = 6;
delete [] f1;
delete [] f2;
delete [] f3;
delete [] f4;
delete [] f5;
return 6;
}
}
//
// We exited the loop because we took a successful step.
// Store the solution for T+H, and evaluate the derivative there.
//
*t = *t + h;
for ( i = 0; i < neqn; i++ )
{
y[i] = f1[i];
}
f ( *t, y, yp );
nfe = nfe + 1;
//
// Choose the next stepsize. The increase is limited to a factor of 5.
// If the step failed, the next stepsize is not allowed to increase.
//
if ( 0.0001889568 < esttol )
{
s = 0.9 / ( float ) pow ( ( double ) esttol, 0.2 );
}
else
{
s = 5.0;
}
if ( hfaild )
{
s = r4_min ( s, 1.0 );
}
h = r4_sign ( h ) * r4_max ( s * r4_abs ( h ), hmin );
//
// End of core integrator
//
// Should we take another step?
//
if ( output )
{
*t = tout;
delete [] f1;
delete [] f2;
delete [] f3;
delete [] f4;
delete [] f5;
return 2;
}
if ( flag <= 0 )
{
delete [] f1;
delete [] f2;
delete [] f3;
delete [] f4;
delete [] f5;
return (-2);
}
}
# undef MAXNFE
}
//****************************************************************************80
float r4_sign ( float x )
//****************************************************************************80
//
// Purpose:
//
// R4_SIGN returns the sign of an R4.
//
// Licensing:
//
// This code is distributed under the GNU LGPL license.
//
// Modified:
//
// 27 March 2004
//
// Author:
//
// John Burkardt
//
// Parameters:
//
// Input, float X, the number whose sign is desired.
//
// Output, float R4_SIGN, the sign of X.
//
{
if ( x < 0.0 )
{
return ( -1.0 );
}
else
{
return ( +1.0 );
}
}
//****************************************************************************80
double r8_abs ( double x )
//****************************************************************************80
//
// Purpose:
//
// R8_ABS returns the absolute value of an R8.
//
// Licensing:
//
// This code is distributed under the GNU LGPL license.
//
// Modified:
//
// 02 April 2005
//
// Author:
//
// John Burkardt
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -