Path: blob/main/src/time_integration/relaxation_methods/methods_subdiagonal.jl
5591 views
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).1# Since these FMAs can increase the performance of many numerical algorithms,2# we need to opt-in explicitly.3# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.4@muladd begin5#! format: noindent67@doc raw"""8SubDiagonalAlgorithm910Abstract type for sub-diagonal Runge-Kutta methods, i.e.,11methods with a Butcher tableau of the form12```math13\begin{array}14{c|c|c c c c c}15i & \boldsymbol c & & & A & & \\16\hline171 & 0 & & & & &\\182 & c_2 & c_2 & & & & \\193 & c_3 & 0 & c_3 & & & \\204 & c_4 & 0 & 0 & c_4 & \\21\vdots & & \vdots & \vdots & \ddots & \ddots & \\22S & c_S & 0 & & \dots & 0 & c_S \\23\hline24& & b_1 & b_2 & \dots & & b_S25\end{array}26```2728Currently implemented are the third-order, three-stage method by Ralston [`Ralston3`](@ref)29and the canonical fourth-order, four-stage method by Kutta [`RK44`](@ref).30"""31abstract type SubDiagonalAlgorithm <: AbstractTimeIntegrationAlgorithm end3233"""34SubDiagonalRelaxationAlgorithm3536Abstract type for sub-diagonal Runge-Kutta algorithms (see [`SubDiagonalAlgorithm`](@ref))37with relaxation to achieve entropy conservation/stability.38In addition to the standard Runge-Kutta method, these algorithms are equipped with a39relaxation solver [`AbstractRelaxationSolver`](@ref) which is used to compute the relaxation parameter ``\\gamma``.40This allows the relaxation methods to suppress entropy defects due to the time stepping.4142For details on the relaxation procedure, see43- Ketcheson (2019)44Relaxation Runge-Kutta Methods: Conservation and Stability for Inner-Product Norms45[DOI: 10.1137/19M1263662](https://doi.org/10.1137/19M1263662)46- Ranocha et al. (2020)47Relaxation Runge-Kutta Methods: Fully Discrete Explicit Entropy-Stable Schemes for the Compressible Euler and Navier-Stokes Equations48[DOI: 10.1137/19M1263480](https://doi.org/10.1137/19M1263480)4950Currently implemented are the third-order, three-stage method by Ralston [`Ralston3`](@ref)51and the canonical fourth-order, four-stage method by Kutta [`RK44`](@ref).52"""53abstract type SubDiagonalRelaxationAlgorithm <:54AbstractRelaxationTimeIntegrationAlgorithm end5556"""57Ralston3()5859Relaxation version of Ralston's third-order Runge-Kutta method, implemented as a [`SubDiagonalAlgorithm`](@ref).60The weight vector is given by ``\\boldsymbol b = [2/9, 1/3, 4/9]`` and the61abscissae/timesteps by ``\\boldsymbol c = [0.0, 0.5, 0.75]``.6263This method has minimum local error bound among the ``S=p=3`` methods.64- Ralston (1962)65Runge-Kutta Methods with Minimum Error Bounds66[DOI: 10.1090/S0025-5718-1962-0150954-0](https://doi.org/10.1090/S0025-5718-1962-0150954-0)67"""68struct Ralston3 <: SubDiagonalAlgorithm69b::SVector{3, Float64}70c::SVector{3, Float64}71end72function Ralston3()73b = SVector(2 / 9, 1 / 3, 4 / 9)74c = SVector(0.0, 0.5, 0.75)7576return Ralston3(b, c)77end7879"""80RelaxationRalston3(; relaxation_solver = RelaxationSolverNewton())8182Relaxation version of Ralston's third-order Runge-Kutta method [`Ralston3()`](@ref),83implemented as a [`SubDiagonalRelaxationAlgorithm`](@ref).84The default relaxation solver [`AbstractRelaxationSolver`](@ref) is [`RelaxationSolverNewton`](@ref).85"""86struct RelaxationRalston3{AbstractRelaxationSolver} <: SubDiagonalRelaxationAlgorithm87sub_diagonal_alg::Ralston388relaxation_solver::AbstractRelaxationSolver89end90function RelaxationRalston3(; relaxation_solver = RelaxationSolverNewton())91return RelaxationRalston3{typeof(relaxation_solver)}(Ralston3(), relaxation_solver)92end9394"""95RK44()9697The canonical fourth-order Runge-Kutta method, implemented as a [`SubDiagonalAlgorithm`](@ref).98The weight vector is given by ``\\boldsymbol b = [1/6, 1/3, 1/3, 1/6]`` and the99abscissae/timesteps by ``\\boldsymbol c = [0.0, 0.5, 0.5, 1.0]``.100"""101struct RK44 <: SubDiagonalAlgorithm102b::SVector{4, Float64}103c::SVector{4, Float64}104end105function RK44()106b = SVector(1 / 6, 1 / 3, 1 / 3, 1 / 6)107c = SVector(0.0, 0.5, 0.5, 1.0)108109return RK44(b, c)110end111112"""113RelaxationRK44(; relaxation_solver = RelaxationSolverNewton())114115Relaxation version of the canonical fourth-order Runge-Kutta method [`RK44()`](@ref),116implemented as a [`SubDiagonalRelaxationAlgorithm`](@ref).117The default relaxation solver [`AbstractRelaxationSolver`](@ref) is [`RelaxationSolverNewton`](@ref).118"""119struct RelaxationRK44{AbstractRelaxationSolver} <: SubDiagonalRelaxationAlgorithm120sub_diagonal_alg::RK44121relaxation_solver::AbstractRelaxationSolver122end123function RelaxationRK44(; relaxation_solver = RelaxationSolverNewton())124return RelaxationRK44{typeof(relaxation_solver)}(RK44(), relaxation_solver)125end126127# This struct is needed to fake https://github.com/SciML/OrdinaryDiffEq.jl/blob/0c2048a502101647ac35faabd80da8a5645beac7/src/integrators/type.jl#L77128# This implements the interface components described at129# https://diffeq.sciml.ai/v6.8/basics/integrator/#Handing-Integrators-1130# which are used in Trixi.jl.131mutable struct SubDiagonalRelaxationIntegrator{RealT <: Real, uType <: AbstractVector,132Params, Sol, F, Alg,133SimpleIntegratorOptions,134AbstractRelaxationSolver} <:135RelaxationIntegrator136u::uType137du::uType138u_tmp::uType139t::RealT140dt::RealT # current time step141dtcache::RealT # ignored142iter::Int # current number of time steps (iteration)143p::Params # will be the semidiscretization from Trixi.jl144sol::Sol # faked145const f::F # `rhs` of the semidiscretization146const alg::Alg # `SubDiagonalRelaxationAlgorithm`147opts::SimpleIntegratorOptions148finalstep::Bool # added for convenience149# Addition for Relaxation methodology150direction::uType # RK update, i.e., sum of stages K_i times weights b_i151gamma::RealT # Relaxation parameter152S_old::RealT # Entropy of previous iterate153const relaxation_solver::AbstractRelaxationSolver154# Note: Could add another register which would store the summed-up155# dot products ∑ₖ (wₖ ⋅ kₖ) and then integrate only once and not per stage k156# Could also add option `recompute_entropy` for entropy-conservative problems157# to save redundant computations.158end159160function init(ode::ODEProblem, alg::SubDiagonalRelaxationAlgorithm;161dt, callback::Union{CallbackSet, Nothing} = nothing, kwargs...)162u = copy(ode.u0)163du = zero(u)164u_tmp = zero(u)165166t = first(ode.tspan)167iter = 0168169# For entropy relaxation170direction = zero(u)171gamma = one(eltype(u))172semi = ode.p173u_wrap = wrap_array(u, semi)174S_old = integrate(entropy, u_wrap, semi.mesh, semi.equations, semi.solver,175semi.cache)176177integrator = SubDiagonalRelaxationIntegrator(u, du, u_tmp, t, dt, zero(dt), iter,178ode.p, (prob = ode,), ode.f,179alg.sub_diagonal_alg,180SimpleIntegratorOptions(callback,181ode.tspan;182kwargs...),183false,184direction, gamma, S_old,185alg.relaxation_solver)186187initialize_callbacks!(callback, integrator)188189return integrator190end191192function step!(integrator::SubDiagonalRelaxationIntegrator)193@unpack prob = integrator.sol194@unpack alg = integrator195t_end = last(prob.tspan)196callbacks = integrator.opts.callback197198@assert !integrator.finalstep199if isnan(integrator.dt)200error("time step size `dt` is NaN")201end202203limit_dt!(integrator, t_end)204205@trixi_timeit timer() "Relaxation sub-diagonal RK integration step" begin206mesh, equations, dg, cache = mesh_equations_solver_cache(prob.p)207208u_wrap = wrap_array(integrator.u, prob.p)209u_tmp_wrap = wrap_array(integrator.u_tmp, prob.p)210211# First stage212integrator.f(integrator.du, integrator.u, prob.p, integrator.t)213# Try to enable optimizations due to `muladd` by computing this factor only once, see214# https://github.com/trixi-framework/Trixi.jl/pull/2480#discussion_r2224529532215b1_dt = alg.b[1] * integrator.dt216@threaded for i in eachindex(integrator.u)217integrator.direction[i] = b1_dt * integrator.du[i]218end219220du_wrap = wrap_array(integrator.du, prob.p)221# Entropy change due to first stage222dS = b1_dt * integrate_w_dot_stage(du_wrap, u_wrap, mesh, equations, dg, cache)223224# Second to last stage225for stage in 2:length(alg.c)226c_dt = alg.c[stage] * integrator.dt227@threaded for i in eachindex(integrator.u)228integrator.u_tmp[i] = integrator.u[i] + c_dt * integrator.du[i]229end230integrator.f(integrator.du, integrator.u_tmp, prob.p,231integrator.t + alg.c[stage] * integrator.dt)232b_dt = alg.b[stage] * integrator.dt233@threaded for i in eachindex(integrator.u)234integrator.direction[i] = integrator.direction[i] +235b_dt * integrator.du[i]236end237238# Entropy change due to current stage239dS += b_dt *240integrate_w_dot_stage(du_wrap, u_tmp_wrap, mesh, equations, dg, cache)241end242243direction_wrap = wrap_array(integrator.direction, prob.p)244245@trixi_timeit timer() "Relaxation solver" relaxation_solver!(integrator,246u_tmp_wrap, u_wrap,247direction_wrap, dS,248mesh, equations,249dg, cache,250integrator.relaxation_solver)251252integrator.iter += 1253update_t_relaxation!(integrator)254255# Do relaxed update256@threaded for i in eachindex(integrator.u)257integrator.u[i] = integrator.u[i] +258integrator.gamma * integrator.direction[i]259end260end261262@trixi_timeit timer() "Step-Callbacks" handle_callbacks!(callbacks, integrator)263264check_max_iter!(integrator)265266return nothing267end268269# used for AMR270function Base.resize!(integrator::SubDiagonalRelaxationIntegrator, new_size)271resize!(integrator.u, new_size)272resize!(integrator.du, new_size)273resize!(integrator.u_tmp, new_size)274# Relaxation addition275resize!(integrator.direction, new_size)276277return nothing278end279end # @muladd280281282