Warning, file /education/step/stepcore/eulersolver.cc was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2007 Vladimir Kuznetsov <ks.vladimir@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "eulersolver.h" 0008 #include "util.h" 0009 0010 #include <cmath> 0011 #include <cstring> 0012 0013 namespace StepCore { 0014 0015 STEPCORE_META_OBJECT(GenericEulerSolver, QT_TRANSLATE_NOOP("ObjectClass", "GenericEulerSolver"), QT_TRANSLATE_NOOP("ObjectDescription", "Generic Euler solver"), MetaObject::ABSTRACT, STEPCORE_SUPER_CLASS(Solver),) 0016 STEPCORE_META_OBJECT(EulerSolver, QT_TRANSLATE_NOOP("ObjectClass", "EulerSolver"), QT_TRANSLATE_NOOP("ObjectDescription", "Non-adaptive Euler solver"), 0, STEPCORE_SUPER_CLASS(GenericEulerSolver),) 0017 STEPCORE_META_OBJECT(AdaptiveEulerSolver, QT_TRANSLATE_NOOP("ObjectClass", "AdaptiveEulerSolver"), QT_TRANSLATE_NOOP("ObjectDescription", "Adaptive Euler solver"), 0, STEPCORE_SUPER_CLASS(GenericEulerSolver),) 0018 0019 void GenericEulerSolver::init() 0020 { 0021 _yerr.resize(_dimension); 0022 _ytemp.resize(_dimension); 0023 _ydiff.resize(_dimension); 0024 _ytempvar.resize(_dimension); 0025 _ydiffvar.resize(_dimension); 0026 } 0027 0028 void GenericEulerSolver::fini() 0029 { 0030 } 0031 0032 int GenericEulerSolver::doCalcFn(double* t, const VectorXd* y, 0033 const VectorXd* yvar, VectorXd* f, VectorXd* fvar) 0034 { 0035 int ret = _function(*t, y->data(), yvar?yvar->data():nullptr, 0036 f ? f->data() : _ydiff.data(), 0037 fvar?fvar->data():nullptr, _params); 0038 //if(f != NULL) std::memcpy(f, _ydiff, _dimension*sizeof(*f)); 0039 return ret; 0040 } 0041 0042 int GenericEulerSolver::doStep(double t, double stepSize, VectorXd* y, VectorXd* yvar) 0043 { 0044 _localError = 0; 0045 _localErrorRatio = 0; 0046 0047 //int ret = _function(t, y, _ydiff, _params); 0048 //if(ret != OK) return ret; 0049 0050 VectorXd* ytempvar = yvar ? &_ytempvar : nullptr; 0051 VectorXd* ydiffvar = yvar ? &_ydiffvar : nullptr; 0052 0053 // Error estimation: integration with timestep = stepSize 0054 _yerr = - *y - stepSize*_ydiff; 0055 // First integration with timestep = stepSize/2 0056 _ytemp = *y + (stepSize/2)*_ydiff; 0057 0058 if(yvar) { // error calculation 0059 *ytempvar = (yvar->array().sqrt().matrix()+(stepSize/2)*(*ydiffvar)).array().square().matrix(); 0060 } 0061 0062 int ret = _function(t + stepSize/2, _ytemp.data(), ytempvar?ytempvar->data():nullptr, 0063 _ydiff.data(), ydiffvar?ydiffvar->data():nullptr, _params); 0064 if(ret != OK) return ret; 0065 0066 for(int i=0; i<_dimension; ++i) { 0067 // Second integration with timestep = stepSize/2 0068 _ytemp[i] += stepSize/2*_ydiff[i]; 0069 // Error estimation and solution improve 0070 _yerr[i] += _ytemp[i]; 0071 // Solution improvement 0072 _ytemp[i] += _yerr[i]; 0073 // Maximal error calculation 0074 double error = fabs(_yerr[i]); 0075 if(error > _localError) _localError = error; 0076 // Maximal error ration calculation 0077 double errorRatio = error / (_toleranceAbs + _toleranceRel * fabs(_ytemp[i])); 0078 if(errorRatio > _localErrorRatio) _localErrorRatio = errorRatio; 0079 } 0080 0081 if(_localErrorRatio > 1.1) return ToleranceError; 0082 0083 // XXX 0084 ret = _function(t + stepSize, _ytemp.data(), ytempvar?ytempvar->data():nullptr, 0085 _ydiff.data(), ydiffvar?ydiffvar->data():nullptr, _params); 0086 if(ret != OK) return ret; 0087 0088 *y = _ytemp; 0089 0090 if(yvar) { // error calculation 0091 // XXX: Strictly speaking yerr are correlated between steps. 0092 // For now we are using the following formula which 0093 // assumes that yerr are equal and correlated on adjacent steps 0094 // TODO: improve this formula 0095 *yvar = (ytempvar->array().sqrt().matrix()+(stepSize/2)*(*ydiffvar)).array().square().matrix() 0096 + 3*_yerr.array().square().matrix(); 0097 } 0098 0099 return OK; 0100 } 0101 0102 int GenericEulerSolver::doEvolve(double* t, double t1, VectorXd* y, VectorXd* yvar) 0103 { 0104 // XXX: add better checks 0105 // XXX: replace asserts by error codes here 0106 // or by check in world 0107 // XXX: do the same checks in doStep 0108 STEPCORE_ASSERT_NOABORT(*t + _stepSize != *t); 0109 STEPCORE_ASSERT_NOABORT(*t != t1); 0110 0111 #ifndef NDEBUG 0112 double realStepSize = fmin( _stepSize, t1 - *t ); 0113 #endif 0114 0115 const double S = 0.9; 0116 int result; 0117 0118 VectorXd* ydiffvar = yvar ? &_ydiffvar : nullptr; 0119 0120 result = _function(*t, y->data(), yvar?yvar->data():nullptr, _ydiff.data(), ydiffvar?ydiffvar->data():nullptr, _params); 0121 if(result != OK) return result; 0122 0123 while(*t < t1) { 0124 STEPCORE_ASSERT_NOABORT(t1-*t > realStepSize/1000); 0125 0126 //double t11 = _stepSize < t1-*t ? *t + _stepSize : t1; 0127 double t11 = t1 - *t > _stepSize*1.01 ? *t + _stepSize : t1; 0128 result = doStep(*t, t11 - *t, y, yvar); 0129 0130 if(result != OK && result != ToleranceError) return result; 0131 0132 if(_adaptive) { 0133 if(_localErrorRatio > 1.1) { 0134 double r = S / _localErrorRatio; 0135 if(r<0.2) r = 0.2; 0136 _stepSize *= r; 0137 } else if(_localErrorRatio < 0.5) { 0138 double r = S / pow(_localErrorRatio, 0.5); 0139 if(r>5.0) r = 5.0; 0140 if(r<1.0) r = 1.0; 0141 double newStepSize = _stepSize*r; 0142 if(newStepSize < t1 - t11) _stepSize = newStepSize; 0143 } 0144 if(result != OK) { 0145 result = _function(*t, y->data(), yvar?yvar->data():nullptr, _ydiff.data(), 0146 ydiffvar?ydiffvar->data():nullptr, _params); 0147 if(result != OK) return result; 0148 continue; 0149 } 0150 } else { 0151 if(result != OK) return ToleranceError; 0152 } 0153 0154 *t = t11; 0155 } 0156 return OK; 0157 } 0158 0159 } // namespace StepCore 0160