Skip to content

Commit c724d12

Browse files
DGMulti shock capturing part 2: SBP approximation types (#2942)
* changes related to cleanup of existing shock capturing remove extra end remove unnecessary comment * add 3d shock capturing elixir add 3d test * formatting * Adding shock capturing routines for SBP on Line/Quad elements * add test for non-Gauss SBP shock capturing * fix naming convention * updating tests Removing curved shock capturing test since we haven't implemented low order free-stream metrics * update CI values with x86 run values * update initial condition to avoid x86/aarch64 differences with CI vals * Use weak C0 blast IC for DGMulti Euler shock capturing Replace the polar-coordinate blast setup with a Cartesian weak C0 initial condition so we avoid floating-point trouble from polar evaluation and from sampling exactly on the discontinuity. Update the GaussSBP and SBP regression L2/L∞ norms in test_dgmulti_2d.jl for elixir_euler_shockcapturing.jl to match the new IC. Made-with: Cursor * Apply suggestions from code review Co-authored-by: Daniel Doehring <doehringd2@gmail.com> * comments on 2 * f_ij and sparse operator column entries rows[j] * fix formatting * Update src/solvers/dgmulti/shock_capturing.jl * Update examples/dgmulti_2d/elixir_euler_shockcapturing.jl * Update src/solvers/dgmulti/shock_capturing.jl * Update src/solvers/dgmulti/shock_capturing.jl --------- Co-authored-by: Daniel Doehring <doehringd2@gmail.com> Co-authored-by: Daniel Doehring <daniel.doehring@rwth-aachen.de>
1 parent 237cca1 commit c724d12

4 files changed

Lines changed: 157 additions & 19 deletions

File tree

examples/dgmulti_2d/elixir_euler_shockcapturing.jl

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,48 @@ using Trixi
66

77
equations = CompressibleEulerEquations2D(1.4)
88

9-
initial_condition = initial_condition_weak_blast_wave
10-
11-
# Up to version 0.13.0, `max_abs_speed_naive` was used as the default wave speed estimate of
12-
# `const flux_lax_friedrichs = FluxLaxFriedrichs(), i.e., `FluxLaxFriedrichs(max_abs_speed = max_abs_speed_naive)`.
13-
# In the `StepsizeCallback`, though, the less diffusive `max_abs_speeds` is employed which is consistent with `max_abs_speed`.
14-
# Thus, we exchanged in PR#2458 the default wave speed used in the LLF flux to `max_abs_speed`.
15-
# To ensure that every example still runs we specify explicitly `FluxLaxFriedrichs(max_abs_speed_naive)`.
16-
# We remark, however, that the now default `max_abs_speed` is in general recommended due to compliance with the
17-
# `StepsizeCallback` (CFL-Condition) and less diffusion.
18-
surface_flux = FluxLaxFriedrichs(max_abs_speed_naive)
9+
# A continuous version of the blast wave initial condition to avoid floating point
10+
# issues when evaluating polar coordinates or evaluating at the discontinuity
11+
function initial_condition_weak_C0_blast_wave(x, t,
12+
equations::CompressibleEulerEquations2D)
13+
RealT = eltype(x)
14+
inicenter = SVector(0, 0)
15+
x_norm = x[1] - inicenter[1]
16+
y_norm = x[2] - inicenter[2]
17+
r = sqrt(x_norm^2 + y_norm^2)
18+
19+
rho_outer = one(RealT)
20+
v1_outer = zero(RealT)
21+
v2_outer = zero(RealT)
22+
p_outer = one(RealT)
23+
rho_inner = 1.1691
24+
v1_inner = 0.1882
25+
v2_inner = 0.1882
26+
p_inner = 1.245
27+
28+
# Calculate primitive variables
29+
if r > 0.5f0
30+
rho = rho_outer
31+
v1 = v1_outer
32+
v2 = v2_outer
33+
p = p_outer
34+
elseif isapprox(r, 0.5f0)
35+
rho = 0.5f0 * (rho_outer + rho_inner)
36+
v1 = 0.5f0 * (v1_outer + v1_inner)
37+
v2 = 0.5f0 * (v2_outer + v2_inner)
38+
p = 0.5f0 * (p_outer + p_inner)
39+
else
40+
rho = rho_inner
41+
v1 = v1_inner
42+
v2 = v2_inner
43+
p = p_inner
44+
end
45+
46+
return prim2cons(SVector(rho, v1, v2, p), equations)
47+
end
48+
initial_condition = initial_condition_weak_C0_blast_wave
49+
50+
surface_flux = FluxLaxFriedrichs()
1951
volume_flux = flux_ranocha
2052

2153
polydeg = 3

src/solvers/dgmulti/flux_differencing.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,14 @@ function create_cache(mesh::DGMultiMesh, equations, dg::DGMultiFluxDiffSBP,
129129
solution_container = initialize_dgmulti_solution_container(mesh, equations, dg,
130130
uEltype)
131131

132+
# this calls the `create_cache` for the shock capturing volume integral
133+
volume_integral_cache = create_cache(mesh, equations, dg.volume_integral,
134+
dg, RealT, uEltype)
135+
132136
return (; md, Qrst_skew, dxidxhatj = md.rstxyzJ,
133137
invJ = inv.(md.J), lift_scalings, inv_wq = inv.(rd.wq),
134-
solution_container, du_local_threaded)
138+
solution_container, du_local_threaded,
139+
volume_integral_cache...)
135140
end
136141

137142
# most general create_cache: works for `DGMultiFluxDiff{<:Polynomial}`

src/solvers/dgmulti/shock_capturing.jl

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,24 @@ function create_cache(mesh::DGMultiMesh{NDIMS}, equations,
3030
element_to_element_connectivity)
3131
end
3232

33+
function create_cache(mesh::DGMultiMesh{NDIMS}, equations,
34+
volume_integral::Union{VolumeIntegralShockCapturingHGType,
35+
VolumeIntegralPureLGLFiniteVolume},
36+
dg::DGMultiFluxDiffSBP, RealT, uEltype) where {NDIMS}
37+
element_to_element_connectivity = build_element_to_element_connectivity(mesh, dg)
38+
39+
# create skew-symmetric parts of sparse hybridized operators for low order scheme.
40+
sparse_SBP_operators, _ = StartUpDG.sparse_low_order_SBP_operators(dg.basis)
41+
sparse_SBP_operators = map(A -> 0.5f0 * (A - A'), sparse_SBP_operators)
42+
43+
# Find the joint sparsity pattern of the entire matrix. We store the sparsity pattern as
44+
# an adjoint for faster iteration through the rows.
45+
sparsity_pattern = sum(map(A -> abs.(A)', sparse_SBP_operators)) .> 100 * eps()
46+
47+
return (; sparse_SBP_operators, sparsity_pattern,
48+
element_to_element_connectivity)
49+
end
50+
3351
# this method is used when the indicator is constructed as for shock-capturing volume integrals
3452
function create_cache(::Type{IndicatorHennemannGassner}, equations::AbstractEquations,
3553
basis::DGMultiBasis{NDIMS}) where {NDIMS}
@@ -305,6 +323,11 @@ function volume_integral_kernel!(du, u, element, mesh::DGMultiMesh,
305323
u_i = u_local[i]
306324
du_i = zero(u_i)
307325
for id in nzrange(A_base, i)
326+
# nonzero column indices for row i of the sparse operator.
327+
# note that because Julia uses SparseMatrixCSC, rows[id]
328+
# are efficient to access. We assume here that `sparsity_pattern`
329+
# is symmetric (which is true since A_base is skew-symmetric),
330+
# so nonzero row indices are the same as nonzero column indices.
308331
j = rows[id]
309332
u_j = u_local[j]
310333

@@ -317,6 +340,13 @@ function volume_integral_kernel!(du, u, element, mesh::DGMultiMesh,
317340
# note that we do not need to normalize `normal_direction_ij` since
318341
# it is typically normalized within the flux computation.
319342
f_ij = volume_flux_fv(u_i, u_j, normal_direction_ij, equations)
343+
344+
# the factor of 2 is for consistency; for example, if f_ij is the central
345+
# flux, flux differencing with a differentiation matrix should recover the
346+
# flux derivative via
347+
# \sum_j 2 * D_ij * f_ij = \sum_j 2 * D_ij * 0.5 * (f(u_i) + f(u_j))
348+
# = f(u_i) \sum_j D_ij + \sum_j D_ij f(u_j)
349+
# = 0 (since \sum_j D_ij = 0) + (D * f(u))_i
320350
du_i = du_i + 2 * f_ij
321351
end
322352
rhs_local[i] = du_i
@@ -325,4 +355,54 @@ function volume_integral_kernel!(du, u, element, mesh::DGMultiMesh,
325355
# TODO: factor this out to avoid calling it twice during calc_volume_integral!
326356
return project_rhs_to_gauss_nodes!(du, rhs_local, element, mesh, dg, cache, alpha)
327357
end
358+
359+
# Calculates the volume integral corresponding to an algebraic low order method for
360+
# DGMultiFluxDiffSBP (traditional SBP operators with LGL-type nodes).
361+
# Unlike GaussSBP, the solution lives at nodes that include the face nodes (at positions
362+
# `rd.Fmask`), so no entropy projection is needed. We build the extended [interior; face]
363+
# vector in-kernel and project back by scattering face contributions to Fmask positions.
364+
function volume_integral_kernel!(du, u, element, mesh::DGMultiMesh,
365+
have_nonconservative_terms::False, equations,
366+
volume_integral::VolumeIntegralPureLGLFiniteVolume,
367+
dg::DGMultiFluxDiffSBP, cache, alpha = true)
368+
(; volume_flux_fv) = volume_integral
369+
370+
(; inv_wq, sparsity_pattern) = cache
371+
A_base, row_ids, rows, _ = sparse_operator_data(sparsity_pattern)
372+
for i in row_ids
373+
u_i = u[i, element]
374+
du_i = zero(u_i)
375+
for id in nzrange(A_base, i)
376+
# nonzero column indices for row i of the sparse operator.
377+
# note that because Julia uses SparseMatrixCSC, rows[id]
378+
# are efficient to access. We assume here that `sparsity_pattern`
379+
# is symmetric (which is true since A_base is skew-symmetric),
380+
# so nonzero row indices are the same as nonzero column indices.
381+
j = rows[id]
382+
u_j = u[j, element]
383+
384+
# compute (Q_1[i,j], Q_2[i,j], ...) where Q_i = ∑_j dxidxhatj * Q̂_j
385+
geometric_matrix = get_low_order_geometric_matrix(i, j, element, mesh,
386+
cache)
387+
reference_operator_entries = get_sparse_operator_entries(i, j, mesh,
388+
cache)
389+
normal_direction_ij = geometric_matrix * reference_operator_entries
390+
391+
# note that we do not need to normalize `normal_direction_ij` since
392+
# it is typically normalized within the flux computation.
393+
f_ij = volume_flux_fv(u_i, u_j, normal_direction_ij, equations)
394+
395+
# the factor of 2 is for consistency; for example, if f_ij is the central
396+
# flux, flux differencing with a differentiation matrix should recover the
397+
# flux derivative via
398+
# \sum_j 2 * D_ij * f_ij = \sum_j 2 * D_ij * 0.5 * (f(u_i) + f(u_j))
399+
# = f(u_i) \sum_j D_ij + \sum_j D_ij f(u_j)
400+
# = 0 (since \sum_j D_ij = 0) + (D * f(u))_i
401+
du_i = du_i + 2 * f_ij
402+
end
403+
du[i, element] = du[i, element] + alpha * du_i * inv_wq[i]
404+
end
405+
406+
return nothing
407+
end
328408
end # @muladd

test/test_dgmulti_2d.jl

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -421,16 +421,37 @@ end
421421
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_shockcapturing.jl"),
422422
cells_per_dimension=4, tspan=(0.0, 0.1),
423423
l2=[
424-
0.05685180852320552,
425-
0.04308097439005265,
426-
0.04308097439005263,
427-
0.21098250258804
424+
0.04459267863712344,
425+
0.04466070180923881,
426+
0.044660701809239055,
427+
0.16853101962882136
428428
],
429429
linf=[
430-
0.2360805191601203,
431-
0.16684117462697776,
432-
0.16684117462697767,
433-
0.8573034682049414
430+
0.18547384414190626,
431+
0.20141544103810083,
432+
0.20141544103810224,
433+
0.6954971316946836
434+
])
435+
# Ensure that we do not have excessive memory allocations
436+
# (e.g., from type instabilities)
437+
@test_allocations(Trixi.rhs!, semi, sol, 1000)
438+
end
439+
440+
@trixi_testset "elixir_euler_shockcapturing.jl (SBP)" begin
441+
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_shockcapturing.jl"),
442+
cells_per_dimension=4, tspan=(0.0, 0.1),
443+
basis=DGMultiBasis(Quad(), 3, approximation_type = SBP()),
444+
l2=[
445+
0.03901629442791245,
446+
0.03830525262399631,
447+
0.03830525262399637,
448+
0.14746814850975215
449+
],
450+
linf=[
451+
0.16680108480009226,
452+
0.21077037377694813,
453+
0.2107703737769464,
454+
0.6400748796169138
434455
])
435456
# Ensure that we do not have excessive memory allocations
436457
# (e.g., from type instabilities)

0 commit comments

Comments
 (0)