00001 // 00002 //##################################################################### 00003 // RKF45method.cpp 00004 //##################################################################### 00005 // 00006 // Class RKF45method implementation 00007 // 00008 // For Math 157 00009 // 00010 //##################################################################### 00011 // Chris Anderson (C) UCLA Jan. 22, 2009 00012 //##################################################################### 00013 // 00014 #include "RKF45method.h" 00015 #include "fortranODEfunction.h" 00016 00017 RKF45method::RKF45method() 00018 { 00019 errorTolerance = 1.0e-02; 00020 errorCode = 0; 00021 } 00022 00023 void RKF45method::setErrorTolerance(double tolerance) 00024 { 00025 errorTolerance = tolerance ; 00026 } 00027 00028 double RKF45method::getErrorTolerance() const 00029 { 00030 return errorTolerance; 00031 } 00032 00033 int RKF45method::getErrorCode() const 00034 { 00035 return errorCode; 00036 } 00037 00038 // 00039 // Prototype for the external ftc translation of rk45. 00040 // 00041 extern "C" int rkf45_(int (*f)(double*, double*, double*), long *neqn, 00042 double *y, double*t, double *tout, double *relerr, 00043 double *abserr, long *iflag, double *work, long *iwork); 00044 00045 /** 00046 @arg yk : DoubleVector containing solution at time t = tk 00047 @arg dt : Timestep 00048 @arg Fptr : Pointer to to a class that extends the VectorFuntion class 00049 \returns 00050 yk is updated with the solution value at t = tk + dt. The return value is 0 if advance is successful; 00051 otherwise the return value is the error code iflag from the rkf45 method. The value of 00052 iflag can also be obtained using the getErrorCode() member function. 00053 The following description is taken from the rkf45.f source code file. <p> 00054 00055 <pre> 00056 iflag = 3 -- integration was not completed because relative error 00057 tolerance was too small. relerr has been increased 00058 appropriately for continuing. 00059 = 4 -- integration was not completed because more than 00060 3000 derivative evaluations were needed. this 00061 is approximately 500 steps. 00062 = 5 -- integration was not completed because solution 00063 vanished making a pure relative error test 00064 impossible. must use non-zero abserr to continue. 00065 using the one-step integration mode for one step 00066 is a good way to proceed. 00067 = 6 -- integration was not completed because requested 00068 accuracy could not be achieved using smallest 00069 allowable stepsize. user must increase the error 00070 tolerance before continued integration can be 00071 attempted. 00072 = 7 -- it is likely that rkf45 is inefficient for solving 00073 this problem. too much output is restricting the 00074 natural stepsize choice. use the one-step integrator 00075 mode. 00076 = 8 -- invalid input parameters 00077 this indicator occurs if any of the following is 00078 satisfied - neqn .le. 0 00079 t=tout and iflag .ne. +1 or -1 00080 relerr or abserr .lt. 0. 00081 iflag .eq. 0 or .lt. -2 or .gt. 8 00082 </pre> 00083 00084 The errorTolerance specified is used to set both the absolute and 00085 relative error tolerence values. As dicussed in the comments in 00086 original rkf45.f, <a href="..\rkf45Comments.f">rkf45Comments.f</a>, 00087 the advance() routine should not be used with relative error control 00088 smaller than about 1.e-8. 00089 */ 00090 00091 int RKF45method::advance(DoubleVector& yk, double dt, VectorFunction* Fptr) 00092 { 00093 long i; 00094 00095 long dim = yk.getSize(); 00096 // 00097 // Use the fortranODEfunction class to provide 00098 // a function pointer interface to the ODE specified by Fptr. 00099 // 00100 fortranODEfunction::setDimension(dim); 00101 fortranODEfunction::setFunction(Fptr); 00102 int (*f)(double*, double*, double*) = fortranODEfunction::f; 00103 // 00104 // Allocate local temporary arrays 00105 // 00106 double* y = new double[dim]; 00107 double* work = new double[4 + 6*dim]; 00108 long* iwork = new long[10]; 00109 // 00110 // Specify parameters and initial condition 00111 // 00112 long neqn = dim; 00113 double t = 0.0; // We assume an autonomous system; so always 00114 double tout = dt; // start at t = 0 and go to t = dt. 00115 double relerr = errorTolerance; 00116 double abserr = errorTolerance; 00117 long iflag = 1; 00118 00119 for(i = 0; i < dim; i++) 00120 { 00121 y[i] = yk(i); 00122 } 00123 // 00124 // Call rkf45 method 00125 // 00126 rkf45_(f,&neqn,y,&t,&tout,&relerr,&abserr,&iflag,work,iwork); 00127 // 00128 // Check for error in integration 00129 // 00130 if(iflag != 2) 00131 { 00132 // clean up 00133 delete [] work; delete [] iwork; delete [] y; 00134 00135 // Set error code to iflag and return iflag value 00136 00137 errorCode = iflag; 00138 return iflag; 00139 } 00140 // 00141 // Call initialize so that ykp1 is guaranteed non-null. 00142 // 00143 00144 for(i = 0; i < dim; i++) 00145 { 00146 yk(i) = y[i]; 00147 } 00148 00149 // clean up 00150 00151 delete [] work; delete [] iwork; delete [] y; 00152 // 00153 // Set error code to 0 and return 0 for success 00154 // Note: this code value is not the iflag value. 00155 // 00156 errorCode = 0; 00157 return 0; 00158 }