ode-toolbox

ODE integration tools
git clone https://git.0xfab.ch/ode-toolbox.git
Log | Files | Refs | README | LICENSE

RKV56.h (7870B)


      1 /* File:   RKV56.h */
      2 /* Date:   Thu Feb 11 11:36:11 2016 */
      3 /* Author: Fabian Wermelinger */
      4 /* Tag:    Runge-Kutta-Verner 5th-6th order variable step method */
      5 /* Copyright 2016 ETH Zurich. All Rights Reserved. */
      6 #ifndef RKV56_H_A6VN39JN
      7 #define RKV56_H_A6VN39JN
      8 
      9 #include <ODETB/TimeStepper/explicit/Euler.h>
     10 
     11 #include <cassert>
     12 #include <cmath>
     13 #include <cstdlib>
     14 #include <iostream>
     15 #include <limits>
     16 
     17 template <typename Tinput, typename Trhs>
     18 class RKV56 : public Euler<Tinput, Trhs>
     19 {
     20     using Euler<Tinput, Trhs>::m_settings;
     21     using Euler<Tinput, Trhs>::m_U;
     22     using Euler<Tinput, Trhs>::m_rhs;
     23     using Euler<Tinput, Trhs>::m_kernel;
     24 
     25     Real m_errold;
     26     const Real m_alpha;
     27     const Real m_beta;
     28     bool m_reject;
     29     bool m_firstPass;
     30 
     31     Trhs m_rhs2;
     32     Trhs m_rhs3;
     33     Trhs m_rhs4;
     34     Trhs m_rhs5;
     35     Trhs m_rhs6;
     36     Trhs m_rhs7;
     37     Trhs m_rhs8;
     38 
     39     Tinput m_output;
     40 
     41     static constexpr Real b1  = 3./40., b3 = 875./2244., b4 = 23./72., b5 = 264./1955., b7 = 125./11592., b8 = 43./616.;
     42     static constexpr Real c21 = 1./6., ct2 = 1./6.;
     43     static constexpr Real c31 = 4./75., c32 = 16./75., ct3 = 4./15.;
     44     static constexpr Real c41 = 5./6., c42 = -8./3., c43 = 2.5, ct4 = 2./3.;
     45     static constexpr Real c51 = -165./64., c52 = 55./6., c53 = -425./64., c54 = 85./96., ct5 = 5./6.;
     46     static constexpr Real c61 = 2.4, c62 = -8., c63 = 4015./612., c64 = -11./36., c65 = 88./255., ct6 = 1.0;
     47     static constexpr Real c71 = -8263./15000., c72 = 124./75., c73 = -643./680., c74 = -81./250., c75 = 2484./10625., ct7 = 1./15.;
     48     static constexpr Real c81 = 3501./1720., c82 = -300./43., c83 = 297275./52632., c84 = -319./2322., c85 = 24068./84065., c87 = 3850./26703., ct8 = 1.0;
     49     static constexpr Real e1  = -1./160., e3 = -125./17952., e4 = 1./144., e5 = -12./1955., e6 = -3./44., e7 = 125./11592., e8 = 43./616.;
     50 
     51     void _computeRHS(const Real t, const Real dt, void const* const data)
     52     {
     53         if (!m_reject) m_kernel->compute(m_U, m_rhs, t, data);
     54         m_kernel->compute(m_U + dt*(c21*m_rhs), m_rhs2, t + ct2*dt, data);
     55         m_kernel->compute(m_U + dt*(c31*m_rhs + c32*m_rhs2), m_rhs3, t + ct3*dt, data);
     56         m_kernel->compute(m_U + dt*(c41*m_rhs + c42*m_rhs2 + c43*m_rhs3), m_rhs4, t + ct4*dt, data);
     57         m_kernel->compute(m_U + dt*(c51*m_rhs + c52*m_rhs2 + c53*m_rhs3 + c54*m_rhs4), m_rhs5, t + ct5*dt, data);
     58         m_kernel->compute(m_U + dt*(c61*m_rhs + c62*m_rhs2 + c63*m_rhs3 + c64*m_rhs4 + c65*m_rhs5), m_rhs6, t + ct6*dt, data);
     59         m_kernel->compute(m_U + dt*(c71*m_rhs + c72*m_rhs2 + c73*m_rhs3 + c74*m_rhs4 + c75*m_rhs5), m_rhs7, t + ct7*dt, data);
     60         m_kernel->compute(m_U + dt*(c81*m_rhs + c82*m_rhs2 + c83*m_rhs3 + c84*m_rhs4 + c85*m_rhs5 + c87*m_rhs7), m_rhs8, t + ct8*dt, data);
     61     }
     62 
     63     inline Real _error() const
     64     {
     65         assert(m_U.size() == m_rhs.size());
     66         Real maxerr = 0.0;
     67         // using inf-norm (could also do L2-norm)
     68         for (size_t i = 0; i < m_U.size(); ++i)
     69         {
     70             const typename Trhs::DataType E = e1*m_rhs[i] + e3*m_rhs3[i] + e4*m_rhs4[i] + e5*m_rhs5[i] + e6*m_rhs6[i] + e7*m_rhs7[i] + e8*m_rhs8[i];
     71             for (int j = 0; j < Trhs::DataType::SIZE; ++j)
     72             {
     73                 const Real IepsI = std::abs(E[j]);
     74                 const Real scale = m_settings.aTol + std::abs(m_U[i][j])*m_settings.rTol;
     75                 maxerr = std::max(maxerr, IepsI/scale);
     76             }
     77         }
     78         assert(!isnan(maxerr));
     79         return maxerr;
     80     }
     81 
     82     bool _inBound(const Real t, Real& dt, void const* const data, Real& dt_next)
     83     {
     84         const Real err = _error();
     85 
     86         const Real dtMin = m_settings.dtMin;
     87         if (dt <= dtMin) {
     88             dt_next = 1.1 * dtMin;
     89             m_errold = std::max(err, static_cast<Real>(1.0e-4));
     90             m_reject = false;
     91             m_firstPass = true;
     92             return true;
     93         }
     94 
     95         if (dt < std::numeric_limits<Real>::epsilon())
     96         {
     97             // if you request the impossible
     98             dt_next = 1.5*std::numeric_limits<Real>::epsilon();
     99             m_errold = std::max(err, static_cast<Real>(1.0e-4));
    100             m_reject = false;
    101             m_firstPass = true;
    102             return true;
    103         }
    104 
    105         const Real safety = m_settings.safety;
    106         const Real minScale = m_settings.minScale;
    107         const Real maxScale = m_settings.maxScale;
    108         Real scale;
    109         if (err <= 1.0)
    110         {
    111             if (err == 0.0)
    112                 scale = maxScale;
    113             else
    114             {
    115                 scale = safety*std::pow(err,-m_alpha)*std::pow(m_errold,m_beta);
    116                 scale = (scale < minScale) ? minScale : ((scale > maxScale) ? maxScale : scale);
    117             }
    118             if (m_reject)
    119                 dt_next = dt*std::min(scale, static_cast<Real>(1.0));
    120             else
    121                 dt_next = dt*scale;
    122             m_errold = std::max(err, static_cast<Real>(1.0e-4));
    123             m_reject = false;
    124             m_firstPass = true;
    125             return true;
    126         }
    127         else
    128         {
    129             scale = safety*std::pow(err,-m_alpha);
    130             scale = std::max(scale, minScale);
    131             dt *= scale;
    132             m_reject = true;
    133             m_firstPass = false;
    134             _computeRHS(t, dt, data);
    135             return false;
    136         }
    137     }
    138 
    139     void _dumpOutput(const void * const data)
    140     {
    141         const Real t  = m_settings.t;
    142         const Real dt = m_settings.dt;
    143         Real& tDump   = m_settings.tDump;
    144 
    145         const Tinput Uold = m_output;
    146         const Trhs rhsOld = m_rhs;
    147 
    148         m_kernel->compute(m_U, m_rhs, t+dt, data);
    149         m_firstPass = false;
    150 
    151         while(tDump <= (t+dt))
    152         {
    153             const Real phi = (tDump - t)/dt;
    154             // Hermite 3rd order
    155             m_output = (1.0-phi)*Uold + phi*m_U + phi*(phi-1.0)*((1.0-2.0*phi)*(m_U - Uold) + (phi-1.0)*dt*rhsOld + phi*dt*m_rhs);
    156             m_kernel->write(m_settings.step, t+phi*dt, phi*dt, m_output, data);
    157             tDump += m_settings.dtDump;
    158         }
    159     }
    160 
    161 public:
    162     RKV56(Tinput& U, StepperSettings& S, KernelBase<Tinput,Trhs> * const kern) :
    163         Euler<Tinput,Trhs>(U,S,kern), m_errold(1.0e-4), m_alpha(0.25*(S.alpha-0.75*S.beta)), m_beta(0.25*S.beta), m_reject(false), m_firstPass(true),
    164         m_rhs2(U.size()), m_rhs3(U.size()), m_rhs4(U.size()), m_rhs5(U.size()), m_rhs6(U.size()), m_rhs7(U.size()), m_rhs8(U.size()), m_output(U.size()) {}
    165     virtual ~RKV56() {}
    166 
    167     virtual void step(void const* const data=nullptr)
    168     {
    169         size_t& step       = m_settings.step;
    170         Real& t            = m_settings.t;
    171         Real& dt           = m_settings.dt;
    172         const Real& tFinal = m_settings.tFinal;
    173         Real& tDump        = m_settings.tDump;
    174         if (!m_settings.bRestart && m_settings.dtDump > 0.0) tDump = t + m_settings.dtDump;
    175 
    176         while(t < tFinal)
    177         {
    178             Real dt_next;
    179             dt = (tFinal-t) < dt ? (tFinal-t) : dt;
    180             _computeRHS(t, dt, data);
    181             while (!_inBound(t, dt, data, dt_next)) {}
    182             m_output = m_U;
    183             m_U += dt*(b1*m_rhs + b3*m_rhs3 + b4*m_rhs4 + b5*m_rhs5 + b7*m_rhs7 + b8*m_rhs8);
    184             if (m_settings.dtDump > 0.0)
    185             {
    186                 if (tDump <= (t+dt)) _dumpOutput(data);
    187             }
    188             else
    189                 if ((step+1) % m_settings.writeGranularity == 0)
    190                     m_kernel->write((step+1), t+dt, dt, m_U, data);
    191             t += dt;
    192             dt = dt_next;
    193             ++step;
    194 
    195             if (m_settings.step % m_settings.reportGranularity == 0)
    196                 std::printf(
    197                     "Time = %e;\tStep = %zu\n", m_settings.t, m_settings.step);
    198 
    199             if (m_settings.restartstep > 0 && step % m_settings.restartstep == 0)
    200                 serialize<Tinput>(m_U, m_settings);
    201         }
    202     }
    203 };
    204 
    205 #endif /* RKV56_H_A6VN39JN */