📄 rkf45.c
字号:
// 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.//// Modified://// 27 March 2004//// Author://// H A Watts and L F Shampine,// Sandia Laboratories,// Albuquerque, New Mexico.//// Reference://// E. Fehlberg,// Low-order Classical Runge-Kutta Formulas with Stepsize Control,// NASA Technical Report R-315.//// L F Shampine, H A 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/output, double Y[NEQN], the current solution vector at T.//// Input/output, double YP[NEQN], the derivative of the current solution // vector at T. The user should not set or alter this information!//// Input/output, double *T, the current value of the independent variable.//// Input, double 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, double *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_D, 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 double abserr_save = -1.0E+00; double ae; double dt; double ee; double eeoet; double eps; double esttol; double et; double *f1; double *f2; double *f3; double *f4; double *f5; static int flag_save = -1000; static double h = -1.0E+00; bool hfaild; double 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; double relerr_min; static double relerr_save = -1.0E+00; static double remin = 1.0E-12; double s; double scale; double tol; double toln; double ypk;//// Check the input parameters.// eps = d_epsilon ( ); if ( neqn < 1 ) { return 8; } if ( (*relerr) < 0.0E+00 ) { return 8; } if ( abserr < 0.0E+00 ) { 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.0E+00 ) { 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.0E+00 < 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.0E+00 * d_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 double[neqn]; f2 = new double[neqn]; f3 = new double[neqn]; f4 = new double[neqn]; f5 = new double[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 = fabs ( dt ); toln = 0.0E+00; for ( k = 0; k < neqn; k++ ) { tol = (*relerr) * fabs ( y[k] ) + abserr; if ( 0.0E+00 < tol ) { toln = tol; ypk = fabs ( yp[k] ); if ( tol < ypk * pow ( h, 5 ) ) { h = pow ( ( tol / ypk ), 0.2E+00 ); } } } if ( toln <= 0.0E+00 ) { h = 0.0E+00; } h = d_max ( h, 26.0E+00 * eps * d_max ( fabs ( *t ), fabs ( dt ) ) ); if ( flag < 0 ) { flag_save = -2; } else { flag_save = 2; } }//// Set stepsize for integration in the direction from T to TOUT.// h = d_sign ( dt ) * fabs ( h );//// Test to see if too may output points are being requested.// if ( 2.0E+00 * fabs ( dt ) <= fabs ( 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 ( fabs ( dt ) <= 26.0E+00 * eps * fabs ( *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.0E+00 / (*relerr); ae = scale * abserr;//// Step by step integration.// for ( ; ; ) { hfaild = false;//// Set the smallest allowable stepsize.// hmin = 26.0E+00 * eps * fabs ( *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.0E+00 * fabs ( h ) <= fabs ( dt ) ) { } else//// Will the next successful step complete the integration to the output point?// { if ( fabs ( dt ) <= fabs ( h ) ) { output = true; h = dt; } else { h = 0.5E+00 * 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; }//
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -