Skip to content

Commit 891417c

Browse files
Fix ShallowWaterExnerEquations1D for negative / zero velocities (#101)
* compare against absolute values for the velocity * add symmetric dam break test case * Setup type conversion for sediment models and add type tests * apply formatter * update test values due to updated roe average for h_s * pass correct sediment height to eigenvalue computation
1 parent 75de71d commit 891417c

File tree

5 files changed

+204
-53
lines changed

5 files changed

+204
-53
lines changed

examples/tree_1d_dgsem/elixir_shallowwater_exner_channel.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ equations = ShallowWaterExnerEquations1D(gravity = 9.81, rho_f = 1.0, rho_s = 1.
1212
porosity = 0.4,
1313
sediment_model = GrassModel(A_g = 0.01))
1414

15-
# Initial condition for for a channel flow problem over a sediment hump.
15+
# Initial condition for a channel flow problem over a sediment hump.
1616
# An asymptotic solution based on the method of characteristics was derived under a rigid-lid
1717
# approximation in chapter 3.5.1 of the thesis:
1818
# - Justin Hudson (2001)
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
2+
using OrdinaryDiffEqSSPRK, OrdinaryDiffEqLowStorageRK
3+
using Trixi
4+
using TrixiShallowWater
5+
6+
###############################################################################
7+
# Semidiscretization of the shallow water exner equations for a subcritical symmetric dam break problem
8+
9+
equations = ShallowWaterExnerEquations1D(gravity = 9.81, rho_f = 0.3, rho_s = 1.0,
10+
porosity = 0.4,
11+
sediment_model = GrassModel(A_g = 0.01))
12+
13+
# Initial condition for a synthetic test case of a symmetric dam break problem.
14+
# The setup is taken from the paper:
15+
# - S. Martínez-Aranda, J. Murillo, P. García-Navarro (2021)
16+
# "Comparison of new efficient 2D models for the simulation of bedload transport using the augmented
17+
# Roe approach"
18+
# [DOI: 10.1016/j.advwatres.2021.103931](https://doi.org/10.1016/j.advwatres.2021.103931)
19+
function initial_condition_dam_break_symmetric(x, t,
20+
equations::ShallowWaterExnerEquations1D)
21+
# Setup initial perturbation of the water height
22+
if -0.5 <= x[1] <= 0.5
23+
h = 1.0
24+
else
25+
h = 0.2
26+
end
27+
28+
# Set constant values for the sediment height and zero momentum
29+
hv = 0.0
30+
h_b = 1.0
31+
32+
return SVector(h, hv, h_b)
33+
end
34+
35+
initial_condition = initial_condition_dam_break_symmetric
36+
37+
###############################################################################
38+
# Get the DG approximation space
39+
40+
volume_flux = (flux_ersing_etal, flux_nonconservative_ersing_etal)
41+
surface_flux = (FluxPlusDissipation(flux_ersing_etal, dissipation_roe),
42+
flux_nonconservative_ersing_etal)
43+
44+
basis = LobattoLegendreBasis(3)
45+
46+
indicator_sc = IndicatorHennemannGassner(equations, basis,
47+
alpha_max = 0.5,
48+
alpha_min = 0.001,
49+
alpha_smooth = true,
50+
variable = water_sediment_height)
51+
volume_integral = VolumeIntegralShockCapturingHG(indicator_sc;
52+
volume_flux_dg = volume_flux,
53+
volume_flux_fv = surface_flux)
54+
55+
solver = DGSEM(basis, surface_flux, volume_integral)
56+
57+
###############################################################################
58+
# Get the TreeMesh and setup a periodic mesh
59+
60+
coordinates_min = -4.0
61+
coordinates_max = 4.0
62+
mesh = TreeMesh(coordinates_min, coordinates_max,
63+
initial_refinement_level = 7,
64+
n_cells_max = 10_000,
65+
periodicity = true)
66+
67+
# Create the semi discretization object
68+
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
69+
70+
###############################################################################
71+
# ODE solver
72+
73+
tspan = (0.0, 0.6)
74+
ode = semidiscretize(semi, tspan)
75+
76+
###############################################################################
77+
# Callbacks
78+
79+
summary_callback = SummaryCallback()
80+
81+
analysis_interval = 1000
82+
analysis_callback = AnalysisCallback(semi, interval = analysis_interval)
83+
84+
alive_callback = AliveCallback(analysis_interval = analysis_interval)
85+
86+
stepsize_callback = StepsizeCallback(cfl = 1.0)
87+
88+
save_solution = SaveSolutionCallback(dt = 0.1,
89+
save_initial_solution = true,
90+
save_final_solution = true)
91+
92+
callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback,
93+
stepsize_callback, save_solution)
94+
95+
###############################################################################
96+
# run the simulation
97+
sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false);
98+
dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
99+
ode_default_options()..., callback = callbacks);

