Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More efficient PERK implementation #2180

Merged
merged 14 commits into from
Dec 1, 2024
4 changes: 2 additions & 2 deletions examples/structured_1d_dgsem/elixir_burgers_perk3.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Convex and ECOS are imported because they are used for finding the optimal time step and optimal
# monomial coefficients in the stability polynomial of P-ERK time integrators.
# monomial coefficients in the stability polynomial of PERK time integrators.
using Convex, ECOS

# NLsolve is imported to solve the system of nonlinear equations to find the coefficients
# in the Butcher tableau in the third order P-ERK time integrator.
# in the Butcher tableau in the third order PERK time integrator.
using NLsolve

using OrdinaryDiffEq
Expand Down
2 changes: 1 addition & 1 deletion examples/tree_1d_dgsem/elixir_advection_perk2.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

# Convex and ECOS are imported because they are used for finding the optimal time step and optimal
# monomial coefficients in the stability polynomial of P-ERK time integrators.
# monomial coefficients in the stability polynomial of PERK time integrators.
using Convex, ECOS

using OrdinaryDiffEq
Expand Down
2 changes: 1 addition & 1 deletion ext/TrixiConvexECOSExt.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Package extension for adding Convex-based features to Trixi.jl
module TrixiConvexECOSExt

# Required for coefficient optimization in P-ERK scheme integrators
# Required for coefficient optimization in PERK scheme integrators
if isdefined(Base, :get_extension)
using Convex: MOI, solve!, Variable, minimize, evaluate
using ECOS: Optimizer
Expand Down
2 changes: 1 addition & 1 deletion ext/TrixiNLsolveExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
module TrixiNLsolveExt

