00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "eulersolver.h"
00020 #include "util.h"
00021
00022 #include <cmath>
00023 #include <cstring>
00024
00025 namespace StepCore {
00026
00027 STEPCORE_META_OBJECT(GenericEulerSolver, "Generic Euler solver", MetaObject::ABSTRACT, STEPCORE_SUPER_CLASS(Solver),)
00028 STEPCORE_META_OBJECT(EulerSolver, "Non-adaptive Euler solver", 0, STEPCORE_SUPER_CLASS(GenericEulerSolver),)
00029 STEPCORE_META_OBJECT(AdaptiveEulerSolver, "Adaptive Euler solver", 0, STEPCORE_SUPER_CLASS(GenericEulerSolver),)
00030
00031 void GenericEulerSolver::init()
00032 {
00033 _yerr = new double[_dimension];
00034 _ytemp = new double[_dimension];
00035 _ydiff = new double[_dimension];
00036 _ytempvar = new double[_dimension];
00037 _ydiffvar = new double[_dimension];
00038 }
00039
00040 void GenericEulerSolver::fini()
00041 {
00042 delete[] _ydiffvar;
00043 delete[] _ytempvar;
00044 delete[] _ydiff;
00045 delete[] _ytemp;
00046 delete[] _yerr;
00047 }
00048
00049 int GenericEulerSolver::doCalcFn(double* t, const double* y,
00050 const double* yvar, double* f, double* fvar)
00051 {
00052 int ret = _function(*t, y, yvar, f ? f : _ydiff, fvar, _params);
00053
00054 return ret;
00055 }
00056
00057 int GenericEulerSolver::doStep(double t, double stepSize, double* y, double* yvar)
00058 {
00059 _localError = 0;
00060 _localErrorRatio = 0;
00061
00062
00063
00064
00065 double* ytempvar = yvar ? _ytempvar : 0;
00066 double* ydiffvar = yvar ? _ydiffvar : 0;
00067
00068 for(int i=0; i<_dimension; ++i) {
00069
00070 _yerr[i] = - y[i] - stepSize*_ydiff[i];
00071
00072 _ytemp[i] = y[i] + stepSize*_ydiff[i]/2;
00073 }
00074
00075 if(yvar) {
00076 for(int i=0; i<_dimension; ++i)
00077 ytempvar[i] = square(sqrt(yvar[i])+ydiffvar[i]*stepSize/2);
00078 }
00079
00080 int ret = _function(t + stepSize/2, _ytemp, ytempvar, _ydiff, ydiffvar, _params);
00081 if(ret != OK) return ret;
00082
00083 for(int i=0; i<_dimension; ++i) {
00084
00085 _ytemp[i] += stepSize/2*_ydiff[i];
00086
00087 _yerr[i] += _ytemp[i];
00088
00089 _ytemp[i] += _yerr[i];
00090
00091 double error = fabs(_yerr[i]);
00092 if(error > _localError) _localError = error;
00093
00094 double errorRatio = error / (_toleranceAbs + _toleranceRel * fabs(_ytemp[i]));
00095 if(errorRatio > _localErrorRatio) _localErrorRatio = errorRatio;
00096 }
00097
00098 if(_localErrorRatio > 1.1) return ToleranceError;
00099
00100
00101 ret = _function(t + stepSize, _ytemp, ytempvar, _ydiff, ydiffvar, _params);
00102 if(ret != OK) return ret;
00103
00104 std::memcpy(y, _ytemp, _dimension*sizeof(*y));
00105
00106 if(yvar) {
00107
00108
00109
00110
00111 for(int i=0; i<_dimension; ++i)
00112 yvar[i] = square(sqrt(ytempvar[i])+ydiffvar[i]*stepSize/2)
00113 + 3*square(_yerr[i]);
00114 }
00115
00116 return OK;
00117 }
00118
00119 int GenericEulerSolver::doEvolve(double* t, double t1, double* y, double* yvar)
00120 {
00121
00122
00123
00124
00125 STEPCORE_ASSERT_NOABORT(*t + _stepSize != *t);
00126 STEPCORE_ASSERT_NOABORT(*t != t1);
00127
00128 #ifndef NDEBUG
00129 double realStepSize = fmin( _stepSize, t1 - *t );
00130 #endif
00131
00132 const double S = 0.9;
00133 int result;
00134
00135 double* ydiffvar = yvar ? _ydiffvar : 0;
00136
00137 result = _function(*t, y, yvar, _ydiff, ydiffvar, _params);
00138 if(result != OK) return result;
00139
00140 while(*t < t1) {
00141 STEPCORE_ASSERT_NOABORT(t1-*t > realStepSize/1000);
00142
00143
00144 double t11 = t1 - *t > _stepSize*1.01 ? *t + _stepSize : t1;
00145 result = doStep(*t, t11 - *t, y, yvar);
00146
00147 if(result != OK && result != ToleranceError) return result;
00148
00149 if(_adaptive) {
00150 if(_localErrorRatio > 1.1) {
00151 double r = S / _localErrorRatio;
00152 if(r<0.2) r = 0.2;
00153 _stepSize *= r;
00154 } else if(_localErrorRatio < 0.5) {
00155 double r = S / pow(_localErrorRatio, 0.5);
00156 if(r>5.0) r = 5.0;
00157 if(r<1.0) r = 1.0;
00158 double newStepSize = _stepSize*r;
00159 if(newStepSize < t1 - t11) _stepSize = newStepSize;
00160 }
00161 if(result != OK) {
00162 result = _function(*t, y, yvar, _ydiff, ydiffvar, _params);
00163 if(result != OK) return result;
00164 continue;
00165 }
00166 } else {
00167 if(result != OK) return ToleranceError;
00168 }
00169
00170 *t = t11;
00171 }
00172 return OK;
00173 }
00174
00175 }
00176