src/equations/shallow_water_exner_1d.jl

Lines changed: 54 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,9 @@ struct GrassModel{RealT} <: SedimentModel{RealT}
8080
m_g::RealT
8181
end
8282

83-
function GrassModel(; A_g, m_g = 3.0)
84-
return GrassModel(A_g, m_g)
83+
function GrassModel(; A_g, m_g = 3)
84+
RealT = promote_type(typeof(A_g), typeof(m_g))
85+
return GrassModel(RealT(A_g), RealT(m_g))
8586
end
8687

8788
@doc raw"""
@@ -97,7 +98,9 @@ An overview of different formulations to compute the sediment discharge can be f
9798
[DOI:10.1016/j.compfluid.2007.07.017](https://doi.org/10.1016/j.compfluid.2007.07.017)
9899
"""
99100
function MeyerPeterMueller(; theta_c, d_s)
100-
return ShieldsStressModel(0.0, 1.5, 0.0, 8.0, 1.0, 0.0, theta_c, d_s)
101+
RealT = promote_type(typeof(theta_c), typeof(d_s))
102+
return ShieldsStressModel(RealT(0.0), RealT(1.5), RealT(0.0), RealT(8.0),
103+
RealT(1.0), RealT(0.0), RealT(theta_c), RealT(d_s))
101104
end
102105

