File indexing completed on 2024-04-14 03:49:25

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