ode-toolbox

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

commit f3947228c46bcd49b18f7e05a85a9a3767086d24
parent 1c50b530ffe3e5dc4e75aa95e0b3e466408f3f17
Author: Fabian Wermelinger <fabianw@mavt.ethz.ch>
Date:   Fri, 18 Dec 2020 20:38:44 +0100

Rename includes

Diffstat:
DGnuplotDump.h | 110-------------------------------------------------------------------------------
DTimeStepper/StepperBase.h | 235-------------------------------------------------------------------------------
DTimeStepper/TimeStepper.h | 23-----------------------
DTimeStepper/explicit/Euler.h | 37-------------------------------------
DTimeStepper/explicit/LSRK3.h | 56--------------------------------------------------------
DTimeStepper/explicit/RK4.h | 53-----------------------------------------------------
DTimeStepper/explicit/variableStep/RKF45.h | 187-------------------------------------------------------------------------------
DTimeStepper/explicit/variableStep/RKV56.h | 194-------------------------------------------------------------------------------
DTimeStepper/implicit/BDF.h | 76----------------------------------------------------------------------------
DTimeStepper/orderVerification/orderVerification.cpp | 145-------------------------------------------------------------------------------
RArgumentParser.h -> include/ArgumentParser.h | 0
Ainclude/GnuplotDump.h | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
RTimeStepper/KernelBase.h -> include/TimeStepper/KernelBase.h | 0
Ainclude/TimeStepper/StepperBase.h | 235+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/TimeStepper/TimeStepper.h | 24++++++++++++++++++++++++
Ainclude/TimeStepper/explicit/Euler.h | 38++++++++++++++++++++++++++++++++++++++
Ainclude/TimeStepper/explicit/LSRK3.h | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/TimeStepper/explicit/RK4.h | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/TimeStepper/explicit/variableStep/RKF45.h | 188+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/TimeStepper/explicit/variableStep/RKV56.h | 195+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/TimeStepper/implicit/BDF.h | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rcommon.h -> include/common.h | 0
RTimeStepper/orderVerification/.gitignore -> test/orderVerification/.gitignore | 0
RTimeStepper/orderVerification/Makefile -> test/orderVerification/Makefile | 0
Atest/orderVerification/orderVerification.cpp | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
25 files changed, 1130 insertions(+), 1116 deletions(-)