103106
@doc raw"""
@@ -228,9 +231,9 @@ A smooth initial condition used for convergence tests in combination with
228231
equations::ShallowWaterExnerEquations1D)
229232
ω = sqrt(2) * pi
230233

231-
h = 2.0 + cos* x[1]) * cos* t)
232-
v = 0.5
233-
h_b = 2.0 + sin* x[1]) * cos* t)
234+
h = 2 + cos* x[1]) * cos* t)
235+
v = 0.5f0
236+
h_b = 2 + sin* x[1]) * cos* t)
234237

235238
return SVector(h, h * v, h_b)
236239
end
@@ -256,20 +259,21 @@ equations = ShallowWaterExnerEquations1D(gravity = 10.0, rho_f = 0.5,
256259
T,
257260
S
258261
}
259-
ω = sqrt(2.0) * pi
262+
ω = sqrt(2) * pi
260263
A_g = equations.sediment_model.A_g
261264

262-
h = -cos(x[1] * ω) * sin(t * ω) * ω - 0.5 * sin(x[1] * ω) * cos(t * ω) * ω
263-
hv = -0.5 * cos(x[1] * ω) * sin(t * ω) * ω - 0.25 * sin(x[1] * ω) * cos(t * ω) * ω +
264-
10.0 * A_g *
265-
(cos(x[1] * ω) * cos(t * ω) * ω - 0.5 * sin(x[1] * ω) * cos(t * ω) * ω) +
266-
10.0 * (2.0 + cos(x[1] * ω) * cos(t * ω)) *
265+
h = -cos(x[1] * ω) * sin(t * ω) * ω - 0.5f0 * sin(x[1] * ω) * cos(t * ω) * ω
266+
hv = -0.5f0 * cos(x[1] * ω) * sin(t * ω) * ω -
267+
0.25f0 * sin(x[1] * ω) * cos(t * ω) * ω +
268+
10 * A_g *
269+
(cos(x[1] * ω) * cos(t * ω) * ω - 0.5f0 * sin(x[1] * ω) * cos(t * ω) * ω) +
270+
10 * (2 + cos(x[1] * ω) * cos(t * ω)) *
267271
(cos(x[1] * ω) * cos(t * ω) * ω - sin(x[1] * ω) * cos(t * ω) * ω)
268272
h_b = -sin(x[1] * ω) * sin(t * ω) * ω
269273
return SVector(h, hv, h_b)
270274
end
271275

272-
"""
276+
"""
273277
source_terms_convergence_test(u, x, t, equations::ShallowWaterExnerEquations1D{T, S, ShieldsStressModel{T}}) where {T, S}
274278
275279
Source terms used for convergence tests in combination with [`Trixi.initial_condition_convergence_test`](@ref)
@@ -291,27 +295,27 @@ equations = ShallowWaterExnerEquations1D(gravity = 10.0, rho_f = 0.5,
291295
T,
292296
S
293297
}
294-
ω = sqrt(2.0) * pi
298+
ω = sqrt(2) * pi
295299
(; gravity, porosity_inv, rho_f, rho_s, r) = equations
296300

297301
n = equations.friction.n
298302

299303
# Constant expression from the MPM model
300-
c = sqrt(gravity * (1 / r - 1)) * 8.0 * porosity_inv *
304+
c = sqrt(gravity * (1 / r - 1)) * 8 * porosity_inv *
301305
(rho_f / (rho_s - rho_f))^(3 / 2) * n^3
302306

303-
h = -cos(x[1] * ω) * sin(t * ω) * ω - 0.5 * sin(x[1] * ω) * cos(t * ω) * ω
307+
h = -cos(x[1] * ω) * sin(t * ω) * ω - 0.5f0 * sin(x[1] * ω) * cos(t * ω) * ω
304308

305-
hv = ((5.0 * c *
306-
(cos(x[1] * ω) * cos(t * ω) * ω - 0.5 * sin(x[1] * ω) * cos(t * ω) * ω)) /
307-
((2.0 + cos(x[1] * ω) * cos(t * ω))^0.5) -
308-
0.5 * cos(x[1] * ω) * sin(t * ω) * ω -
309-
0.25 * sin(x[1] * ω) * cos(t * ω) * ω +
310-
10.0 * (2.0 + cos(x[1] * ω) * cos(t * ω)) *
309+
hv = ((5 * c *
310+
(cos(x[1] * ω) * cos(t * ω) * ω - 0.5f0 * sin(x[1] * ω) * cos(t * ω) * ω)) /
311+
((2 + cos(x[1] * ω) * cos(t * ω))^0.5) -
312+
0.5f0 * cos(x[1] * ω) * sin(t * ω) * ω -
313+
0.25f0 * sin(x[1] * ω) * cos(t * ω) * ω +
314+
10 * (2 + cos(x[1] * ω) * cos(t * ω)) *
311315
(cos(x[1] * ω) * cos(t * ω) * ω - sin(x[1] * ω) * cos(t * ω) * ω))
312316

313-
h_b = ((0.5 * ((0.125 * c) / (2.0 + cos(x[1] * ω) * cos(t * ω))) * sin(x[1] * ω) *
314-
cos(t * ω) * ω) / ((2.0 + cos(x[1] * ω) * cos(t * ω))^0.5) -
317+
h_b = ((0.5f0 * ((0.125f0 * c) / (2 + cos(x[1] * ω) * cos(t * ω))) * sin(x[1] * ω) *
318+
cos(t * ω) * ω) / ((2 + cos(x[1] * ω) * cos(t * ω))^0.5) -
315319
sin(x[1] * ω) * sin(t * ω) * ω)
316320

317321
return SVector(h, hv, h_b)
@@ -367,8 +371,8 @@ scheme that is entropy conservative and well-balanced.
367371
h_b_jump = h_b_rr - h_b_ll
368372

369373
# Workaround to avoid division by zero, when computing the effective sediment height
370-
if velocity(u_ll, equations) < eps()
371-
h_s_ll = 0.0
374+
if abs(velocity(u_ll, equations)) < eps(typeof(h_ll))
375+
h_s_ll = 0
372376
else
373377
h_s_ll = q_s(u_ll, equations) / velocity(u_ll, equations)
374378
end
@@ -405,12 +409,12 @@ To obtain an entropy stable formulation the `surface_flux` can be set as
405409
v_rr = velocity(u_rr, equations)
406410

407411
# Average each factor of products in flux
408-
v_avg = 0.5 * (v_ll + v_rr)
412+
v_avg = 0.5f0 * (v_ll + v_rr)
409413

410414
# Calculate fluxes depending on orientation
411-
f1 = 0.5 * (h_v_ll + h_v_rr)
415+
f1 = 0.5f0 * (h_v_ll + h_v_rr)
412416
f2 = f1 * v_avg
413-
f3 = 0.5 * (q_s(u_ll, equations) + q_s(u_rr, equations))
417+
f3 = 0.5f0 * (q_s(u_ll, equations) + q_s(u_rr, equations))
414418

415419
return SVector(f1, f2, f3)
416420
end
@@ -432,21 +436,24 @@ for the sediment discharge `q_s`.
432436
v_rr = velocity(u_rr, equations)
433437

434438
# Compute approximate Roe averages.
435-
# The actual Roe average for the sediment discharge `q_s` would depend on the sediment and
436-
# friction model and can be difficult to compute analytically.
439+
# The actual Roe average for the sediment height `h_b` depends on the sediment and
440+
# friction model and an explicit formula is not always available.
437441
# Therefore we only use an approximation here.
438-
h_avg = 0.5 * (u_ll[1] + u_rr[1])
442+
h_avg = 0.5f0 * (u_ll[1] + u_rr[1])
439443
v_avg = (sqrt(u_ll[1]) * v_ll + sqrt(u_rr[1]) * v_rr) /
440444
(sqrt(u_ll[1]) + sqrt(u_rr[1]))
445+
h_b_avg = 0.5f0 * (u_ll[3] + u_rr[3])
446+
441447
# Workaround to avoid division by zero, when computing the effective sediment height
442-
if v_avg < eps()
443-
h_s_avg = 0.0
448+
if abs(v_avg) < eps(typeof(h_avg))
449+
h_s_avg = 0
444450
else
445-
h_s_avg = 0.5 * (q_s(u_ll, equations) / v_ll + q_s(u_rr, equations) / v_rr)
451+
h_s_avg = (q_s(SVector(h_avg, h_avg * v_avg, h_b_avg), equations) /
452+
v_avg)
446453
end
447454

448455
# Compute the eigenvalues using Cardano's formula
449-
λ1, λ2, λ3 = eigvals_cardano(SVector(h_avg, h_avg * v_avg, h_s_avg), equations)
456+
λ1, λ2, λ3 = eigvals_cardano(SVector(h_avg, h_avg * v_avg, h_b_avg), equations)
450457

451458
# Precompute some common expressions
452459
c1 = g * (h_avg + h_s_avg)
@@ -470,7 +477,7 @@ for the sediment discharge `q_s`.
470477
Λ_abs = @SMatrix [abs(λ1) z z; z abs(λ2) z; z z abs(λ3)]
471478

472479
# Compute dissipation
473-
diss = SVector(-0.5 * R * Λ_abs * R_inv * (u_rr - u_ll))
480+
diss = SVector(-0.5f0 * R * Λ_abs * R_inv * (u_rr - u_ll))
474481

475482
return SVector(diss[1], diss[2], diss[3])
476483
end
@@ -506,11 +513,11 @@ end
506513

507514
theta = rho_f * abs(shear_stress(u, equations)) / (gravity * (rho_s - rho_f) * d_s) # Shields stress
508515

509-
Q = d_s * sqrt(gravity * (rho_s / rho_f - 1.0) * d_s) # Characteristic discharge
516+
Q = d_s * sqrt(gravity * (rho_s / rho_f - 1) * d_s) # Characteristic discharge
510517

511518
return (porosity_inv * Q * sign(theta) * k_1 * theta^m_1 *
512-
(max(theta - k_2 * theta_c, 0.0))^m_2 *
513-
(max(sqrt(theta) - k_3 * sqrt(theta_c), 0.0))^m_3)
519+
(max(theta - k_2 * theta_c, 0))^m_2 *
520+
(max(sqrt(theta) - k_3 * sqrt(theta_c), 0))^m_3)
514521
end
515522

516523
# Compute the sediment discharge for the Grass model
@@ -553,7 +560,7 @@ end
553560

554561
v = velocity(u, equations)
555562

556-
w1 = r * (gravity * (h + h_b) - 0.5 * v^2)
563+
w1 = r * (gravity * (h + h_b) - 0.5f0 * v^2)
557564
w2 = r * v
558565
w3 = gravity * (r * h + h_b)
559566

@@ -585,7 +592,8 @@ end
585592
v = velocity(u, equations)
586593
(; gravity, r) = equations
587594

588-
return 0.5 * r * h * v^2 + 0.5 * gravity * (r * h^2 + h_b^2) + r * gravity * h * h_b
595+
return 0.5f0 * r * h * v^2 + 0.5f0 * gravity * (r * h^2 + h_b^2) +
596+
r * gravity * h * h_b
589597
end
590598

591599
# Calculate the error for the "lake-at-rest" test case where H = h + h_b should
@@ -605,16 +613,16 @@ end
605613
r = equations.r
606614

607615
# Workaround to avoid division by zero, when computing the effective sediment height
608-
if v > eps()
616+
if abs(v) > eps(typeof(h))
609617
h_s = q_s(u, equations) / v
610618
# Compute gradients of q_s using automatic differentiation.
611619
# Introduces a closure to make q_s a function of u only. This is necessary since the
612620
# gradient function only accepts functions of one variable.
613621
dq_s_dh, dq_s_dhv, _ = Trixi.ForwardDiff.gradient(u -> q_s(u, equations), u)
614622
else
615-
h_s = 0.0
616-
dq_s_dh = 0.0
617-
dq_s_dhv = 0.0
623+
h_s = 0
624+
dq_s_dh = 0
625+
dq_s_dhv = 0
618626
end
619627

620628
# Coefficient for the original cubic equation ax^3 + bx^2 + cx + d

test/test_tree_1d.jl

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,14 +1074,14 @@ end # MLSWE
10741074
@test_trixi_include(joinpath(EXAMPLES_DIR,
10751075
"elixir_shallowwater_exner_channel.jl"),
10761076
l2=[
1077-
0.061254947613784645,
1078-
0.0042920880585939165,
1079-
0.06170746499938789
1077+
0.0612549484022613,
1078+
0.004292092111911898,
1079+
0.06170746566027052
10801080
],
10811081
linf=[
1082-
0.5552555774807875,
1083-
0.009352028888004682,
1084-
0.549962205546136
1082+
0.555255659544283,
1083+
0.009352017074210295,
1084+
0.5499622869285822
10851085
])
10861086
# Ensure that we do not have excessive memory allocations
10871087
# (e.g., from type instabilities)
@@ -1117,6 +1117,29 @@ end # MLSWE
11171117
@test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000
11181118
end
11191119
end
1120+
1121+
@trixi_testset "elixir_shallowwater_exner_dam_break_symmetric.jl" begin
1122+
@test_trixi_include(joinpath(EXAMPLES_DIR,
1123+
"elixir_shallowwater_exner_dam_break_symmetric.jl"),
1124+
l2=[
1125+
0.2965046413567244,
1126+
0.41070730713108367,
1127+
0.025179300052016823
1128+
],
1129+
linf=[
1130+
0.7785005894122902,
1131+
0.9193314051729015,
1132+
0.06639643395379602
1133+
])
1134+
# Ensure that we do not have excessive memory allocations
1135+
# (e.g., from type instabilities)
1136+
let
1137+
t = sol.t[end]
1138+
u_ode = sol.u[end]
1139+
du_ode = similar(u_ode)
1140+
@test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000
1141+
end
1142+
end
11201143
end # SWE-Exner
11211144
end # TreeMesh1D
11221145

0 commit comments

Comments
 (0)