Skip to content

Commit 8a6349b

Browse files
MarcoArtianoDanielDoehringbenegeeranocha
authored
User defined RHS Splitting for IMEX (#2518)
* add user defined rhs splitting * testing some IMEX schemes * clean up * format * add hacky fix for amr * format * better splitting * add tests * rm old elixir * Delete examples/p4est_2d_dgsem/elixir_euler_imex_inertia_gravity_waves.jl * using ADTypes * make rhs call compatible for testing allocations * increase coverage * change 1,2 to stiff and nonstiff * add comments and fmt * add comments, fix docs * add comments and fix docs * Update examples/p4est_2d_dgsem/elixir_euler_imex_warm_bubble.jl * Update examples/p4est_2d_dgsem/elixir_euler_imex_warm_bubble.jl * Update examples/p4est_2d_dgsem/elixir_euler_imex_warm_bubble.jl * Apply suggestions from code review * Update examples/p4est_2d_dgsem/elixir_euler_imex_warm_bubble.jl * Apply suggestions from code review Co-authored-by: Daniel Doehring <doehringd2@gmail.com> * Update src/semidiscretization/semidiscretization_split.jl * add other suggestions * Update src/semidiscretization/semidiscretization_split.jl * fmt * Apply suggestions from code review Co-authored-by: Benedict <135045760+benegee@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Hendrik Ranocha <ranocha@users.noreply.github.com> * fix indentation, tuple of solvers and fix test values * move definition of dt, reduce output * add ndims for plotting * add comment on bc's and test unit * change time integration method and update test values * change to KenCarp4 and update test values * fix tests * update elixir and test * fix tests * add news entry * fix test * reduce boilerplate * Apply suggestions from code review * Update src/semidiscretization/semidiscretization_split.jl * Update src/semidiscretization/semidiscretization_split.jl * Apply suggestions from code review * Apply suggestions from code review * Update src/semidiscretization/semidiscretization_split.jl * Update src/semidiscretization/semidiscretization_split.jl * Update src/semidiscretization/semidiscretization_split.jl * Apply suggestions from code review * Update src/semidiscretization/semidiscretization_split.jl * Update src/semidiscretization/semidiscretization_split.jl * Update src/semidiscretization/semidiscretization_split.jl * Update src/semidiscretization/semidiscretization_split.jl * Update src/semidiscretization/semidiscretization_split.jl * Update src/semidiscretization/semidiscretization_split.jl * print boundary conditions * fix tests * Apply suggestions from code review Co-authored-by: Daniel Doehring <doehringd2@gmail.com> * format * renaming in the elixir * fix link and rename bc * Update src/semidiscretization/semidiscretization_split.jl * add NEWS * Update examples/p4est_2d_dgsem/elixir_euler_imex_warm_bubble.jl Co-authored-by: Hendrik Ranocha <ranocha@users.noreply.github.com> * Update src/semidiscretization/semidiscretization_split.jl Co-authored-by: Hendrik Ranocha <ranocha@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Hendrik Ranocha <ranocha@users.noreply.github.com> * fix docs, and rename rhs! to rhs_nonstiff! * fix tests * Apply suggestion from @ranocha --------- Co-authored-by: Daniel Doehring <doehringd2@gmail.com> Co-authored-by: Benedict <135045760+benegee@users.noreply.github.com> Co-authored-by: Hendrik Ranocha <ranocha@users.noreply.github.com>
1 parent ccb7fe5 commit 8a6349b

6 files changed

Lines changed: 558 additions & 0 deletions

File tree

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ The new equation types `LinearDiffusionEquation1D` and `LinearDiffusionEquation2
1616
- A new AMR indicator `IndicatorNodalFunction` is introduced, which allows AMR depending on the solution, space, and time. This can be useful, for example, for testing AMR implementations, but also when the solution behavior is known a priori ([#2881]).
1717
- GPU support extended to include AMD GPU with a buildkite workflow using `TRIXI_TEST=AMDGPU` ([#2834]).
1818
- Support for 3D subcell limiting was extended by local limiting for nonperiodic `TreeMesh`es ([#2878]).
19+
- Support for user-defined RHS splitting for IMEX methods via SemidiscretizationHyperbolicSplit ([#2518]). The splitting follows the form `y_t = f_1(y) + f_2(y)`, allowing users to define separate solvers for the stiff (`f_1`) and non-stiff (`f_2`) parts of the right-hand side. Boundary conditions and source terms can be specified independently for the stiff and non-stiff parts.
1920

2021
## Changes when updating to v0.16 from v0.15.x
2122

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
# This elixir demonstrates how an implicit-explicit (IMEX) time integration scheme can be applied to the stiff and non-stiff parts of a right hand side, respectively.
2+
# We define separate solvers, boundary conditions, and source terms, and create a `SemidiscretizationHyperbolicSplit`, which will return a `SplitODEProblem` compatible with OrdinaryDiffEqBDF.jl, cf. https://docs.sciml.ai/OrdinaryDiffEq/stable/imex/IMEXBDF.
3+
# Note: This is currently more of a proof of concept and not particularly useful in practice, as fully explicit methods are still faster at the moment.
4+
5+
using Trixi
6+
using OrdinaryDiffEqBDF
7+
using ADTypes # This is needed to set 'autodiff = AutoFiniteDiff()' in the ODE solver.
8+
using LinearSolve
9+
10+
function initial_condition_warm_bubble(x, t, equations::CompressibleEulerEquations2D)
11+
g = 9.81
12+
c_p = 1004.0
13+
c_v = 717.0
14+
15+
# center of perturbation
16+
center_x = 10000.0
17+
center_z = 2000.0
18+
# radius of perturbation
19+
radius = 2000.0
20+
# distance of current x to center of perturbation
21+
r = sqrt((x[1] - center_x)^2 + (x[2] - center_z)^2)
22+
23+
# perturbation in potential temperature
24+
potential_temperature_ref = 300.0
25+
potential_temperature_perturbation = 0.0
26+
if r <= radius
27+
potential_temperature_perturbation = 2 * cospi(0.5 * r / radius)^2
28+
end
29+
potential_temperature = potential_temperature_ref + potential_temperature_perturbation
30+
31+
# Exner pressure, solves hydrostatic equation for x[2]
32+
exner = 1 - g / (c_p * potential_temperature) * x[2]
33+
34+
# pressure
35+
p_0 = 100_000.0 # reference pressure
36+
R = c_p - c_v # gas constant (dry air)
37+
p = p_0 * exner^(c_p / R)
38+
39+
# temperature
40+
T = potential_temperature * exner
41+
42+
# density
43+
rho = p / (R * T)
44+
45+
v1 = 20.0
46+
v2 = 0.0
47+
return prim2cons(SVector(rho, v1, v2, p), equations)
48+
end
49+
50+
# The standard Trixi.jl implementation of the slip wall boundary condition is not directly
51+
# compatible with this general splitting approach, since it is based on Toro's Riemann solver
52+
# which always returns boundary condition values for the entire right-hand side.
53+
# This function computes the boundary condition based on the surface flux function of the
54+
# explicit and implicit parts, where the splitting has been defined and thus accounts for it.
55+
@inline function boundary_condition_slip_wall_simple(u_inner,
56+
normal_direction::AbstractVector,
57+
x, t,
58+
surface_flux_function,
59+
equations::CompressibleEulerEquations2D)
60+
# normalize the outward pointing direction
61+
normal = normal_direction / Trixi.norm(normal_direction)
62+
63+
# compute the normal momentum from
64+
# u = (rho, rho v1, rho v2, rho e)
65+
u_normal = normal[1] * u_inner[2] + normal[2] * u_inner[3]
66+
67+
# create the "external" boundary solution state
68+
u_boundary = SVector(u_inner[1],
69+
u_inner[2] - 2 * u_normal * normal[1],
70+
u_inner[3] - 2 * u_normal * normal[2],
71+
u_inner[4])
72+
73+
# calculate the boundary flux
74+
flux = surface_flux_function(u_inner, u_boundary, normal_direction, equations)
75+
76+
return flux
77+
end
78+
79+
# The total flux is split into:
80+
# - Fast (implicit/stiff) part: Contains all pressure-related terms responsible for acoustic waves.
81+
# Uses LMARS for surface fluxes and Kennedy-Gruber for volume fluxes.
82+
# - Slow (explicit/non-stiff) part: Contains convective terms (advection).
83+
@inline function flux_lmars_fast(u_ll, u_rr, normal_direction::AbstractVector,
84+
equations::CompressibleEulerEquations2D)
85+
# Reference speed of sound
86+
a = 340
87+
# Unpack left and right state
88+
rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations)
89+
rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations)
90+
91+
v_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2]
92+
v_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2]
93+
94+
norm_ = Trixi.norm(normal_direction)
95+
96+
rho = 0.5f0 * (rho_ll + rho_rr)
97+
98+
p_interface = 0.5f0 * (p_ll + p_rr) - 0.5f0 * a * rho * (v_rr - v_ll) / norm_
99+
v_interface = 0.5f0 * (v_ll + v_rr) - 1 / (2 * a * rho) * (p_rr - p_ll) * norm_
100+
101+
if (v_interface > 0)
102+
f4 = p_ll * v_interface
103+
else
104+
f4 = p_rr * v_interface
105+
end
106+
107+
return SVector(zero(eltype(u_ll)),
108+
p_interface * normal_direction[1],
109+
p_interface * normal_direction[2],
110+
f4)
111+
end
112+
113+
@inline function flux_lmars_slow(u_ll, u_rr, normal_direction::AbstractVector,
114+
equations::CompressibleEulerEquations2D)
115+
# Reference speed of sound
116+
a = 340
117+
# Unpack left and right state
118+
rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations)
119+
rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations)
120+
121+
v_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2]
122+
v_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2]
123+
124+
norm_ = Trixi.norm(normal_direction)
125+
126+
rho = 0.5f0 * (rho_ll + rho_rr)
127+
128+
v_interface = 0.5f0 * (v_ll + v_rr) - 1 / (2 * a * rho) * (p_rr - p_ll) * norm_
129+
130+
if (v_interface > 0)
131+
f1, f2, f3, f4 = u_ll * v_interface
132+
else
133+
f1, f2, f3, f4 = u_rr * v_interface
134+
end
135+
136+
return SVector(f1, f2, f3, f4)
137+
end
138+
139+
@inline function flux_kennedy_gruber_slow(u_ll, u_rr, normal_direction::AbstractVector,
140+
equations::CompressibleEulerEquations2D)
141+
# Unpack left and right state
142+
rho_e_ll = last(u_ll)
143+
rho_e_rr = last(u_rr)
144+
rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations)
145+
rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations)
146+
147+
# Average each factor of products in flux
148+
rho_avg = 0.5f0 * (rho_ll + rho_rr)
149+
v1_avg = 0.5f0 * (v1_ll + v1_rr)
150+
v2_avg = 0.5f0 * (v2_ll + v2_rr)
151+
v_dot_n_avg = v1_avg * normal_direction[1] + v2_avg * normal_direction[2]
152+
p_avg = 0.5f0 * (p_ll + p_rr)
153+
e_avg = 0.5f0 * (rho_e_ll / rho_ll + rho_e_rr / rho_rr)
154+
155+
# Calculate fluxes depending on normal_direction
156+
f1 = rho_avg * v_dot_n_avg
157+
f2 = f1 * v1_avg
158+
f3 = f1 * v2_avg
159+
f4 = f1 * e_avg
160+
161+
return SVector(f1, f2, f3, f4)
162+
end
163+
164+
@inline function flux_kennedy_gruber_fast(u_ll, u_rr, normal_direction::AbstractVector,
165+
equations::CompressibleEulerEquations2D)
166+
# Unpack left and right state
167+
rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations)
168+
rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations)
169+
170+
# Average each factor of products in flux
171+
v1_avg = 0.5f0 * (v1_ll + v1_rr)
172+
v2_avg = 0.5f0 * (v2_ll + v2_rr)
173+
v_dot_n_avg = v1_avg * normal_direction[1] + v2_avg * normal_direction[2]
174+
p_avg = 0.5f0 * (p_ll + p_rr)
175+
176+
# Calculate fluxes depending on normal_direction
177+
f2 = p_avg * normal_direction[1]
178+
f3 = p_avg * normal_direction[2]
179+
f4 = p_avg * v_dot_n_avg
180+
181+
return SVector(zero(eltype(u_ll)), f2, f3, f4)
182+
end
183+
184+
@inline function source_terms_gravity(u, x, t, equations::CompressibleEulerEquations2D)
185+
g = 9.81
186+
rho, _, rho_v2, _ = u
187+
return SVector(zero(eltype(u)), zero(eltype(u)), -g * rho, -g * rho_v2)
188+
end
189+
190+
gamma = 1004 / 717
191+
equations = CompressibleEulerEquations2D(gamma)
192+
193+
polydeg = 2
194+
basis = LobattoLegendreBasis(polydeg)
195+
196+
volume_integral_nonstiff = VolumeIntegralFluxDifferencing(flux_kennedy_gruber_slow)
197+
solver_nonstiff = DGSEM(basis, flux_lmars_slow, volume_integral_nonstiff)
198+
199+
volume_integral_stiff = VolumeIntegralFluxDifferencing(flux_kennedy_gruber_fast)
200+
solver_stiff = DGSEM(basis, flux_lmars_fast, volume_integral_stiff)
201+
202+
coordinates_min = (0.0, 0.0)
203+
coordinates_max = (20_000.0, 10_000.0)
204+
trees_per_dimension = (16, 8)
205+
mesh = P4estMesh(trees_per_dimension; polydeg = polydeg,
206+
coordinates_min = coordinates_min, coordinates_max = coordinates_max,
207+
periodicity = (true, false), initial_refinement_level = 0)
208+
209+
boundary_conditions = (;
210+
y_neg = boundary_condition_slip_wall_simple,
211+
y_pos = boundary_condition_slip_wall_simple)
212+
213+
initial_condition = initial_condition_warm_bubble
214+
215+
semi = SemidiscretizationHyperbolicSplit(mesh,
216+
(equations, equations),
217+
initial_condition,
218+
(solver_stiff, solver_nonstiff);
219+
boundary_conditions = (boundary_conditions,
220+
boundary_conditions),
221+
source_terms = (nothing, source_terms_gravity),)
222+
tspan = (0.0, 1000.0)
223+
ode = semidiscretize(semi, tspan)
224+
225+
summary_callback = SummaryCallback()
226+
227+
analysis_callback = AnalysisCallback(semi, interval = 1000)
228+
229+
alive_callback = AliveCallback(analysis_interval = 1000)
230+
231+
save_solution = SaveSolutionCallback(interval = 1000, solution_variables = cons2prim)
232+
233+
callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, alive_callback)
234+
235+
###############################################################################
236+
# run the simulation
237+
238+
# Tolerances for GMRES residual, see https://jso.dev/Krylov.jl/stable/solvers/unsymmetric/#Krylov.gmres
239+
atol_lin_solve = 1e-5
240+
rtol_lin_solve = 1e-5
241+
242+
# Jacobian-free Newton-Krylov (GMRES) solver, see
243+
# https://docs.sciml.ai/DiffEqDocs/stable/tutorials/advanced_ode_example/#Using-Jacobian-Free-Newton-Krylov
244+
linsolve = KrylovJL_GMRES(atol = atol_lin_solve, rtol = rtol_lin_solve)
245+
246+
# Use second-order implicit Runge-Kutta BDF, see
247+
# https://docs.sciml.ai/OrdinaryDiffEq/stable/imex/IMEXBDF/
248+
ode_alg = SBDF2(autodiff = AutoFiniteDiff(), linsolve = linsolve)
249+
250+
atol_ode_solve = 1e-4
251+
rtol_ode_solve = 1e-4
252+
sol = solve(ode, ode_alg;
253+
dt = 0.5,
254+
abstol = atol_ode_solve, reltol = rtol_ode_solve,
255+
ode_default_options()..., callback = callbacks);

src/Trixi.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ include("semidiscretization/semidiscretization_parabolic.jl")
158158
include("semidiscretization/semidiscretization_hyperbolic_parabolic.jl")
159159
include("semidiscretization/semidiscretization_euler_acoustics.jl")
160160
include("semidiscretization/semidiscretization_coupled.jl")
161+
include("semidiscretization/semidiscretization_split.jl")
161162
include("semidiscretization/semidiscretization_coupled_p4est.jl")
162163
include("time_integration/time_integration.jl")
163164
include("callbacks_step/callbacks_step.jl")
@@ -311,6 +312,8 @@ export SemidiscretizationParabolic
311312
export SemidiscretizationHyperbolicParabolic
312313
export have_constant_diffusivity, max_diffusivity
313314

315+
export SemidiscretizationHyperbolicSplit
316+
314317
export SemidiscretizationEulerAcoustics
315318

316319
export SemidiscretizationEulerGravity, ParametersEulerGravity,

0 commit comments

Comments
 (0)