# Required for finding coefficients in Butcher tableau in the third order of
# P-ERK scheme integrators
# PERK scheme integrators
if isdefined(Base, :get_extension)
using NLsolve: nlsolve
else
Expand Down
70 changes: 20 additions & 50 deletions src/time_integration/paired_explicit_runge_kutta/methods_PERK2.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ function compute_PairedExplicitRK2_butcher_tableau(num_stages, eig_vals, tspan,
# - 2 Since first entry of A is always zero (explicit method) and second is given by c_2 (consistency)
coeffs_max = num_stages - 2

a_matrix = zeros(coeffs_max, 2)
a_matrix[:, 1] = c[3:end]
a_matrix = zeros(2, coeffs_max)
JoshuaLampert marked this conversation as resolved.
Show resolved Hide resolved
a_matrix[1, :] = c[3:end]

consistency_order = 2

Expand All @@ -56,8 +56,8 @@ function compute_PairedExplicitRK2_butcher_tableau(num_stages, eig_vals, tspan,
num_monomial_coeffs = length(monomial_coeffs)
@assert num_monomial_coeffs == coeffs_max
A = compute_a_coeffs(num_stages, stage_scaling_factors, monomial_coeffs)
a_matrix[:, 1] -= A
a_matrix[:, 2] = A
a_matrix[1, :] -= A
a_matrix[2, :] = A
end

return a_matrix, c, dt_opt
Expand All @@ -78,8 +78,8 @@ function compute_PairedExplicitRK2_butcher_tableau(num_stages,
# - 2 Since first entry of A is always zero (explicit method) and second is given by c_2 (consistency)
coeffs_max = num_stages - 2

a_matrix = zeros(coeffs_max, 2)
a_matrix[:, 1] = c[3:end]
a_matrix = zeros(2, coeffs_max)
a_matrix[1, :] = c[3:end]

path_monomial_coeffs = joinpath(base_path_monomial_coeffs,
"gamma_" * string(num_stages) * ".txt")
Expand All @@ -91,8 +91,8 @@ function compute_PairedExplicitRK2_butcher_tableau(num_stages,
@assert num_monomial_coeffs == coeffs_max
A = compute_a_coeffs(num_stages, stage_scaling_factors, monomial_coeffs)

a_matrix[:, 1] -= A
a_matrix[:, 2] = A
a_matrix[1, :] -= A
a_matrix[2, :] = A

return a_matrix, c
end
Expand Down Expand Up @@ -140,7 +140,7 @@ mutable struct PairedExplicitRK2 <: AbstractPairedExplicitRKSingle
bS::Float64
cS::Float64
dt_opt::Union{Float64, Nothing}
end # struct PairedExplicitRK2
end

# Constructor that reads the coefficients from a file
function PairedExplicitRK2(num_stages, base_path_monomial_coeffs::AbstractString,
Expand Down Expand Up @@ -200,9 +200,8 @@ mutable struct PairedExplicitRK2Integrator{RealT <: Real, uType, Params, Sol, F,
finalstep::Bool # added for convenience
dtchangeable::Bool
force_stepfail::Bool
# PairedExplicitRK2 stages:
# Additional PERK register
k1::uType
k_higher::uType
end

function init(ode::ODEProblem, alg::PairedExplicitRK2;
Expand All @@ -211,9 +210,7 @@ function init(ode::ODEProblem, alg::PairedExplicitRK2;
du = zero(u0)
u_tmp = zero(u0)

# PairedExplicitRK2 stages
k1 = zero(u0)
k_higher = zero(u0)
k1 = zero(u0) # Additional PERK register

t0 = first(ode.tspan)
tdir = sign(ode.tspan[end] - ode.tspan[1])
Expand All @@ -226,7 +223,7 @@ function init(ode::ODEProblem, alg::PairedExplicitRK2;
ode.tspan;
kwargs...),
false, true, false,
k1, k_higher)
k1)

# initialize callbacks
if callback isa CallbackSet
Expand Down Expand Up @@ -262,48 +259,21 @@ function step!(integrator::PairedExplicitRK2Integrator)
end

@trixi_timeit timer() "Paired Explicit Runge-Kutta ODE integration step" begin
# k1
integrator.f(integrator.du, integrator.u, prob.p, integrator.t)
@threaded for i in eachindex(integrator.du)
integrator.k1[i] = integrator.du[i] * integrator.dt
end

# Construct current state
@threaded for i in eachindex(integrator.u)
integrator.u_tmp[i] = integrator.u[i] + alg.c[2] * integrator.k1[i]
end
# k2
integrator.f(integrator.du, integrator.u_tmp, prob.p,
integrator.t + alg.c[2] * integrator.dt)

@threaded for i in eachindex(integrator.du)
integrator.k_higher[i] = integrator.du[i] * integrator.dt
end
# First and second stage are identical across all single/standalone PERK methods
PERK_k1!(integrator, prob.p)
PERK_k2!(integrator, prob.p, alg)

# Higher stages
for stage in 3:(alg.num_stages)
# Construct current state
@threaded for i in eachindex(integrator.u)
integrator.u_tmp[i] = integrator.u[i] +
alg.a_matrix[stage - 2, 1] *
integrator.k1[i] +
alg.a_matrix[stage - 2, 2] *
integrator.k_higher[i]
end

integrator.f(integrator.du, integrator.u_tmp, prob.p,
integrator.t + alg.c[stage] * integrator.dt)

@threaded for i in eachindex(integrator.du)
integrator.k_higher[i] = integrator.du[i] * integrator.dt
end
PERK_ki!(integrator, prob.p, alg, stage)
end

@threaded for i in eachindex(integrator.u)
integrator.u[i] += alg.b1 * integrator.k1[i] +
alg.bS * integrator.k_higher[i]
integrator.u[i] += integrator.dt *
(alg.b1 * integrator.k1[i] +
alg.bS * integrator.du[i])
end
end # PairedExplicitRK2 step
end

integrator.iter += 1
integrator.t += integrator.dt
Expand Down
117 changes: 48 additions & 69 deletions src/time_integration/paired_explicit_runge_kutta/methods_PERK3.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ function compute_PairedExplicitRK3_butcher_tableau(num_stages, tspan,
monomial_coeffs, c;
verbose)
end
# Fill A-matrix in P-ERK style
a_matrix = zeros(num_stages - 2, 2)
a_matrix[:, 1] = c[3:end]
a_matrix[:, 1] -= a_unknown
a_matrix[:, 2] = a_unknown
# Fill A-matrix in PERK style
a_matrix = zeros(2, num_stages - 2)
a_matrix[1, :] = c[3:end]
a_matrix[1, :] -= a_unknown
a_matrix[2, :] = a_unknown

return a_matrix, c, dt_opt
end
Expand All @@ -80,8 +80,8 @@ function compute_PairedExplicitRK3_butcher_tableau(num_stages,
# - 2 Since First entry of A is always zero (explicit method) and second is given by c_2 (consistency)
a_coeffs_max = num_stages - 2

a_matrix = zeros(a_coeffs_max, 2)
a_matrix[:, 1] = c[3:end]
a_matrix = zeros(2, a_coeffs_max)
a_matrix[1, :] = c[3:end]

path_a_coeffs = joinpath(base_path_a_coeffs,
"a_" * string(num_stages) * ".txt")
Expand All @@ -91,9 +91,9 @@ function compute_PairedExplicitRK3_butcher_tableau(num_stages,
num_a_coeffs = size(a_coeffs, 1)

@assert num_a_coeffs == a_coeffs_max
# Fill A-matrix in P-ERK style
a_matrix[:, 1] -= a_coeffs
a_matrix[:, 2] = a_coeffs
# Fill A-matrix in PERK style
a_matrix[1, :] -= a_coeffs
a_matrix[2, :] = a_coeffs

return a_matrix, c
end
Expand All @@ -107,7 +107,7 @@ end
verbose = false, cS2 = 1.0f0)

Parameters:
- `num_stages` (`Int`): Number of stages in the paired explicit Runge-Kutta (P-ERK) method.
- `num_stages` (`Int`): Number of stages in the paired explicit Runge-Kutta (PERK) method.
- `base_path_a_coeffs` (`AbstractString`): Path to a file containing some coefficients in the A-matrix in
the Butcher tableau of the Runge Kutta method.
The matrix should be stored in a text file at `joinpath(base_path_a_coeffs, "a_$(num_stages).txt")` and separated by line breaks.
Expand All @@ -122,7 +122,7 @@ end
s is the number of stages, default is 1.0f0.

The following structures and methods provide an implementation of
the third-order paired explicit Runge-Kutta (P-ERK) method
the third-order paired explicit Runge-Kutta (PERK) method
optimized for a certain simulation setup (PDE, IC & BC, Riemann Solver, DG Solver).
The original paper is
- Nasab, Vermeire (2022)
Expand All @@ -141,7 +141,7 @@ mutable struct PairedExplicitRK3 <: AbstractPairedExplicitRKSingle
a_matrix::Matrix{Float64}
c::Vector{Float64}
dt_opt::Union{Float64, Nothing}
end # struct PairedExplicitRK3
end

# Constructor for previously computed A Coeffs
function PairedExplicitRK3(num_stages, base_path_a_coeffs::AbstractString,
Expand Down Expand Up @@ -195,9 +195,9 @@ mutable struct PairedExplicitRK3Integrator{RealT <: Real, uType, Params, Sol, F,
finalstep::Bool # added for convenience
dtchangeable::Bool
force_stepfail::Bool
# PairedExplicitRK stages:
# Additional PERK3 registers
k1::uType
k_higher::uType
kS1::uType
end

function init(ode::ODEProblem, alg::PairedExplicitRK3;
Expand All @@ -206,9 +206,9 @@ function init(ode::ODEProblem, alg::PairedExplicitRK3;
du = zero(u0)
u_tmp = zero(u0)

# PairedExplicitRK stages
# Additional PERK3 registers
k1 = zero(u0)
k_higher = zero(u0)
kS1 = zero(u0)

t0 = first(ode.tspan)
tdir = sign(ode.tspan[end] - ode.tspan[1])
Expand All @@ -221,7 +221,7 @@ function init(ode::ODEProblem, alg::PairedExplicitRK3;
ode.tspan;
kwargs...),
false, true, false,
k1, k_higher)
k1, kS1)

# initialize callbacks
if callback isa CallbackSet
Expand Down Expand Up @@ -258,72 +258,42 @@ function step!(integrator::PairedExplicitRK3Integrator)
end

@trixi_timeit timer() "Paired Explicit Runge-Kutta ODE integration step" begin
# k1
integrator.f(integrator.du, integrator.u, prob.p, integrator.t)
@threaded for i in eachindex(integrator.du)
integrator.k1[i] = integrator.du[i] * integrator.dt
end

# Construct current state
@threaded for i in eachindex(integrator.du)
integrator.u_tmp[i] = integrator.u[i] + alg.c[2] * integrator.k1[i]
end
# k2
integrator.f(integrator.du, integrator.u_tmp, prob.p,
integrator.t + alg.c[2] * integrator.dt)
# First and second stage are identical across all single/standalone PERK methods
PERK_k1!(integrator, prob.p)
PERK_k2!(integrator, prob.p, alg)

@threaded for i in eachindex(integrator.du)
integrator.k_higher[i] = integrator.du[i] * integrator.dt
end

# Higher stages
for stage in 3:(alg.num_stages - 1)
# Construct current state
@threaded for i in eachindex(integrator.du)
integrator.u_tmp[i] = integrator.u[i] +
alg.a_matrix[stage - 2, 1] *
integrator.k1[i] +
alg.a_matrix[stage - 2, 2] *
integrator.k_higher[i]
end

integrator.f(integrator.du, integrator.u_tmp, prob.p,
integrator.t + alg.c[stage] * integrator.dt)

@threaded for i in eachindex(integrator.du)
integrator.k_higher[i] = integrator.du[i] * integrator.dt
end
PERK_ki!(integrator, prob.p, alg, stage)
end

# Last stage
@threaded for i in eachindex(integrator.du)
integrator.u_tmp[i] = integrator.u[i] +
alg.a_matrix[alg.num_stages - 2, 1] *
integrator.k1[i] +
alg.a_matrix[alg.num_stages - 2, 2] *
integrator.k_higher[i]
# We need to store `du` of the S-1 stage in `kS1` for the final update:
@threaded for i in eachindex(integrator.u)
integrator.kS1[i] = integrator.du[i]
end

integrator.f(integrator.du, integrator.u_tmp, prob.p,
integrator.t + alg.c[alg.num_stages] * integrator.dt)
PERK_ki!(integrator, prob.p, alg, alg.num_stages)

@threaded for i in eachindex(integrator.u)
# "Own" PairedExplicitRK based on SSPRK33.
# Note that 'k_higher' carries the values of K_{S-1}
# Note that 'kS1' carries the values of K_{S-1}
# and that we construct 'K_S' "in-place" from 'integrator.du'
integrator.u[i] += (integrator.k1[i] + integrator.k_higher[i] +
4.0 * integrator.du[i] * integrator.dt) / 6.0
integrator.u[i] += integrator.dt *
(integrator.k1[i] + integrator.kS1[i] +
4.0 * integrator.du[i]) / 6.0
end
end # PairedExplicitRK step timer
end

integrator.iter += 1
integrator.t += integrator.dt

# handle callbacks
if callbacks isa CallbackSet
for cb in callbacks.discrete_callbacks
if cb.condition(integrator.u, integrator.t, integrator)
cb.affect!(integrator)
@trixi_timeit timer() "Step-Callbacks" begin
DanielDoehring marked this conversation as resolved.
Show resolved Hide resolved
# handle callbacks
if callbacks isa CallbackSet
foreach(callbacks.discrete_callbacks) do cb
if cb.condition(integrator.u, integrator.t, integrator)
cb.affect!(integrator)
end
return nothing
end
end
end
Expand All @@ -334,4 +304,13 @@ function step!(integrator::PairedExplicitRK3Integrator)
terminate!(integrator)
end
end

function Base.resize!(integrator::PairedExplicitRK3Integrator, new_size)
resize!(integrator.u, new_size)
resize!(integrator.du, new_size)
resize!(integrator.u_tmp, new_size)

resize!(integrator.k1, new_size)
resize!(integrator.kS1, new_size)
end
DanielDoehring marked this conversation as resolved.
Show resolved Hide resolved
end # @muladd
Loading
Loading