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 */