diff --git a/GnuplotDump.h b/GnuplotDump.h @@ -1,110 +0,0 @@ -/* File: GnuplotDump.h */ -/* Date: Tue Mar 1 21:07:49 2016 */ -/* Author: Fabian Wermelinger */ -/* Tag: Gnuplot Dumper */ -/* Copyright 2016 ETH Zurich. All Rights Reserved. */ -#ifndef GNUPLOTDUMP_H_2VNNTIUB -#define GNUPLOTDUMP_H_2VNNTIUB - -#ifdef _FLOAT_PRECISION_ -using Real = float; -#else -using Real = double; -#endif - -#ifdef _GNUPLOT_DOUBLE_ -using GPfloat = double; -#else -using GPfloat = float; -#endif - -class GnuplotDump -{ -private: - const int m_ftype; - std::string m_fname; - std::ofstream m_file; - size_t m_N; - - void _writeASCII(const size_t step, const Real t, const Real dt, const Real * const src, const size_t N) - { - if (m_file.good()) - { - m_file << step << '\t' << t << '\t' << dt; - for (size_t i = 0; i < N; ++i) - m_file << '\t' << src[i]; - m_file << std::endl; - m_N = N+3; - } - } - - void _writeBIN(const size_t step, const GPfloat t, const GPfloat dt, const Real * const src, size_t N) - { - if (1024 < N) - { - std::cerr << "GnuplotDump: WARNING -- Max. Column width for BIN dump is 1024" << std::endl; - N = 1024; - } - GPfloat buf[1024+3]; - - buf[0] = static_cast<GPfloat>(step); - buf[1] = t; - buf[2] = dt; - for (size_t i = 0; i < N; ++i) - buf[i+3] = static_cast<GPfloat>(src[i]); - - if (m_file.good()) - { - m_file.write((const char*)(&buf[0]), (N+3)*sizeof(GPfloat)); - m_N = N+3; - } - } - - void _writeGPscript() const - { - std::ofstream out(m_fname+".gp"); - if (m_ftype == ASCII) - out << "plot '" << m_fname+".dat" << "' using 1:2 with lines" << std::endl; - else - { - out << "plot '" << m_fname+".bin" << "' binary format='\%" << m_N; - if (sizeof(GPfloat) == 4) - out << "float"; - else - out << "double"; - out << "' using 1:2 with lines" << std::endl; - } - out.close(); - } - -public: - GnuplotDump(const std::string fname="gnuplot", const int type=BIN, const size_t pos=0) : m_ftype(type), m_fname(fname) - { - if (m_ftype == ASCII) - { - m_file.open(m_fname+".dat", std::ios_base::out|std::ios_base::app); - m_file.setf(std::ios::scientific, std::ios::floatfield); - } - else - m_file.open(m_fname+".bin", std::ios_base::out|std::ios_base::binary|std::ios_base::app); - - if (pos > 0) m_file.seekp(pos); - } - ~GnuplotDump() - { - m_file.close(); - _writeGPscript(); - } - - enum {ASCII, BIN}; - - void write(const size_t step, const Real t, const Real dt, const Real * const src, const size_t N) - { - if (m_ftype == ASCII) - _writeASCII(step, t, dt, src, N); - else - _writeBIN(step, t, dt, src, N); - } -}; - -#endif /* GNUPLOTDUMP_H_2VNNTIUB */ diff --git a/TimeStepper/StepperBase.h b/TimeStepper/StepperBase.h @@ -1,235 +0,0 @@ -// File : StepperBase.h -// Date : Thu Sep 22 13:27:19 2016 -// Author : Fabian Wermelinger -// Description: Time stepper base class -// Copyright 2016 ETH Zurich. All Rights Reserved. -#ifndef STEPPERBASE_H_FCCSM4HL -#define STEPPERBASE_H_FCCSM4HL - -#include "KernelBase.h" - -// forward declarations -template <typename Tinput, typename Trhs=Tinput> -class StepperBase; -template <typename Tinput, typename Trhs=Tinput> -class Euler; -template <typename Tinput, typename Trhs=Tinput> -class RK4; -template <typename Tinput, typename Trhs=Tinput> -class LSRK3; -template <typename Tinput, typename Trhs=Tinput> -class RKF45; -template <typename Tinput, typename Trhs=Tinput> -class RKV56; -#ifdef _USE_SUNDIALS_ -template <typename Tinput, typename Trhs=Tinput> -class BDF; -#endif /* _USE_SUNDIALS_ */ - -#ifdef _FLOAT_PRECISION_ -using Real = float; -#else -using Real = double; -#endif - -struct StepperSettings -{ - StepperSettings(ArgumentParser& p) - { - aTol = p("-ts_aTol").asDouble(1.0e-8); - rTol = p("-ts_rTol").asDouble(1.0e-8); - minScale = p("-ts_minScale").asDouble(0.2); - maxScale = p("-ts_maxScale").asDouble(10.0); - alpha = p("-ts_alpha").asDouble(1.0); - beta = p("-ts_beta").asDouble(0.0); - safety = p("-ts_safety").asDouble(0.9); - t = p("-ts_t0").asDouble(0.0); - dt = p("-ts_dt").asDouble(1.0e-4); - step = 0; - nsteps = p("-ts_nsteps").asInt(0); - p.set_strict_mode(); - tFinal = p("-ts_tend").asDouble(); - p.unset_strict_mode(); - dtDump = p("-ts_dtDump").asDouble(-1.0); - tDump = t; - writeGranularity = p("-ts_writeGranularity").asInt(1); - writeCount = 0; - reportGranularity= p("-ts_reportGranularity").asInt(100); - bFixedStep = true; - - // restarts - bRestart = p("-ts_restart").asBool(false); - restartstep = p("-ts_restart_step").asInt(0); - - } - - Real aTol; - Real rTol; - Real minScale; - Real maxScale; - Real alpha; - Real beta; - Real safety; - Real t; - Real dt; - size_t step; - size_t nsteps; - Real tFinal; - Real dtDump; - Real tDump; - size_t writeGranularity; - size_t writeCount; - size_t reportGranularity; - bool bFixedStep; - - // restarts - bool bRestart; - int restartstep; - - - // helper - void print() const - { - printf("Time Stepper :\n"); - printf("\tAbsolute tolerance = %e\n", aTol); - printf("\tRelative tolerance = %e\n", rTol); - printf("\tMinimum time step scale = %e\n", minScale); - printf("\tMaximum time step scale = %e\n", maxScale); - printf("\tError PI control alpha = %e\n", alpha); - printf("\tError PI control beta = %e\n", beta); - } - - template <typename Tinput, typename Trhs=Tinput> - StepperBase<Tinput,Trhs>* stepperFactory(ArgumentParser& p, Tinput& U, KernelBase<Tinput,Trhs> * const kern=nullptr) - { - assert(kern != nullptr); - - if (p("-ts").asString("rkv56") == "euler") - { - cout << "Allocating Euler time stepper..." << endl; - return new Euler<Tinput, Trhs>(U, *this, kern); - } - else if (p("-ts").asString("rkv56") == "rk4") - { - cout << "Allocating RK4 time stepper..." << endl; - return new RK4<Tinput, Trhs>(U, *this, kern); - } - else if (p("-ts").asString("rkv56") == "lsrk3") - { - cout << "Allocating LSRK3 time stepper..." << endl; - return new LSRK3<Tinput, Trhs>(U, *this, kern); - } - else if (p("-ts").asString("rkv56") == "rkf45") - { - cout << "Allocating RKF45 adaptive time stepper..." << endl; - this->bFixedStep = false; - this->print(); - return new RKF45<Tinput, Trhs>(U, *this, kern); - } - else if (p("-ts").asString("rkv56") == "rkv56") - { - cout << "Allocating RKV56 adaptive time stepper..." << endl; - this->bFixedStep = false; - this->print(); - return new RKV56<Tinput, Trhs>(U, *this, kern); - } -#ifdef _USE_SUNDIALS_ - else if (p("-ts").asString("rkv56") == "bdf") - { - cout << "Allocating Sundials BDF/Newton implicit time stepper..." << endl; - return new BDF<Tinput, Trhs>(U, *this, kern); - } -#endif /* _USE_SUNDIALS_ */ - else - return nullptr; - } -}; - - -template <typename Tinput, typename Trhs> -class StepperBase -{ - StepperBase(Tinput const& rhs) = delete; - StepperBase& operator=(Tinput const& rhs) = delete; - -protected: - StepperSettings& m_settings; - Tinput& m_U; - KernelBase<Tinput,Trhs> * const m_kernel; - -public: - StepperBase(Tinput& U, StepperSettings& settings, KernelBase<Tinput,Trhs> * const kern=nullptr) : m_settings(settings), m_U(U), m_kernel(kern) { } - virtual ~StepperBase() { } - - inline Tinput& U() { return m_U; } - inline Tinput const& U() const { return m_U; } - - virtual void step(void const* const data=nullptr) = 0; - - inline void write(const size_t step, const Real t, const Real dt, const Tinput& U, const void * const data=nullptr) - { - m_kernel->write(step, t, dt, U, data); - } - - inline void dispose() { delete m_kernel; } -}; - -// serializer -struct _simulation_state_t -{ - Real t; - Real dt; - Real tFinal; - Real dtDump; - Real tDump; - size_t step; - size_t nsteps; - size_t writeCount; -} __attribute__((packed)); - - -template <typename Tinput> -void serialize(const Tinput& U, const StepperSettings& S, const std::string fname="restart.bin") -{ - _simulation_state_t state; - state.t = S.t; - state.dt = S.dt; - state.tFinal = S.tFinal; - state.dtDump = S.dtDump; - state.tDump = S.tDump; - state.step = S.step; - state.nsteps = S.nsteps; - state.writeCount = S.writeCount; - - ofstream _stream(fname, ios::out | ios::binary); - _stream.write((const char*)&state.t, sizeof(_simulation_state_t)); - - const char* const _src = (const char*)U.data(); - _stream.write(_src, sizeof(typename Tinput::DataType)*U.size()); - - _stream.close(); -} - -template <typename Tinput> -void deserialize(Tinput& U, StepperSettings& S, const std::string fname="restart.bin") -{ - ifstream _stream(fname, ios::in | ios::binary); - - _simulation_state_t state; - _stream.read((char*)&state.t, sizeof(_simulation_state_t)); - S.t = state.t; - S.dt = state.dt; - S.tFinal = state.tFinal; - S.dtDump = state.dtDump; - S.tDump = state.tDump; - S.step = state.step; - S.nsteps = state.nsteps; - S.writeCount = state.writeCount; - - char* const _dst = (char*)U.data(); - _stream.read(_dst, sizeof(typename Tinput::DataType)*U.size()); - - _stream.close(); -} - -#endif /* STEPPERBASE_H_FCCSM4HL */ diff --git a/TimeStepper/TimeStepper.h b/TimeStepper/TimeStepper.h @@ -1,23 +0,0 @@ -// File : TimeStepper.h -// Date : Thu Sep 22 13:23:57 2016 -// Author : Fabian Wermelinger -// Description: Structure to setup a time stepper -// Copyright 2016 ETH Zurich. All Rights Reserved. -#ifndef TIMESTEPPER_H_XFR2JEZJ -#define TIMESTEPPER_H_XFR2JEZJ - -#include <cstdio> -#include "ArgumentParser.h" -#include "KernelBase.h" -#include "StepperBase.h" - -#include "explicit/Euler.h" -#include "explicit/RK4.h" -#include "explicit/LSRK3.h" -#include "explicit/variableStep/RKF45.h" -#include "explicit/variableStep/RKV56.h" -#ifdef _USE_SUNDIALS_ -#include "implicit/BDF.h" -#endif /* _USE_SUNDIALS_ */ - -#endif /* TIMESTEPPER_H_XFR2JEZJ */ diff --git a/TimeStepper/explicit/Euler.h b/TimeStepper/explicit/Euler.h @@ -1,37 +0,0 @@ -/* File: Euler.h */ -/* Date: Fri Feb 5 18:57:57 2016 */ -/* Author: Fabian Wermelinger */ -/* Tag: 1st order Euler */ -/* Copyright 2016 ETH Zurich. All Rights Reserved. */ -#ifndef EULER_H_8R2CXYR0 -#define EULER_H_8R2CXYR0 - -#include <cstdio> -#include "StepperBase.h" - -template <typename Tinput, typename Trhs> -class Euler : public StepperBase<Tinput,Trhs> -{ -protected: - using StepperBase<Tinput,Trhs>::m_settings; - using StepperBase<Tinput,Trhs>::m_U; - using StepperBase<Tinput,Trhs>::m_kernel; - Trhs m_rhs; - -public: - Euler(Tinput& U, StepperSettings& S, KernelBase<Tinput,Trhs> * const kern) : StepperBase<Tinput,Trhs>(U,S,kern), m_rhs(U.size()) { } - virtual ~Euler() {} - - virtual void step(void const* const data=nullptr) - { - m_kernel->compute(m_U, m_rhs, m_settings.t, data); - m_U += static_cast<Real>(m_settings.dt)*m_rhs; - m_settings.t += m_settings.dt; - ++(m_settings.step); - - if (m_settings.step % m_settings.reportGranularity == 0) - std::printf("Time = %e;\tStep = %d\n", m_settings.t, m_settings.step); - } -}; - -#endif /* EULER_H_8R2CXYR0 */ diff --git a/TimeStepper/explicit/LSRK3.h b/TimeStepper/explicit/LSRK3.h @@ -1,56 +0,0 @@ -/* File: LSRK3.h */ -/* Date: Fri Feb 5 18:58:48 2016 */ -/* Author: Fabian Wermelinger */ -/* Tag: 3rd order low-storage Runge-Kutta. Requires a compatible kernel */ -/* Copyright 2016 ETH Zurich. All Rights Reserved. */ -#ifndef LSRK3_H_MJ6ACLH4 -#define LSRK3_H_MJ6ACLH4 - -#include "explicit/Euler.h" - -template <typename Tinput, typename Trhs> -class LSRK3 : public Euler<Tinput, Trhs> -{ - const Real m_A[3]; - const Real m_B[3]; - using Euler<Tinput,Trhs>::m_settings; - using Euler<Tinput,Trhs>::m_U; - using Euler<Tinput,Trhs>::m_rhs; - using Euler<Tinput,Trhs>::m_kernel; - -public: - LSRK3(Tinput& U, StepperSettings& S, KernelBase<Tinput,Trhs> * const kern) : - Euler<Tinput,Trhs>(U,S,kern), - // m_A{0.0, -2.915492524638791, -0.000000093517376}, // Gottlieb & Shu (Optimal LSRK3 for CFL = 0.32) - // m_B{0.924574000000000, 0.287713063186749, 0.626538109512740} - m_A{ 0.0, -17./32, -32./27}, - m_B{1./4, 8./9, 3./4} - { } - virtual ~LSRK3() {} - - virtual void step(void const* const data=nullptr) - { - // stage 1 - Real lsrk_data[2] = {static_cast<Real>(m_settings.dt), m_A[0]}; - m_kernel->compute(m_U, m_rhs, m_settings.t, lsrk_data); - m_U += m_B[0]*m_rhs; - - // stage 2 - lsrk_data[1] = m_A[1]; - m_kernel->compute(m_U, m_rhs, m_settings.t, lsrk_data); - m_U += m_B[1]*m_rhs; - - // stage 3 - lsrk_data[1] = m_A[2]; - m_kernel->compute(m_U, m_rhs, m_settings.t, lsrk_data); - m_U += m_B[2]*m_rhs; - - m_settings.t += m_settings.dt; - ++(m_settings.step); - - if (m_settings.step % m_settings.reportGranularity == 0) - std::printf("Time = %e;\tStep = %d\n", m_settings.t, m_settings.step); - } -}; - -#endif /* LSRK3_H_MJ6ACLH4 */ diff --git a/TimeStepper/explicit/RK4.h b/TimeStepper/explicit/RK4.h @@ -1,53 +0,0 @@ -/* File: RK4.h */ -/* Date: Fri Feb 5 19:14:44 2016 */ -/* Author: Fabian Wermelinger */ -/* Tag: Classical 4th order Runge-Kutta */ -/* Copyright 2016 ETH Zurich. All Rights Reserved. */ -#ifndef RK4_H_LROUVHDE -#define RK4_H_LROUVHDE - -#include "explicit/Euler.h" - -template <typename Tinput, typename Trhs> -class RK4 : public Euler<Tinput, Trhs> -{ - using Euler<Tinput, Trhs>::m_settings; - using Euler<Tinput, Trhs>::m_U; - using Euler<Tinput, Trhs>::m_rhs; - using Euler<Tinput, Trhs>::m_kernel; - -public: - RK4(Tinput& U, StepperSettings& S, KernelBase<Tinput,Trhs> * const kern) : Euler<Tinput,Trhs>(U,S,kern) { } - virtual ~RK4() {} - - virtual void step(void const* const data=nullptr) - { - const Real oneHalf = static_cast<Real>(0.5); - const Real oneThird = static_cast<Real>(1./3.); - Tinput tmpU = m_U; - - // stage 1 - m_kernel->compute(tmpU, m_rhs, m_settings.t, data); - m_U += oneHalf*oneThird*m_settings.dt*m_rhs; - - // stage 2 - m_kernel->compute(tmpU + oneHalf*m_settings.dt*m_rhs, m_rhs, m_settings.t + oneHalf*m_settings.dt, data); - m_U += oneThird*m_settings.dt*m_rhs; - - // stage 3 - m_kernel->compute(tmpU + oneHalf*m_settings.dt*m_rhs, m_rhs, m_settings.t + oneHalf*m_settings.dt, data); - m_U += oneThird*m_settings.dt*m_rhs; - - // stage 4 - m_kernel->compute(tmpU + m_settings.dt*m_rhs, m_rhs, m_settings.t + m_settings.dt, data); - m_U += oneHalf*oneThird*m_settings.dt*m_rhs; - - m_settings.t += m_settings.dt; - ++(m_settings.step); - - if (m_settings.step % m_settings.reportGranularity == 0) - std::printf("Time = %e;\tStep = %d\n", m_settings.t, m_settings.step); - } -}; - -#endif /* RK4_H_LROUVHDE */ diff --git a/TimeStepper/explicit/variableStep/RKF45.h b/TimeStepper/explicit/variableStep/RKF45.h @@ -1,187 +0,0 @@ -/* File: RKF45.h */ -/* Date: Wed Feb 10 17:33:42 2016 */ -/* Author: Fabian Wermelinger */ -/* Tag: Runge-Kutta-Fehlberg 4th-5th order variable step method */ -/* Copyright 2016 ETH Zurich. All Rights Reserved. */ -#ifndef RKF45_H_UWN6WM1V -#define RKF45_H_UWN6WM1V - -#include <cmath> -#include <cstdlib> -#include <limits> - -#include "explicit/Euler.h" - -template <typename Tinput, typename Trhs> -class RKF45 : public Euler<Tinput, Trhs> -{ - using Euler<Tinput, Trhs>::m_settings; - using Euler<Tinput, Trhs>::m_U; - using Euler<Tinput, Trhs>::m_rhs; - using Euler<Tinput, Trhs>::m_kernel; - - Real m_errold; - const Real m_alpha; - const Real m_beta; - bool m_reject; - bool m_firstPass; - - Trhs m_rhs2; - Trhs m_rhs3; - Trhs m_rhs4; - Trhs m_rhs5; - Trhs m_rhs6; - - Tinput m_output; - - static constexpr Real b1 = 16./135., b3 = 6656./12825., b4 = 28561./56430., b5 = -9./50., b6 = 2./55.; - static constexpr Real c21 = 0.25, ct2 = 0.25; - static constexpr Real c31 = 3./32., c32 = 9./32., ct3 = 3./8.; - static constexpr Real c41 = 1932./2197., c42 = -7200./2197., c43 = 7296./2197., ct4 = 12./13.; - static constexpr Real c51 = 439./216., c52 = -8., c53 = 3680./513., c54 = -845./4104., ct5 = 1.0; - static constexpr Real c61 = -8./27., c62 = 2., c63 = -3544./2565., c64 = 1859./4104., c65 = -11./40., ct6 = 0.5; - static constexpr Real e1 = 1./360., e3 = -128./4275., e4 = -2197./75240., e5 = 1./50., e6 = 2./55.; - - void _computeRHS(const Real t, const Real dt, void const* const data) - { - if (!m_reject) m_kernel->compute(m_U, m_rhs, t, data); - m_kernel->compute(m_U + dt*(c21*m_rhs), m_rhs2, t + ct2*dt, data); - m_kernel->compute(m_U + dt*(c31*m_rhs + c32*m_rhs2), m_rhs3, t + ct3*dt, data); - m_kernel->compute(m_U + dt*(c41*m_rhs + c42*m_rhs2 + c43*m_rhs3), m_rhs4, t + ct4*dt, data); - m_kernel->compute(m_U + dt*(c51*m_rhs + c52*m_rhs2 + c53*m_rhs3 + c54*m_rhs4), m_rhs5, t + ct5*dt, data); - 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); - } - - inline Real _error() const - { - assert(m_U.size() == m_rhs.size()); - Real maxerr = 0.0; - // using inf-norm (could also do L2-norm) - for (size_t i = 0; i < m_U.size(); ++i) - { - 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]; - for (int j = 0; j < Trhs::DataType::SIZE; ++j) - { - const Real IepsI = std::abs(E[j]); - const Real scale = m_settings.aTol + std::abs(m_U[i][j])*m_settings.rTol; - maxerr = std::max(maxerr, IepsI/scale); - } - } - assert(!isnan(maxerr)); - return maxerr; - } - - bool _inBound(const Real t, Real& dt, void const* const data, Real& dt_next) - { - const Real err = _error(); - - if (dt < std::numeric_limits<Real>::epsilon()) - { - // if you request the impossible - dt_next = 1.5*std::numeric_limits<Real>::epsilon(); - m_errold = std::max(err, static_cast<Real>(1.0e-4)); - m_reject = false; - m_firstPass = true; - return true; - } - - const Real safety = m_settings.safety; - const Real minScale = m_settings.minScale; - const Real maxScale = m_settings.maxScale; - Real scale; - if (err <= 1.0) - { - if (err == 0.0) - scale = maxScale; - else - { - scale = safety*std::pow(err,-m_alpha)*std::pow(m_errold,m_beta); - scale = (scale < minScale) ? minScale : ((scale > maxScale) ? maxScale : scale); - } - if (m_reject) - dt_next = dt*std::min(scale, static_cast<Real>(1.0)); - else - dt_next = dt*scale; - m_errold = std::max(err, static_cast<Real>(1.0e-4)); - m_reject = false; - m_firstPass = true; - return true; - } - else - { - scale = safety*std::pow(err,-m_alpha); - scale = std::max(scale, minScale); - dt *= scale; - m_reject = true; - m_firstPass = false; - _computeRHS(t, dt, data); - return false; - } - } - - void _dumpOutput(const void * const data) - { - const Real t = m_settings.t; - const Real dt = m_settings.dt; - Real& tDump = m_settings.tDump; - - const Tinput Uold = m_output; - const Trhs rhsOld = m_rhs; - - m_kernel->compute(m_U, m_rhs, t+dt, data); - m_firstPass = false; - - while(tDump <= (t+dt)) - { - const Real phi = (tDump - t)/dt; - // Hermite 3rd order - 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); - m_kernel->write(m_settings.step, t+phi*dt, phi*dt, m_output, data); - tDump += m_settings.dtDump; - } - } - -public: - RKF45(Tinput& U, StepperSettings& S, KernelBase<Tinput,Trhs> * const kern) : - 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), - m_rhs2(U.size()), m_rhs3(U.size()), m_rhs4(U.size()), m_rhs5(U.size()), m_rhs6(U.size()), m_output(U.size()) {} - virtual ~RKF45() {} - - virtual void step(void const* const data=nullptr) - { - size_t& step = m_settings.step; - Real& t = m_settings.t; - Real& dt = m_settings.dt; - const Real& tFinal = m_settings.tFinal; - Real& tDump = m_settings.tDump; - if (!m_settings.bRestart && m_settings.dtDump > 0.0) tDump = t + m_settings.dtDump; - - while(t < tFinal) - { - Real dt_next; - dt = (tFinal-t) < dt ? (tFinal-t) : dt; - _computeRHS(t, dt, data); - while (!_inBound(t, dt, data, dt_next)) {} - m_output = m_U; - m_U += dt*(b1*m_rhs + b3*m_rhs3 + b4*m_rhs4 + b5*m_rhs5 + b6*m_rhs6); - if (m_settings.dtDump > 0.0) - { - if (tDump <= (t+dt)) _dumpOutput(data); - } - else - if ((step+1) % m_settings.writeGranularity == 0) - m_kernel->write((step+1), t+dt, dt, m_U, data); - t += dt; - dt = dt_next; - ++step; - - if (m_settings.step % m_settings.reportGranularity == 0) - std::printf("Time = %e;\tStep = %d\n", m_settings.t, m_settings.step); - - if (m_settings.restartstep > 0 && step % m_settings.restartstep == 0) - serialize<Tinput>(m_U, m_settings); - } - } -}; - -#endif /* RKF45_H_UWN6WM1V */ diff --git a/TimeStepper/explicit/variableStep/RKV56.h b/TimeStepper/explicit/variableStep/RKV56.h @@ -1,194 +0,0 @@ -/* File: RKV56.h */ -/* Date: Thu Feb 11 11:36:11 2016 */ -/* Author: Fabian Wermelinger */ -/* Tag: Runge-Kutta-Verner 5th-6th order variable step method */ -/* Copyright 2016 ETH Zurich. All Rights Reserved. */ -#ifndef RKV56_H_A6VN39JN -#define RKV56_H_A6VN39JN - -#include <iostream> -#include <cmath> -#include <cstdlib> -#include <limits> - -#include "explicit/Euler.h" - -template <typename Tinput, typename Trhs> -class RKV56 : public Euler<Tinput, Trhs> -{ - using Euler<Tinput, Trhs>::m_settings; - using Euler<Tinput, Trhs>::m_U; - using Euler<Tinput, Trhs>::m_rhs; - using Euler<Tinput, Trhs>::m_kernel; - - Real m_errold; - const Real m_alpha; - const Real m_beta; - bool m_reject; - bool m_firstPass; - - Trhs m_rhs2; - Trhs m_rhs3; - Trhs m_rhs4; - Trhs m_rhs5; - Trhs m_rhs6; - Trhs m_rhs7; - Trhs m_rhs8; - - Tinput m_output; - - static constexpr Real b1 = 3./40., b3 = 875./2244., b4 = 23./72., b5 = 264./1955., b7 = 125./11592., b8 = 43./616.; - static constexpr Real c21 = 1./6., ct2 = 1./6.; - static constexpr Real c31 = 4./75., c32 = 16./75., ct3 = 4./15.; - static constexpr Real c41 = 5./6., c42 = -8./3., c43 = 2.5, ct4 = 2./3.; - static constexpr Real c51 = -165./64., c52 = 55./6., c53 = -425./64., c54 = 85./96., ct5 = 5./6.; - static constexpr Real c61 = 2.4, c62 = -8., c63 = 4015./612., c64 = -11./36., c65 = 88./255., ct6 = 1.0; - static constexpr Real c71 = -8263./15000., c72 = 124./75., c73 = -643./680., c74 = -81./250., c75 = 2484./10625., ct7 = 1./15.; - static constexpr Real c81 = 3501./1720., c82 = -300./43., c83 = 297275./52632., c84 = -319./2322., c85 = 24068./84065., c87 = 3850./26703., ct8 = 1.0; - static constexpr Real e1 = -1./160., e3 = -125./17952., e4 = 1./144., e5 = -12./1955., e6 = -3./44., e7 = 125./11592., e8 = 43./616.; - - void _computeRHS(const Real t, const Real dt, void const* const data) - { - if (!m_reject) m_kernel->compute(m_U, m_rhs, t, data); - m_kernel->compute(m_U + dt*(c21*m_rhs), m_rhs2, t + ct2*dt, data); - m_kernel->compute(m_U + dt*(c31*m_rhs + c32*m_rhs2), m_rhs3, t + ct3*dt, data); - m_kernel->compute(m_U + dt*(c41*m_rhs + c42*m_rhs2 + c43*m_rhs3), m_rhs4, t + ct4*dt, data); - m_kernel->compute(m_U + dt*(c51*m_rhs + c52*m_rhs2 + c53*m_rhs3 + c54*m_rhs4), m_rhs5, t + ct5*dt, data); - 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); - 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); - 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); - } - - inline Real _error() const - { - assert(m_U.size() == m_rhs.size()); - Real maxerr = 0.0; - // using inf-norm (could also do L2-norm) - for (size_t i = 0; i < m_U.size(); ++i) - { - 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]; - for (int j = 0; j < Trhs::DataType::SIZE; ++j) - { - const Real IepsI = std::abs(E[j]); - const Real scale = m_settings.aTol + std::abs(m_U[i][j])*m_settings.rTol; - maxerr = std::max(maxerr, IepsI/scale); - } - } - assert(!isnan(maxerr)); - return maxerr; - } - - bool _inBound(const Real t, Real& dt, void const* const data, Real& dt_next) - { - const Real err = _error(); - - if (dt < std::numeric_limits<Real>::epsilon()) - { - // if you request the impossible - dt_next = 1.5*std::numeric_limits<Real>::epsilon(); - m_errold = std::max(err, static_cast<Real>(1.0e-4)); - m_reject = false; - m_firstPass = true; - return true; - } - - const Real safety = m_settings.safety; - const Real minScale = m_settings.minScale; - const Real maxScale = m_settings.maxScale; - Real scale; - if (err <= 1.0) - { - if (err == 0.0) - scale = maxScale; - else - { - scale = safety*std::pow(err,-m_alpha)*std::pow(m_errold,m_beta); - scale = (scale < minScale) ? minScale : ((scale > maxScale) ? maxScale : scale); - } - if (m_reject) - dt_next = dt*std::min(scale, static_cast<Real>(1.0)); - else - dt_next = dt*scale; - m_errold = std::max(err, static_cast<Real>(1.0e-4)); - m_reject = false; - m_firstPass = true; - return true; - } - else - { - scale = safety*std::pow(err,-m_alpha); - scale = std::max(scale, minScale); - dt *= scale; - m_reject = true; - m_firstPass = false; - _computeRHS(t, dt, data); - return false; - } - } - - void _dumpOutput(const void * const data) - { - const Real t = m_settings.t; - const Real dt = m_settings.dt; - Real& tDump = m_settings.tDump; - - const Tinput Uold = m_output; - const Trhs rhsOld = m_rhs; - - m_kernel->compute(m_U, m_rhs, t+dt, data); - m_firstPass = false; - - while(tDump <= (t+dt)) - { - const Real phi = (tDump - t)/dt; - // Hermite 3rd order - 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); - m_kernel->write(m_settings.step, t+phi*dt, phi*dt, m_output, data); - tDump += m_settings.dtDump; - } - } - -public: - RKV56(Tinput& U, StepperSettings& S, KernelBase<Tinput,Trhs> * const kern) : - 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), - 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()) {} - virtual ~RKV56() {} - - virtual void step(void const* const data=nullptr) - { - size_t& step = m_settings.step; - Real& t = m_settings.t; - Real& dt = m_settings.dt; - const Real& tFinal = m_settings.tFinal; - Real& tDump = m_settings.tDump; - if (!m_settings.bRestart && m_settings.dtDump > 0.0) tDump = t + m_settings.dtDump; - - while(t < tFinal) - { - Real dt_next; - dt = (tFinal-t) < dt ? (tFinal-t) : dt; - _computeRHS(t, dt, data); - while (!_inBound(t, dt, data, dt_next)) {} - m_output = m_U; - m_U += dt*(b1*m_rhs + b3*m_rhs3 + b4*m_rhs4 + b5*m_rhs5 + b7*m_rhs7 + b8*m_rhs8); - if (m_settings.dtDump > 0.0) - { - if (tDump <= (t+dt)) _dumpOutput(data); - } - else - if ((step+1) % m_settings.writeGranularity == 0) - m_kernel->write((step+1), t+dt, dt, m_U, data); - t += dt; - dt = dt_next; - ++step; - - if (m_settings.step % m_settings.reportGranularity == 0) - std::printf("Time = %e;\tStep = %d\n", m_settings.t, m_settings.step); - - if (m_settings.restartstep > 0 && step % m_settings.restartstep == 0) - serialize<Tinput>(m_U, m_settings); - } - } -}; - -#endif /* RKV56_H_A6VN39JN */ diff --git a/TimeStepper/implicit/BDF.h b/TimeStepper/implicit/BDF.h @@ -1,76 +0,0 @@ -/* File: BDF.h */ -/* Date: Mon Feb 8 11:44:23 2016 */ -/* Author: Fabian Wermelinger */ -/* Tag: Backward differentiation formula using SUNDIALS library */ -/* Copyright 2016 ETH Zurich. All Rights Reserved. */ -#ifndef BDF_H_YF523CRX -#define BDF_H_YF523CRX - -#ifdef _USE_SUNDIALS_ -#include <cassert> -#include "StepperBase.h" - -#include "cvode/cvode.h" -#include "cvode/cvode_dense.h" -#include "nvector/nvector_serial.h" - -template <typename Tinput, typename Trhs> -class BDF : public StepperBase<Tinput,Trhs> -{ - Real m_a; - void* m_cvode_mem; - N_Vector m_y; - - void _init_sundials() - { - m_cvode_mem = CVodeCreate(CV_BDF, CV_NEWTON); - m_y = N_VMake_Serial(Tinput::SIZE, m_U.data()); - CVodeInit(m_cvode_mem, BDF::f, 0.0, m_y); - CVodeSStolerances(m_cvode_mem, 1.0e-6, 1.0e-6); - CVDense(m_cvode_mem, Trhs::SIZE); - CVodeSetUserData(m_cvode_mem, static_cast<void*>(&m_a)); - } - -protected: - using StepperBase<Tinput,Trhs>::m_settings; - using StepperBase<Tinput,Trhs>::m_U; - using StepperBase<Tinput,Trhs>::m_kernel; - Trhs m_rhs; - -public: - BDF(Tinput& U, StepperSettings& S, KernelBase<Tinput,Trhs> * const kern) : - StepperBase<Tinput,Trhs>(U,S,kern), m_rhs(U.size()) - { - _init_sundials(); - } - ~BDF() { CVodeFree(&m_cvode_mem); } - - virtual void step(void const* const data=nullptr) - { - /* TODO: (fabianw; Fri Feb 12 13:26:02 2016) this does not work yet. - * Do not compile with Sundials support */ - /* m_a = a; */ - Real tret; // interface dummy - CVode(m_cvode_mem, t+dt, m_y, &tret, CV_ONE_STEP); - t = tret; - - if (m_settings.step % m_settings.reportGranularity == 0) - std::printf("Time = %e;\tStep = %d\n", m_settings.t, m_settings.step); - } - - virtual inline void setIC(Real const ic, const Real t0=0) - { - m_U = ic; - CVodeReInit(m_cvode_mem, t0, m_y); - } - - static inline int f(Real t, N_Vector y, N_Vector ydot, void *a) - { - Tkernel kernel(Trhs::SIZE); - kernel.compute(NV_DATA_S(y), NV_DATA_S(ydot), *static_cast<Real*>(a), t); - return 0; - } -}; -#endif /* _USE_SUNDIALS_ */ - -#endif /* BDF_H_YF523CRX */ diff --git a/TimeStepper/orderVerification/orderVerification.cpp b/TimeStepper/orderVerification/orderVerification.cpp @@ -1,145 +0,0 @@ -/* File: orderVerification.cpp */ -/* Date: Sun Feb 7 22:48:30 2016 */ -/* Author: Fabian Wermelinger */ -/* Tag: Verify order of time stepper */ -/* Copyright 2016 ETH Zurich. All Rights Reserved. */ -#include <cassert> -#include <iostream> -#include <cmath> -#include <vector> -#include <string> -#include <utility> -#include <fstream> - -#include "common.h" -#include "KernelBase.h" -#include "TimeStepper.h" -#include "GnuplotDump.h" - -using namespace std; - -constexpr Real DECAY = -1.0; - -template <typename Tinput, typename Trhs=Tinput> -class Dahlquist : public KernelBase<Tinput,Trhs> -{ - GnuplotDump m_gp; - -protected: - const Real m_a; - -public: - Dahlquist(const Real a=1.0) : m_gp("out", GnuplotDump::ASCII), m_a(a) {} - ~Dahlquist() {} - - void compute(const Tinput& U, Trhs& rhs, const Real t, void const* const data=nullptr) - { - rhs = m_a*U; - } - - void write(const size_t step, const Real t, const Real dt, const Tinput& U, const void * const data=nullptr) - { - m_gp.write(step, t, dt, U.data(), U.size()*Tinput::DataType::SIZE); - } -}; - -template <typename Tinput, typename Trhs=Tinput> -class DahlquistLSRK3 : public Dahlquist<Tinput,Trhs> -{ - using Dahlquist<Tinput, Trhs>::m_a; - -public: - DahlquistLSRK3(const Real a=1.0) : Dahlquist<Tinput,Trhs>(a) {} - ~DahlquistLSRK3() {} - - void compute(const Tinput& U, Trhs& rhs, const Real t, void const* const data=nullptr) - { - assert(data != nullptr); - const Real * const LSRKData = static_cast<const Real* const>(data); - const Real dt = LSRKData[0]; - const Real A = LSRKData[1]; - rhs = A*rhs + dt*m_a*U; - } -}; - -// types we use -typedef State<Real,1> State1; -using vec_t = LightVector<State1>; -using baseKernel_t = KernelBase<vec_t>*; -using baseStepper_t = StepperBase<vec_t>*; - -inline Real exact(const Real t, const Real c) { return exp(c*t); } - -struct Error -{ - Error() : dt(0), L1(0), L2(0), Linf(0) {} - Real dt, L1, L2, Linf; -}; - -vector<Error> verify(baseStepper_t stepper, int const stages, StepperSettings& S, const Real dt=1.0, const Real scale=0.5) -{ - vector<Error> error(stages); - S.dt = dt; - for (int s = 0; s < stages; ++s) - { - size_t const N = static_cast<size_t>(S.tFinal/S.dt)+1; - S.t = 0.0; - stepper->U() = 1.0; - Real& numerical = stepper->U()[0][0]; - for (size_t i = 0; i < N; ++i) - { - stepper->step(); - const Real absErr = abs(numerical - exact(S.t, DECAY)); - error[s].L1 += absErr; - error[s].L2 += absErr*absErr; - error[s].Linf = (error[s].Linf < absErr ? absErr : error[s].Linf); - } - error[s].L1 /= N; - error[s].L2 = sqrt(error[s].L2/N); - error[s].dt = S.dt; - S.dt *= scale; - } - return error; -} - -int main(int argc, const char** argv) -{ - // set up solution vector - vec_t U; - - // kernel instances - baseKernel_t dahlquist = new Dahlquist<vec_t>(DECAY); - baseKernel_t dahlquistLSRK3 = new DahlquistLSRK3<vec_t>(DECAY); - - // setup time steppers - ArgumentParser parser(argc, argv); - StepperSettings S(parser); - - vector<pair<pair<string,Real>, baseStepper_t> > timeStepperList; - timeStepperList.push_back(make_pair(make_pair("Euler",1.0), new Euler<vec_t>(U, S, dahlquist))); - timeStepperList.push_back(make_pair(make_pair("RK4",4.0), new RK4<vec_t>(U, S, dahlquist))); - timeStepperList.push_back(make_pair(make_pair("LSRK3",3.0), new LSRK3<vec_t>(U, S, dahlquistLSRK3))); - // timeStepperList.push_back(make_pair(make_pair("RKF45",5.0), new RKF45<vec_t>(U, S, dahlquist))); - // timeStepperList.push_back(make_pair(make_pair("RKV56",6.0), new RKV56<vec_t>(U, S, dahlquist))); -#ifdef _USE_SUNDIALS_ - timeStepperList.push_back(make_pair(make_pair("BDF",5.0), new BDF<vec_t>(U, S, dahlquist))); -#endif /* _USE_SUNDIALS_ */ - - for (size_t i = 0; i < timeStepperList.size(); ++i) - { - vector<Error> error = verify(timeStepperList[i].second, 7, S); - ofstream save(timeStepperList[i].first.first + ".dat"); - save.setf(std::ios::scientific, std::ios::floatfield); - auto expected = [&](const Real x){ return error[0].L1*pow(x, timeStepperList[i].first.second); }; - for (size_t j = 0; j < error.size(); ++j) - save << error[j].dt << '\t' << 1.0/error[j].dt << '\t' << error[j].L1 << '\t' << error[j].L2 << '\t' << error[j].Linf << '\t' << expected(error[j].dt) << endl; - save.close(); - } - - for (size_t i = 0; i < timeStepperList.size(); ++i) - delete timeStepperList[i].second; - delete dahlquist; - delete dahlquistLSRK3; - - return 0; -} diff --git a/ArgumentParser.h b/include/ArgumentParser.h diff --git a/include/GnuplotDump.h b/include/GnuplotDump.h @@ -0,0 +1,115 @@ +/* File: GnuplotDump.h */ +/* Date: Tue Mar 1 21:07:49 2016 */ +/* Author: Fabian Wermelinger */ +/* Tag: Gnuplot Dumper */ +/* Copyright 2016 ETH Zurich. All Rights Reserved. */ +#ifndef GNUPLOTDUMP_H_2VNNTIUB +#define GNUPLOTDUMP_H_2VNNTIUB + +#include <cstdlib> +#include <fstream> +#include <iostream> +#include <string> + +#ifdef _FLOAT_PRECISION_ +using Real = float; +#else +using Real = double; +#endif + +#ifdef _GNUPLOT_DOUBLE_ +using GPfloat = double; +#else +using GPfloat = float; +#endif + +class GnuplotDump +{ +private: + const int m_ftype; + std::string m_fname; + std::ofstream m_file; + size_t m_N; + + void _writeASCII(const size_t step, const Real t, const Real dt, const Real * const src, const size_t N) + { + if (m_file.good()) + { + m_file << step << '\t' << t << '\t' << dt; + for (size_t i = 0; i < N; ++i) + m_file << '\t' << src[i]; + m_file << std::endl; + m_N = N+3; + } + } + + void _writeBIN(const size_t step, const GPfloat t, const GPfloat dt, const Real * const src, size_t N) + { + if (1024 < N) + { + std::cerr << "GnuplotDump: WARNING -- Max. Column width for BIN dump is 1024" << std::endl; + N = 1024; + } + GPfloat buf[1024+3]; + + buf[0] = static_cast<GPfloat>(step); + buf[1] = t; + buf[2] = dt; + for (size_t i = 0; i < N; ++i) + buf[i+3] = static_cast<GPfloat>(src[i]); + + if (m_file.good()) + { + m_file.write((const char*)(&buf[0]), (N+3)*sizeof(GPfloat)); + m_N = N+3; + } + } + + void _writeGPscript() const + { + std::ofstream out(m_fname+".gp"); + if (m_ftype == ASCII) + out << "plot '" << m_fname+".dat" << "' using 1:2 with lines" << std::endl; + else + { + out << "plot '" << m_fname+".bin" << "' binary format='\%" << m_N; + if (sizeof(GPfloat) == 4) + out << "float"; + else + out << "double"; + out << "' using 1:2 with lines" << std::endl; + } + out.close(); + } + +public: + GnuplotDump(const std::string fname="gnuplot", const int type=BIN, const size_t pos=0) : m_ftype(type), m_fname(fname) + { + if (m_ftype == ASCII) + { + m_file.open(m_fname+".dat", std::ios_base::out|std::ios_base::app); + m_file.setf(std::ios::scientific, std::ios::floatfield); + } + else + m_file.open(m_fname+".bin", std::ios_base::out|std::ios_base::binary|std::ios_base::app); + + if (pos > 0) m_file.seekp(pos); + } + ~GnuplotDump() + { + m_file.close(); + _writeGPscript(); + } + + enum {ASCII, BIN}; + + void write(const size_t step, const Real t, const Real dt, const Real * const src, const size_t N) + { + if (m_ftype == ASCII) + _writeASCII(step, t, dt, src, N); + else + _writeBIN(step, t, dt, src, N); + } +}; + +#endif /* GNUPLOTDUMP_H_2VNNTIUB */ diff --git a/TimeStepper/KernelBase.h b/include/TimeStepper/KernelBase.h diff --git a/include/TimeStepper/StepperBase.h b/include/TimeStepper/StepperBase.h @@ -0,0 +1,235 @@ +// File : StepperBase.h +// Date : Thu Sep 22 13:27:19 2016 +// Author : Fabian Wermelinger +// Description: Time stepper base class +// Copyright 2016 ETH Zurich. All Rights Reserved. +#ifndef STEPPERBASE_H_FCCSM4HL +#define STEPPERBASE_H_FCCSM4HL + +#include <TimeStepper/KernelBase.h> + +// forward declarations +template <typename Tinput, typename Trhs=Tinput> +class StepperBase; +template <typename Tinput, typename Trhs=Tinput> +class Euler; +template <typename Tinput, typename Trhs=Tinput> +class RK4; +template <typename Tinput, typename Trhs=Tinput> +class LSRK3; +template <typename Tinput, typename Trhs=Tinput> +class RKF45; +template <typename Tinput, typename Trhs=Tinput> +class RKV56; +#ifdef _USE_SUNDIALS_ +template <typename Tinput, typename Trhs=Tinput> +class BDF; +#endif /* _USE_SUNDIALS_ */ + +#ifdef _FLOAT_PRECISION_ +using Real = float; +#else +using Real = double; +#endif + +struct StepperSettings +{ + StepperSettings(ArgumentParser& p) + { + aTol = p("-ts_aTol").asDouble(1.0e-8); + rTol = p("-ts_rTol").asDouble(1.0e-8); + minScale = p("-ts_minScale").asDouble(0.2); + maxScale = p("-ts_maxScale").asDouble(10.0); + alpha = p("-ts_alpha").asDouble(1.0); + beta = p("-ts_beta").asDouble(0.0); + safety = p("-ts_safety").asDouble(0.9); + t = p("-ts_t0").asDouble(0.0); + dt = p("-ts_dt").asDouble(1.0e-4); + step = 0; + nsteps = p("-ts_nsteps").asInt(0); + p.set_strict_mode(); + tFinal = p("-ts_tend").asDouble(); + p.unset_strict_mode(); + dtDump = p("-ts_dtDump").asDouble(-1.0); + tDump = t; + writeGranularity = p("-ts_writeGranularity").asInt(1); + writeCount = 0; + reportGranularity= p("-ts_reportGranularity").asInt(100); + bFixedStep = true; + + // restarts + bRestart = p("-ts_restart").asBool(false); + restartstep = p("-ts_restart_step").asInt(0); + + } + + Real aTol; + Real rTol; + Real minScale; + Real maxScale; + Real alpha; + Real beta; + Real safety; + Real t; + Real dt; + size_t step; + size_t nsteps; + Real tFinal; + Real dtDump; + Real tDump; + size_t writeGranularity; + size_t writeCount; + size_t reportGranularity; + bool bFixedStep; + + // restarts + bool bRestart; + int restartstep; + + + // helper + void print() const + { + printf("Time Stepper :\n"); + printf("\tAbsolute tolerance = %e\n", aTol); + printf("\tRelative tolerance = %e\n", rTol); + printf("\tMinimum time step scale = %e\n", minScale); + printf("\tMaximum time step scale = %e\n", maxScale); + printf("\tError PI control alpha = %e\n", alpha); + printf("\tError PI control beta = %e\n", beta); + } + + template <typename Tinput, typename Trhs=Tinput> + StepperBase<Tinput,Trhs>* stepperFactory(ArgumentParser& p, Tinput& U, KernelBase<Tinput,Trhs> * const kern=nullptr) + { + assert(kern != nullptr); + + if (p("-ts").asString("rkv56") == "euler") + { + cout << "Allocating Euler time stepper..." << endl; + return new Euler<Tinput, Trhs>(U, *this, kern); + } + else if (p("-ts").asString("rkv56") == "rk4") + { + cout << "Allocating RK4 time stepper..." << endl; + return new RK4<Tinput, Trhs>(U, *this, kern); + } + else if (p("-ts").asString("rkv56") == "lsrk3") + { + cout << "Allocating LSRK3 time stepper..." << endl; + return new LSRK3<Tinput, Trhs>(U, *this, kern); + } + else if (p("-ts").asString("rkv56") == "rkf45") + { + cout << "Allocating RKF45 adaptive time stepper..." << endl; + this->bFixedStep = false; + this->print(); + return new RKF45<Tinput, Trhs>(U, *this, kern); + } + else if (p("-ts").asString("rkv56") == "rkv56") + { + cout << "Allocating RKV56 adaptive time stepper..." << endl; + this->bFixedStep = false; + this->print(); + return new RKV56<Tinput, Trhs>(U, *this, kern); + } +#ifdef _USE_SUNDIALS_ + else if (p("-ts").asString("rkv56") == "bdf") + { + cout << "Allocating Sundials BDF/Newton implicit time stepper..." << endl; + return new BDF<Tinput, Trhs>(U, *this, kern); + } +#endif /* _USE_SUNDIALS_ */ + else + return nullptr; + } +}; + + +template <typename Tinput, typename Trhs> +class StepperBase +{ + StepperBase(Tinput const& rhs) = delete; + StepperBase& operator=(Tinput const& rhs) = delete; + +protected: + StepperSettings& m_settings; + Tinput& m_U; + KernelBase<Tinput,Trhs> * const m_kernel; + +public: + StepperBase(Tinput& U, StepperSettings& settings, KernelBase<Tinput,Trhs> * const kern=nullptr) : m_settings(settings), m_U(U), m_kernel(kern) { } + virtual ~StepperBase() { } + + inline Tinput& U() { return m_U; } + inline Tinput const& U() const { return m_U; } + + virtual void step(void const* const data=nullptr) = 0; + + inline void write(const size_t step, const Real t, const Real dt, const Tinput& U, const void * const data=nullptr) + { + m_kernel->write(step, t, dt, U, data); + } + + inline void dispose() { delete m_kernel; } +}; + +// serializer +struct _simulation_state_t +{ + Real t; + Real dt; + Real tFinal; + Real dtDump; + Real tDump; + size_t step; + size_t nsteps; + size_t writeCount; +} __attribute__((packed)); + + +template <typename Tinput> +void serialize(const Tinput& U, const StepperSettings& S, const std::string fname="restart.bin") +{ + _simulation_state_t state; + state.t = S.t; + state.dt = S.dt; + state.tFinal = S.tFinal; + state.dtDump = S.dtDump; + state.tDump = S.tDump; + state.step = S.step; + state.nsteps = S.nsteps; + state.writeCount = S.writeCount; + + ofstream _stream(fname, ios::out | ios::binary); + _stream.write((const char*)&state.t, sizeof(_simulation_state_t)); + + const char* const _src = (const char*)U.data(); + _stream.write(_src, sizeof(typename Tinput::DataType)*U.size()); + + _stream.close(); +} + +template <typename Tinput> +void deserialize(Tinput& U, StepperSettings& S, const std::string fname="restart.bin") +{ + ifstream _stream(fname, ios::in | ios::binary); + + _simulation_state_t state; + _stream.read((char*)&state.t, sizeof(_simulation_state_t)); + S.t = state.t; + S.dt = state.dt; + S.tFinal = state.tFinal; + S.dtDump = state.dtDump; + S.tDump = state.tDump; + S.step = state.step; + S.nsteps = state.nsteps; + S.writeCount = state.writeCount; + + char* const _dst = (char*)U.data(); + _stream.read(_dst, sizeof(typename Tinput::DataType)*U.size()); + + _stream.close(); +} + +#endif /* STEPPERBASE_H_FCCSM4HL */ diff --git a/include/TimeStepper/TimeStepper.h b/include/TimeStepper/TimeStepper.h @@ -0,0 +1,24 @@ +// File : TimeStepper.h +// Date : Thu Sep 22 13:23:57 2016 +// Author : Fabian Wermelinger +// Description: Structure to setup a time stepper +// Copyright 2016 ETH Zurich. All Rights Reserved. +#ifndef TIMESTEPPER_H_XFR2JEZJ +#define TIMESTEPPER_H_XFR2JEZJ + +#include <ArgumentParser.h> +#include <TimeStepper/KernelBase.h> +#include <TimeStepper/StepperBase.h> + +#include <TimeStepper/explicit/Euler.h> +#include <TimeStepper/explicit/LSRK3.h> +#include <TimeStepper/explicit/RK4.h> +#include <TimeStepper/explicit/variableStep/RKF45.h> +#include <TimeStepper/explicit/variableStep/RKV56.h> +#ifdef _USE_SUNDIALS_ +#include <TimeStepper/implicit/BDF.h> +#endif /* _USE_SUNDIALS_ */ + +#include <cstdio> + +#endif /* TIMESTEPPER_H_XFR2JEZJ */ diff --git a/include/TimeStepper/explicit/Euler.h b/include/TimeStepper/explicit/Euler.h @@ -0,0 +1,38 @@ +/* File: Euler.h */ +/* Date: Fri Feb 5 18:57:57 2016 */ +/* Author: Fabian Wermelinger */ +/* Tag: 1st order Euler */ +/* Copyright 2016 ETH Zurich. All Rights Reserved. */ +#ifndef EULER_H_8R2CXYR0 +#define EULER_H_8R2CXYR0 + +#include <TimeStepper/StepperBase.h> + +#include <cstdio> + +template <typename Tinput, typename Trhs> +class Euler : public StepperBase<Tinput,Trhs> +{ +protected: + using StepperBase<Tinput,Trhs>::m_settings; + using StepperBase<Tinput,Trhs>::m_U; + using StepperBase<Tinput,Trhs>::m_kernel; + Trhs m_rhs; + +public: + Euler(Tinput& U, StepperSettings& S, KernelBase<Tinput,Trhs> * const kern) : StepperBase<Tinput,Trhs>(U,S,kern), m_rhs(U.size()) { } + virtual ~Euler() {} + + virtual void step(void const* const data=nullptr) + { + m_kernel->compute(m_U, m_rhs, m_settings.t, data); + m_U += static_cast<Real>(m_settings.dt)*m_rhs; + m_settings.t += m_settings.dt; + ++(m_settings.step); + + if (m_settings.step % m_settings.reportGranularity == 0) + std::printf("Time = %e;\tStep = %d\n", m_settings.t, m_settings.step); + } +}; + +#endif /* EULER_H_8R2CXYR0 */ diff --git a/include/TimeStepper/explicit/LSRK3.h b/include/TimeStepper/explicit/LSRK3.h @@ -0,0 +1,56 @@ +/* File: LSRK3.h */ +/* Date: Fri Feb 5 18:58:48 2016 */ +/* Author: Fabian Wermelinger */ +/* Tag: 3rd order low-storage Runge-Kutta. Requires a compatible kernel */ +/* Copyright 2016 ETH Zurich. All Rights Reserved. */ +#ifndef LSRK3_H_MJ6ACLH4 +#define LSRK3_H_MJ6ACLH4 + +#include <TimeStepper/explicit/Euler.h> + +template <typename Tinput, typename Trhs> +class LSRK3 : public Euler<Tinput, Trhs> +{ + const Real m_A[3]; + const Real m_B[3]; + using Euler<Tinput,Trhs>::m_settings; + using Euler<Tinput,Trhs>::m_U; + using Euler<Tinput,Trhs>::m_rhs; + using Euler<Tinput,Trhs>::m_kernel; + +public: + LSRK3(Tinput& U, StepperSettings& S, KernelBase<Tinput,Trhs> * const kern) : + Euler<Tinput,Trhs>(U,S,kern), + // m_A{0.0, -2.915492524638791, -0.000000093517376}, // Gottlieb & Shu (Optimal LSRK3 for CFL = 0.32) + // m_B{0.924574000000000, 0.287713063186749, 0.626538109512740} + m_A{ 0.0, -17./32, -32./27}, + m_B{1./4, 8./9, 3./4} + { } + virtual ~LSRK3() {} + + virtual void step(void const* const data=nullptr) + { + // stage 1 + Real lsrk_data[2] = {static_cast<Real>(m_settings.dt), m_A[0]}; + m_kernel->compute(m_U, m_rhs, m_settings.t, lsrk_data); + m_U += m_B[0]*m_rhs; + + // stage 2 + lsrk_data[1] = m_A[1]; + m_kernel->compute(m_U, m_rhs, m_settings.t, lsrk_data); + m_U += m_B[1]*m_rhs; + + // stage 3 + lsrk_data[1] = m_A[2]; + m_kernel->compute(m_U, m_rhs, m_settings.t, lsrk_data); + m_U += m_B[2]*m_rhs; + + m_settings.t += m_settings.dt; + ++(m_settings.step); + + if (m_settings.step % m_settings.reportGranularity == 0) + std::printf("Time = %e;\tStep = %d\n", m_settings.t, m_settings.step); + } +}; + +#endif /* LSRK3_H_MJ6ACLH4 */ diff --git a/include/TimeStepper/explicit/RK4.h b/include/TimeStepper/explicit/RK4.h @@ -0,0 +1,53 @@ +/* File: RK4.h */ +/* Date: Fri Feb 5 19:14:44 2016 */ +/* Author: Fabian Wermelinger */ +/* Tag: Classical 4th order Runge-Kutta */ +/* Copyright 2016 ETH Zurich. All Rights Reserved. */ +#ifndef RK4_H_LROUVHDE +#define RK4_H_LROUVHDE + +#include <TimeStepper/explicit/Euler.h> + +template <typename Tinput, typename Trhs> +class RK4 : public Euler<Tinput, Trhs> +{ + using Euler<Tinput, Trhs>::m_settings; + using Euler<Tinput, Trhs>::m_U; + using Euler<Tinput, Trhs>::m_rhs; + using Euler<Tinput, Trhs>::m_kernel; + +public: + RK4(Tinput& U, StepperSettings& S, KernelBase<Tinput,Trhs> * const kern) : Euler<Tinput,Trhs>(U,S,kern) { } + virtual ~RK4() {} + + virtual void step(void const* const data=nullptr) + { + const Real oneHalf = static_cast<Real>(0.5); + const Real oneThird = static_cast<Real>(1./3.); + Tinput tmpU = m_U; + + // stage 1 + m_kernel->compute(tmpU, m_rhs, m_settings.t, data); + m_U += oneHalf*oneThird*m_settings.dt*m_rhs; + + // stage 2 + m_kernel->compute(tmpU + oneHalf*m_settings.dt*m_rhs, m_rhs, m_settings.t + oneHalf*m_settings.dt, data); + m_U += oneThird*m_settings.dt*m_rhs; + + // stage 3 + m_kernel->compute(tmpU + oneHalf*m_settings.dt*m_rhs, m_rhs, m_settings.t + oneHalf*m_settings.dt, data); + m_U += oneThird*m_settings.dt*m_rhs; + + // stage 4 + m_kernel->compute(tmpU + m_settings.dt*m_rhs, m_rhs, m_settings.t + m_settings.dt, data); + m_U += oneHalf*oneThird*m_settings.dt*m_rhs; + + m_settings.t += m_settings.dt; + ++(m_settings.step); + + if (m_settings.step % m_settings.reportGranularity == 0) + std::printf("Time = %e;\tStep = %d\n", m_settings.t, m_settings.step); + } +}; + +#endif /* RK4_H_LROUVHDE */ diff --git a/include/TimeStepper/explicit/variableStep/RKF45.h b/include/TimeStepper/explicit/variableStep/RKF45.h @@ -0,0 +1,188 @@ +/* File: RKF45.h */ +/* Date: Wed Feb 10 17:33:42 2016 */ +/* Author: Fabian Wermelinger */ +/* Tag: Runge-Kutta-Fehlberg 4th-5th order variable step method */ +/* Copyright 2016 ETH Zurich. All Rights Reserved. */ +#ifndef RKF45_H_UWN6WM1V +#define RKF45_H_UWN6WM1V + +#include <TimeStepper/explicit/Euler.h> + +#include <cassert> +#include <cmath> +#include <cstdlib> +#include <limits> + +template <typename Tinput, typename Trhs> +class RKF45 : public Euler<Tinput, Trhs> +{ + using Euler<Tinput, Trhs>::m_settings; + using Euler<Tinput, Trhs>::m_U; + using Euler<Tinput, Trhs>::m_rhs; + using Euler<Tinput, Trhs>::m_kernel; + + Real m_errold; + const Real m_alpha; + const Real m_beta; + bool m_reject; + bool m_firstPass; + + Trhs m_rhs2; + Trhs m_rhs3; + Trhs m_rhs4; + Trhs m_rhs5; + Trhs m_rhs6; + + Tinput m_output; + + static constexpr Real b1 = 16./135., b3 = 6656./12825., b4 = 28561./56430., b5 = -9./50., b6 = 2./55.; + static constexpr Real c21 = 0.25, ct2 = 0.25; + static constexpr Real c31 = 3./32., c32 = 9./32., ct3 = 3./8.; + static constexpr Real c41 = 1932./2197., c42 = -7200./2197., c43 = 7296./2197., ct4 = 12./13.; + static constexpr Real c51 = 439./216., c52 = -8., c53 = 3680./513., c54 = -845./4104., ct5 = 1.0; + static constexpr Real c61 = -8./27., c62 = 2., c63 = -3544./2565., c64 = 1859./4104., c65 = -11./40., ct6 = 0.5; + static constexpr Real e1 = 1./360., e3 = -128./4275., e4 = -2197./75240., e5 = 1./50., e6 = 2./55.; + + void _computeRHS(const Real t, const Real dt, void const* const data) + { + if (!m_reject) m_kernel->compute(m_U, m_rhs, t, data); + m_kernel->compute(m_U + dt*(c21*m_rhs), m_rhs2, t + ct2*dt, data); + m_kernel->compute(m_U + dt*(c31*m_rhs + c32*m_rhs2), m_rhs3, t + ct3*dt, data); + m_kernel->compute(m_U + dt*(c41*m_rhs + c42*m_rhs2 + c43*m_rhs3), m_rhs4, t + ct4*dt, data); + m_kernel->compute(m_U + dt*(c51*m_rhs + c52*m_rhs2 + c53*m_rhs3 + c54*m_rhs4), m_rhs5, t + ct5*dt, data); + 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); + } + + inline Real _error() const + { + assert(m_U.size() == m_rhs.size()); + Real maxerr = 0.0; + // using inf-norm (could also do L2-norm) + for (size_t i = 0; i < m_U.size(); ++i) + { + 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]; + for (int j = 0; j < Trhs::DataType::SIZE; ++j) + { + const Real IepsI = std::abs(E[j]); + const Real scale = m_settings.aTol + std::abs(m_U[i][j])*m_settings.rTol; + maxerr = std::max(maxerr, IepsI/scale); + } + } + assert(!isnan(maxerr)); + return maxerr; + } + + bool _inBound(const Real t, Real& dt, void const* const data, Real& dt_next) + { + const Real err = _error(); + + if (dt < std::numeric_limits<Real>::epsilon()) + { + // if you request the impossible + dt_next = 1.5*std::numeric_limits<Real>::epsilon(); + m_errold = std::max(err, static_cast<Real>(1.0e-4)); + m_reject = false; + m_firstPass = true; + return true; + } + + const Real safety = m_settings.safety; + const Real minScale = m_settings.minScale; + const Real maxScale = m_settings.maxScale; + Real scale; + if (err <= 1.0) + { + if (err == 0.0) + scale = maxScale; + else + { + scale = safety*std::pow(err,-m_alpha)*std::pow(m_errold,m_beta); + scale = (scale < minScale) ? minScale : ((scale > maxScale) ? maxScale : scale); + } + if (m_reject) + dt_next = dt*std::min(scale, static_cast<Real>(1.0)); + else + dt_next = dt*scale; + m_errold = std::max(err, static_cast<Real>(1.0e-4)); + m_reject = false; + m_firstPass = true; + return true; + } + else + { + scale = safety*std::pow(err,-m_alpha); + scale = std::max(scale, minScale); + dt *= scale; + m_reject = true; + m_firstPass = false; + _computeRHS(t, dt, data); + return false; + } + } + + void _dumpOutput(const void * const data) + { + const Real t = m_settings.t; + const Real dt = m_settings.dt; + Real& tDump = m_settings.tDump; + + const Tinput Uold = m_output; + const Trhs rhsOld = m_rhs; + + m_kernel->compute(m_U, m_rhs, t+dt, data); + m_firstPass = false; + + while(tDump <= (t+dt)) + { + const Real phi = (tDump - t)/dt; + // Hermite 3rd order + 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); + m_kernel->write(m_settings.step, t+phi*dt, phi*dt, m_output, data); + tDump += m_settings.dtDump; + } + } + +public: + RKF45(Tinput& U, StepperSettings& S, KernelBase<Tinput,Trhs> * const kern) : + 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), + m_rhs2(U.size()), m_rhs3(U.size()), m_rhs4(U.size()), m_rhs5(U.size()), m_rhs6(U.size()), m_output(U.size()) {} + virtual ~RKF45() {} + + virtual void step(void const* const data=nullptr) + { + size_t& step = m_settings.step; + Real& t = m_settings.t; + Real& dt = m_settings.dt; + const Real& tFinal = m_settings.tFinal; + Real& tDump = m_settings.tDump; + if (!m_settings.bRestart && m_settings.dtDump > 0.0) tDump = t + m_settings.dtDump; + + while(t < tFinal) + { + Real dt_next; + dt = (tFinal-t) < dt ? (tFinal-t) : dt; + _computeRHS(t, dt, data); + while (!_inBound(t, dt, data, dt_next)) {} + m_output = m_U; + m_U += dt*(b1*m_rhs + b3*m_rhs3 + b4*m_rhs4 + b5*m_rhs5 + b6*m_rhs6); + if (m_settings.dtDump > 0.0) + { + if (tDump <= (t+dt)) _dumpOutput(data); + } + else + if ((step+1) % m_settings.writeGranularity == 0) + m_kernel->write((step+1), t+dt, dt, m_U, data); + t += dt; + dt = dt_next; + ++step; + + if (m_settings.step % m_settings.reportGranularity == 0) + std::printf("Time = %e;\tStep = %d\n", m_settings.t, m_settings.step); + + if (m_settings.restartstep > 0 && step % m_settings.restartstep == 0) + serialize<Tinput>(m_U, m_settings); + } + } +}; + +#endif /* RKF45_H_UWN6WM1V */ diff --git a/include/TimeStepper/explicit/variableStep/RKV56.h b/include/TimeStepper/explicit/variableStep/RKV56.h @@ -0,0 +1,195 @@ +/* File: RKV56.h */ +/* Date: Thu Feb 11 11:36:11 2016 */ +/* Author: Fabian Wermelinger */ +/* Tag: Runge-Kutta-Verner 5th-6th order variable step method */ +/* Copyright 2016 ETH Zurich. All Rights Reserved. */ +#ifndef RKV56_H_A6VN39JN +#define RKV56_H_A6VN39JN + +#include <TimeStepper/explicit/Euler.h> + +#include <cassert> +#include <cmath> +#include <cstdlib> +#include <iostream> +#include <limits> + +template <typename Tinput, typename Trhs> +class RKV56 : public Euler<Tinput, Trhs> +{ + using Euler<Tinput, Trhs>::m_settings; + using Euler<Tinput, Trhs>::m_U; + using Euler<Tinput, Trhs>::m_rhs; + using Euler<Tinput, Trhs>::m_kernel; + + Real m_errold; + const Real m_alpha; + const Real m_beta; + bool m_reject; + bool m_firstPass; + + Trhs m_rhs2; + Trhs m_rhs3; + Trhs m_rhs4; + Trhs m_rhs5; + Trhs m_rhs6; + Trhs m_rhs7; + Trhs m_rhs8; + + Tinput m_output; + + static constexpr Real b1 = 3./40., b3 = 875./2244., b4 = 23./72., b5 = 264./1955., b7 = 125./11592., b8 = 43./616.; + static constexpr Real c21 = 1./6., ct2 = 1./6.; + static constexpr Real c31 = 4./75., c32 = 16./75., ct3 = 4./15.; + static constexpr Real c41 = 5./6., c42 = -8./3., c43 = 2.5, ct4 = 2./3.; + static constexpr Real c51 = -165./64., c52 = 55./6., c53 = -425./64., c54 = 85./96., ct5 = 5./6.; + static constexpr Real c61 = 2.4, c62 = -8., c63 = 4015./612., c64 = -11./36., c65 = 88./255., ct6 = 1.0; + static constexpr Real c71 = -8263./15000., c72 = 124./75., c73 = -643./680., c74 = -81./250., c75 = 2484./10625., ct7 = 1./15.; + static constexpr Real c81 = 3501./1720., c82 = -300./43., c83 = 297275./52632., c84 = -319./2322., c85 = 24068./84065., c87 = 3850./26703., ct8 = 1.0; + static constexpr Real e1 = -1./160., e3 = -125./17952., e4 = 1./144., e5 = -12./1955., e6 = -3./44., e7 = 125./11592., e8 = 43./616.; + + void _computeRHS(const Real t, const Real dt, void const* const data) + { + if (!m_reject) m_kernel->compute(m_U, m_rhs, t, data); + m_kernel->compute(m_U + dt*(c21*m_rhs), m_rhs2, t + ct2*dt, data); + m_kernel->compute(m_U + dt*(c31*m_rhs + c32*m_rhs2), m_rhs3, t + ct3*dt, data); + m_kernel->compute(m_U + dt*(c41*m_rhs + c42*m_rhs2 + c43*m_rhs3), m_rhs4, t + ct4*dt, data); + m_kernel->compute(m_U + dt*(c51*m_rhs + c52*m_rhs2 + c53*m_rhs3 + c54*m_rhs4), m_rhs5, t + ct5*dt, data); + 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); + 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); + 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); + } + + inline Real _error() const + { + assert(m_U.size() == m_rhs.size()); + Real maxerr = 0.0; + // using inf-norm (could also do L2-norm) + for (size_t i = 0; i < m_U.size(); ++i) + { + 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]; + for (int j = 0; j < Trhs::DataType::SIZE; ++j) + { + const Real IepsI = std::abs(E[j]); + const Real scale = m_settings.aTol + std::abs(m_U[i][j])*m_settings.rTol; + maxerr = std::max(maxerr, IepsI/scale); + } + } + assert(!isnan(maxerr)); + return maxerr; + } + + bool _inBound(const Real t, Real& dt, void const* const data, Real& dt_next) + { + const Real err = _error(); + + if (dt < std::numeric_limits<Real>::epsilon()) + { + // if you request the impossible + dt_next = 1.5*std::numeric_limits<Real>::epsilon(); + m_errold = std::max(err, static_cast<Real>(1.0e-4)); + m_reject = false; + m_firstPass = true; + return true; + } + + const Real safety = m_settings.safety; + const Real minScale = m_settings.minScale; + const Real maxScale = m_settings.maxScale; + Real scale; + if (err <= 1.0) + { + if (err == 0.0) + scale = maxScale; + else + { + scale = safety*std::pow(err,-m_alpha)*std::pow(m_errold,m_beta); + scale = (scale < minScale) ? minScale : ((scale > maxScale) ? maxScale : scale); + } + if (m_reject) + dt_next = dt*std::min(scale, static_cast<Real>(1.0)); + else + dt_next = dt*scale; + m_errold = std::max(err, static_cast<Real>(1.0e-4)); + m_reject = false; + m_firstPass = true; + return true; + } + else + { + scale = safety*std::pow(err,-m_alpha); + scale = std::max(scale, minScale); + dt *= scale; + m_reject = true; + m_firstPass = false; + _computeRHS(t, dt, data); + return false; + } + } + + void _dumpOutput(const void * const data) + { + const Real t = m_settings.t; + const Real dt = m_settings.dt; + Real& tDump = m_settings.tDump; + + const Tinput Uold = m_output; + const Trhs rhsOld = m_rhs; + + m_kernel->compute(m_U, m_rhs, t+dt, data); + m_firstPass = false; + + while(tDump <= (t+dt)) + { + const Real phi = (tDump - t)/dt; + // Hermite 3rd order + 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); + m_kernel->write(m_settings.step, t+phi*dt, phi*dt, m_output, data); + tDump += m_settings.dtDump; + } + } + +public: + RKV56(Tinput& U, StepperSettings& S, KernelBase<Tinput,Trhs> * const kern) : + 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), + 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()) {} + virtual ~RKV56() {} + + virtual void step(void const* const data=nullptr) + { + size_t& step = m_settings.step; + Real& t = m_settings.t; + Real& dt = m_settings.dt; + const Real& tFinal = m_settings.tFinal; + Real& tDump = m_settings.tDump; + if (!m_settings.bRestart && m_settings.dtDump > 0.0) tDump = t + m_settings.dtDump; + + while(t < tFinal) + { + Real dt_next; + dt = (tFinal-t) < dt ? (tFinal-t) : dt; + _computeRHS(t, dt, data); + while (!_inBound(t, dt, data, dt_next)) {} + m_output = m_U; + m_U += dt*(b1*m_rhs + b3*m_rhs3 + b4*m_rhs4 + b5*m_rhs5 + b7*m_rhs7 + b8*m_rhs8); + if (m_settings.dtDump > 0.0) + { + if (tDump <= (t+dt)) _dumpOutput(data); + } + else + if ((step+1) % m_settings.writeGranularity == 0) + m_kernel->write((step+1), t+dt, dt, m_U, data); + t += dt; + dt = dt_next; + ++step; + + if (m_settings.step % m_settings.reportGranularity == 0) + std::printf("Time = %e;\tStep = %d\n", m_settings.t, m_settings.step); + + if (m_settings.restartstep > 0 && step % m_settings.restartstep == 0) + serialize<Tinput>(m_U, m_settings); + } + } +}; + +#endif /* RKV56_H_A6VN39JN */ diff --git a/include/TimeStepper/implicit/BDF.h b/include/TimeStepper/implicit/BDF.h @@ -0,0 +1,77 @@ +/* File: BDF.h */ +/* Date: Mon Feb 8 11:44:23 2016 */ +/* Author: Fabian Wermelinger */ +/* Tag: Backward differentiation formula using SUNDIALS library */ +/* Copyright 2016 ETH Zurich. All Rights Reserved. */ +#ifndef BDF_H_YF523CRX +#define BDF_H_YF523CRX + +#ifdef _USE_SUNDIALS_ +#include <TimeStepper/StepperBase.h> + +#include <cvode/cvode.h> +#include <cvode/cvode_dense.h> +#include <nvector/nvector_serial.h> + +#include <cassert> + +template <typename Tinput, typename Trhs> +class BDF : public StepperBase<Tinput,Trhs> +{ + Real m_a; + void* m_cvode_mem; + N_Vector m_y; + + void _init_sundials() + { + m_cvode_mem = CVodeCreate(CV_BDF, CV_NEWTON); + m_y = N_VMake_Serial(Tinput::SIZE, m_U.data()); + CVodeInit(m_cvode_mem, BDF::f, 0.0, m_y); + CVodeSStolerances(m_cvode_mem, 1.0e-6, 1.0e-6); + CVDense(m_cvode_mem, Trhs::SIZE); + CVodeSetUserData(m_cvode_mem, static_cast<void*>(&m_a)); + } + +protected: + using StepperBase<Tinput,Trhs>::m_settings; + using StepperBase<Tinput,Trhs>::m_U; + using StepperBase<Tinput,Trhs>::m_kernel; + Trhs m_rhs; + +public: + BDF(Tinput& U, StepperSettings& S, KernelBase<Tinput,Trhs> * const kern) : + StepperBase<Tinput,Trhs>(U,S,kern), m_rhs(U.size()) + { + _init_sundials(); + } + ~BDF() { CVodeFree(&m_cvode_mem); } + + virtual void step(void const* const data=nullptr) + { + /* TODO: (fabianw; Fri Feb 12 13:26:02 2016) this does not work yet. + * Do not compile with Sundials support */ + /* m_a = a; */ + Real tret; // interface dummy + CVode(m_cvode_mem, t+dt, m_y, &tret, CV_ONE_STEP); + t = tret; + + if (m_settings.step % m_settings.reportGranularity == 0) + std::printf("Time = %e;\tStep = %d\n", m_settings.t, m_settings.step); + } + + virtual inline void setIC(Real const ic, const Real t0=0) + { + m_U = ic; + CVodeReInit(m_cvode_mem, t0, m_y); + } + + static inline int f(Real t, N_Vector y, N_Vector ydot, void *a) + { + Tkernel kernel(Trhs::SIZE); + kernel.compute(NV_DATA_S(y), NV_DATA_S(ydot), *static_cast<Real*>(a), t); + return 0; + } +}; +#endif /* _USE_SUNDIALS_ */ + +#endif /* BDF_H_YF523CRX */ diff --git a/common.h b/include/common.h diff --git a/TimeStepper/orderVerification/.gitignore b/test/orderVerification/.gitignore diff --git a/TimeStepper/orderVerification/Makefile b/test/orderVerification/Makefile diff --git a/test/orderVerification/orderVerification.cpp b/test/orderVerification/orderVerification.cpp @@ -0,0 +1,149 @@ +/* File: orderVerification.cpp */ +/* Date: Sun Feb 7 22:48:30 2016 */ +/* Author: Fabian Wermelinger */ +/* Tag: Verify order of time stepper */ +/* Copyright 2016 ETH Zurich. All Rights Reserved. */ +#include <GnuplotDump.h> +#include <TimeStepper/KernelBase.h> +#include <TimeStepper/TimeStepper.h> +#include <common.h> + +#include <cassert> +#include <iostream> +#include <cmath> +#include <vector> +#include <string> +#include <utility> +#include <fstream> + +using namespace std; + +constexpr Real DECAY = -1.0; + +template <typename Tinput, typename Trhs=Tinput> +class Dahlquist : public KernelBase<Tinput,Trhs> +{ + GnuplotDump m_gp; + +protected: + const Real m_a; + +public: + Dahlquist(const Real a=1.0) : m_gp("out", GnuplotDump::ASCII), m_a(a) {} + ~Dahlquist() {} + + void compute(const Tinput& U, Trhs& rhs, const Real t, void const* const data=nullptr) + { + rhs = m_a*U; + } + + void write(const size_t step, const Real t, const Real dt, const Tinput& U, const void * const data=nullptr) + { + m_gp.write(step, + t, + dt, + (const Real *)U.data(), + U.size() * Tinput::DataType::SIZE); + } +}; + +template <typename Tinput, typename Trhs=Tinput> +class DahlquistLSRK3 : public Dahlquist<Tinput,Trhs> +{ + using Dahlquist<Tinput, Trhs>::m_a; + +public: + DahlquistLSRK3(const Real a=1.0) : Dahlquist<Tinput,Trhs>(a) {} + ~DahlquistLSRK3() {} + + void compute(const Tinput& U, Trhs& rhs, const Real t, void const* const data=nullptr) + { + assert(data != nullptr); + const Real * const LSRKData = static_cast<const Real* const>(data); + const Real dt = LSRKData[0]; + const Real A = LSRKData[1]; + rhs = A*rhs + dt*m_a*U; + } +}; + +// types we use +typedef State<Real,1> State1; +using vec_t = LightVector<State1>; +using baseKernel_t = KernelBase<vec_t>*; +using baseStepper_t = StepperBase<vec_t>*; + +inline Real exact(const Real t, const Real c) { return exp(c*t); } + +struct Error +{ + Error() : dt(0), L1(0), L2(0), Linf(0) {} + Real dt, L1, L2, Linf; +}; + +vector<Error> verify(baseStepper_t stepper, int const stages, StepperSettings& S, const Real dt=1.0, const Real scale=0.5) +{ + vector<Error> error(stages); + S.dt = dt; + for (int s = 0; s < stages; ++s) + { + size_t const N = static_cast<size_t>(S.tFinal/S.dt)+1; + S.t = 0.0; + stepper->U() = 1.0; + Real& numerical = stepper->U()[0][0]; + for (size_t i = 0; i < N; ++i) + { + stepper->step(); + const Real absErr = abs(numerical - exact(S.t, DECAY)); + error[s].L1 += absErr; + error[s].L2 += absErr*absErr; + error[s].Linf = (error[s].Linf < absErr ? absErr : error[s].Linf); + } + error[s].L1 /= N; + error[s].L2 = sqrt(error[s].L2/N); + error[s].dt = S.dt; + S.dt *= scale; + } + return error; +} + +int main(int argc, const char** argv) +{ + // set up solution vector + vec_t U; + + // kernel instances + baseKernel_t dahlquist = new Dahlquist<vec_t>(DECAY); + baseKernel_t dahlquistLSRK3 = new DahlquistLSRK3<vec_t>(DECAY); + + // setup time steppers + ArgumentParser parser(argc, argv); + StepperSettings S(parser); + + vector<pair<pair<string,Real>, baseStepper_t> > timeStepperList; + timeStepperList.push_back(make_pair(make_pair("Euler",1.0), new Euler<vec_t>(U, S, dahlquist))); + timeStepperList.push_back(make_pair(make_pair("RK4",4.0), new RK4<vec_t>(U, S, dahlquist))); + timeStepperList.push_back(make_pair(make_pair("LSRK3",3.0), new LSRK3<vec_t>(U, S, dahlquistLSRK3))); + // timeStepperList.push_back(make_pair(make_pair("RKF45",5.0), new RKF45<vec_t>(U, S, dahlquist))); + // timeStepperList.push_back(make_pair(make_pair("RKV56",6.0), new RKV56<vec_t>(U, S, dahlquist))); +#ifdef _USE_SUNDIALS_ + timeStepperList.push_back(make_pair(make_pair("BDF",5.0), new BDF<vec_t>(U, S, dahlquist))); +#endif /* _USE_SUNDIALS_ */ + + for (size_t i = 0; i < timeStepperList.size(); ++i) + { + vector<Error> error = verify(timeStepperList[i].second, 7, S); + ofstream save(timeStepperList[i].first.first + ".dat"); + save.setf(std::ios::scientific, std::ios::floatfield); + auto expected = [&](const Real x){ return error[0].L1*pow(x, timeStepperList[i].first.second); }; + for (size_t j = 0; j < error.size(); ++j) + save << error[j].dt << '\t' << 1.0/error[j].dt << '\t' << error[j].L1 << '\t' << error[j].L2 << '\t' << error[j].Linf << '\t' << expected(error[j].dt) << endl; + save.close(); + } + + for (size_t i = 0; i < timeStepperList.size(); ++i) + delete timeStepperList[i].second; + delete dahlquist; + delete dahlquistLSRK3; + + return 0; +}