Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
61ac83a
Initial commit for 3D subcell limiting support
bennibolm Sep 24, 2025
198ffe6
Adapt maximum allowed allocations
bennibolm Sep 24, 2025
1a1c500
Implement suggestions
bennibolm Sep 24, 2025
31eb511
Merge branch 'main' into 3d-subcell-limiting-first
bennibolm Oct 1, 2025
0d127a4
Apply changes to 3d code (remove `alpha1/2/3`)
bennibolm Oct 1, 2025
ddcc5e4
Merge branch 'main' into 3d-subcell-limiting-first
bennibolm Oct 1, 2025
ead0595
Reset antidiffusive fluxes after resizing in 3d
bennibolm Oct 1, 2025
f74d77b
Add empty line
bennibolm Oct 1, 2025
36ed9b8
Fix bug
bennibolm Oct 1, 2025
53a7ff6
Nicer line breaks
bennibolm Oct 1, 2025
b1873e1
Move funcs to p4est files
bennibolm Oct 6, 2025
80837a0
Move even more
bennibolm Oct 6, 2025
f40719d
Reduce allowed allocs in test
bennibolm Oct 6, 2025
d3657c9
Increase allocs number again
bennibolm Oct 6, 2025
ed5fe33
Merge branch 'main' into 3d-subcell-limiting-first
bennibolm Oct 6, 2025
19a9efb
Clean up elixir
bennibolm Oct 6, 2025
4fe6c1f
Add note to news.md
bennibolm Oct 8, 2025
4d9cdad
Merge branch 'main' into 3d-subcell-limiting-first
bennibolm Oct 15, 2025
af89370
Update news
bennibolm Oct 15, 2025
620d3a9
Implement suggestions
bennibolm Oct 15, 2025
cab6331
Merge branch 'main' into 3d-subcell-limiting-first
DanielDoehring Oct 15, 2025
75d682e
Update src/callbacks_stage/subcell_limiter_idp_correction_3d.jl
DanielDoehring Oct 15, 2025
e66bf54
Merge branch 'main' into 3d-subcell-limiting-first
DanielDoehring Oct 24, 2025
090ffc2
Merge branch 'main' into 3d-subcell-limiting-first
ranocha Oct 24, 2025
8ffef8f
Remove `A4dp1_x/y/z`
bennibolm Oct 24, 2025
84c025c
Use `maxthreadid()`
bennibolm Oct 24, 2025
5f271e7
Merge branch 'main' into 3d-subcell-limiting-first
bennibolm Oct 27, 2025
691c88f
Add full 3d implementation on P4estMesh
bennibolm Oct 23, 2025
50a361f
Add `A5d`
bennibolm Oct 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ used in the Julia ecosystem. Notable changes will be documented in this file
for human readability.


## Changes in the v0.13 lifecycle

#### Added
- Initial 3D support for subcell limiting with `P4estMesh` was added ([#2582]).
In the new version, positivity limiting for conservative variables (using `positivity_variables_cons`)
is supported. `BoundsCheckCallback` is not supported in 3D yet.


## Changes when updating to v0.13 from v0.12.x

#### Changed
Expand Down
103 changes: 103 additions & 0 deletions examples/p4est_3d_dgsem/elixir_euler_sedov_sc_subcell.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using Trixi

###############################################################################
# semidiscretization of the compressible Euler equations

equations = CompressibleEulerEquations3D(1.4)

"""
initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations3D)

The Sedov blast wave setup based on Flash
- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
with smaller strength of the initial discontinuity.
"""
function initial_condition_sedov_blast_wave(x, t,
equations::CompressibleEulerEquations3D)
# Set up polar coordinates
inicenter = SVector(0.0, 0.0, 0.0)
x_norm = x[1] - inicenter[1]
y_norm = x[2] - inicenter[2]
z_norm = x[3] - inicenter[3]
r = sqrt(x_norm^2 + y_norm^2 + z_norm^2)

# Setup based on https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
r0 = 0.21875 # = 3.5 * smallest dx (for domain length=4 and max-ref=6)
E = 1.0
p0_inner = 3 * (equations.gamma - 1) * E / (4 * pi * r0^2)
p0_outer = 1.0e-5

# Calculate primitive variables
rho = 1.0
v1 = 0.0
v2 = 0.0
v3 = 0.0
p = r > r0 ? p0_outer : p0_inner

return prim2cons(SVector(rho, v1, v2, v3, p), equations)
end

initial_condition = initial_condition_sedov_blast_wave

surface_flux = flux_lax_friedrichs
volume_flux = flux_ranocha
polydeg = 3
basis = LobattoLegendreBasis(polydeg)
limiter_idp = SubcellLimiterIDP(equations, basis;
positivity_variables_cons = ["rho"],
positivity_variables_nonlinear = [pressure],
local_twosided_variables_cons = [],
local_onesided_variables_nonlinear = [],
max_iterations_newton = 40, # Default parameters are not sufficient to fulfill bounds properly.
newton_tolerances = (1.0e-14, 1.0e-15))
volume_integral = VolumeIntegralSubcellLimiting(limiter_idp;
volume_flux_dg = volume_flux,
volume_flux_fv = surface_flux)
solver = DGSEM(basis, surface_flux, volume_integral)

coordinates_min = (-1.0, -1.0, -1.0)
coordinates_max = (1.0, 1.0, 1.0)

trees_per_dimension = (8, 8, 8)
mesh = P4estMesh(trees_per_dimension,
polydeg = 1, initial_refinement_level = 0,
coordinates_min = coordinates_min, coordinates_max = coordinates_max,
periodicity = true)

# create the semi discretization object
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)

###############################################################################
# ODE solvers, callbacks etc.

tspan = (0.0, 3.0)
ode = semidiscretize(semi, tspan)

summary_callback = SummaryCallback()

analysis_interval = 100
analysis_callback = AnalysisCallback(semi, interval = analysis_interval)

alive_callback = AliveCallback(analysis_interval = analysis_interval)

save_solution = SaveSolutionCallback(interval = 10,
save_initial_solution = true,
save_final_solution = true,
extra_node_variables = (:limiting_coefficient,))

stepsize_callback = StepsizeCallback(cfl = 0.5)

callbacks = CallbackSet(summary_callback,
analysis_callback,
alive_callback,
save_solution,
stepsize_callback)

###############################################################################
# run the simulation

stage_callbacks = (SubcellLimiterIDPCorrection(), BoundsCheckCallback())

sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks);
dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
callback = callbacks);
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using OrdinaryDiffEqLowStorageRK
using Trixi

###############################################################################
# semidiscretization of the compressible Euler equations

equations = CompressibleEulerEquations3D(1.4)

initial_condition = initial_condition_convergence_test

boundary_condition = BoundaryConditionDirichlet(initial_condition)
boundary_conditions = Dict(:Bottom => boundary_condition,
:Top => boundary_condition,
:Circle => boundary_condition,
:Cut => boundary_condition)

surface_flux = flux_lax_friedrichs
volume_flux = flux_ranocha
polydeg = 4
basis = LobattoLegendreBasis(polydeg)
limiter_idp = SubcellLimiterIDP(equations, basis;
positivity_variables_cons = ["rho"],
positivity_variables_nonlinear = [pressure],
local_twosided_variables_cons = [],
local_onesided_variables_nonlinear = [])
volume_integral = VolumeIntegralSubcellLimiting(limiter_idp;
volume_flux_dg = volume_flux,
volume_flux_fv = surface_flux)
solver = DGSEM(basis, surface_flux, volume_integral)

# Unstructured 3D half circle mesh from HOHQMesh
mesh_file = Trixi.download("https://gist.githubusercontent.com/andrewwinters5000/11461efbfb02c42e06aca338b3d0b645/raw/81deeb1ebc4945952c30af5bb75fe222a18d975c/abaqus_half_circle_3d.inp",
joinpath(@__DIR__, "abaqus_half_circle_3d.inp"))

mesh = P4estMesh{3}(mesh_file, initial_refinement_level = 0)

semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
source_terms = source_terms_convergence_test,
boundary_conditions = boundary_conditions)

###############################################################################
# ODE solvers, callbacks etc.

tspan = (0.0, 1.0)
ode = semidiscretize(semi, tspan)

summary_callback = SummaryCallback()

analysis_interval = 100
analysis_callback = AnalysisCallback(semi, interval = analysis_interval,
extra_analysis_integrals = (entropy,))

alive_callback = AliveCallback(analysis_interval = analysis_interval)

save_solution = SaveSolutionCallback(interval = 1,
save_initial_solution = true,
save_final_solution = true,
solution_variables = cons2prim,
extra_node_variables = (:limiting_coefficient,))

stepsize_callback = StepsizeCallback(cfl = 0.5)

callbacks = CallbackSet(summary_callback,
analysis_callback, alive_callback,
save_solution,
stepsize_callback)

###############################################################################
# run the simulation

stage_callbacks = (SubcellLimiterIDPCorrection(), BoundsCheckCallback())

sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks);
dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
ode_default_options()..., callback = callbacks);
124 changes: 124 additions & 0 deletions examples/p4est_3d_dgsem/elixir_mhd_shockcapturing_subcell.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
using Trixi

###############################################################################
# semidiscretization of the compressible ideal GLM-MHD equations

equations = IdealGlmMhdEquations3D(1.4)

"""
initial_condition_blast_wave(x, t, equations::IdealGlmMhdEquations3D)

Weak magnetic blast wave setup taken from Section 6.1 of the paper:
- A. M. Rueda-Ramírez, S. Hennemann, F. J. Hindenlang, A. R. Winters, G. J. Gassner (2021)
An entropy stable nodal discontinuous Galerkin method for the resistive MHD
equations. Part II: Subcell finite volume shock capturing
[doi: 10.1016/j.jcp.2021.110580](https://doi.org/10.1016/j.jcp.2021.110580)
"""
function initial_condition_blast_wave(x, t, equations::IdealGlmMhdEquations3D)
# Center of the blast wave is selected for the domain [0, 3]^3
inicenter = (1.5, 1.5, 1.5)
x_norm = x[1] - inicenter[1]
y_norm = x[2] - inicenter[2]
z_norm = x[3] - inicenter[3]
r = sqrt(x_norm^2 + y_norm^2 + z_norm^2)

delta_0 = 0.1
r_0 = 0.3
lambda = exp(5.0 / delta_0 * (r - r_0))

prim_inner = SVector(1.2, 0.1, 0.0, 0.1, 0.9, 1.0, 1.0, 1.0, 0.0)
prim_outer = SVector(1.2, 0.2, -0.4, 0.2, 0.3, 1.0, 1.0, 1.0, 0.0)
prim_vars = (prim_inner + lambda * prim_outer) / (1.0 + lambda)

return prim2cons(prim_vars, equations)
end
initial_condition = initial_condition_blast_wave

surface_flux = (flux_lax_friedrichs, flux_nonconservative_powell_local_symmetric)
volume_flux = (flux_hindenlang_gassner, flux_nonconservative_powell_local_symmetric)
polydeg = 3
basis = LobattoLegendreBasis(polydeg)
limiter_idp = SubcellLimiterIDP(equations, basis;
positivity_variables_cons = ["rho"],
positivity_variables_nonlinear = [pressure])
volume_integral = VolumeIntegralSubcellLimiting(limiter_idp;
volume_flux_dg = volume_flux,
volume_flux_fv = surface_flux)
solver = DGSEM(basis, surface_flux, volume_integral)

# Mapping as described in https://arxiv.org/abs/2012.12040 but with slightly less warping.
# The mapping will be interpolated at tree level, and then refined without changing
# the geometry interpolant.
function mapping(xi_, eta_, zeta_)
# Transform input variables between -1 and 1 onto [0,3]
xi = 1.5 * xi_ + 1.5
eta = 1.5 * eta_ + 1.5
zeta = 1.5 * zeta_ + 1.5

y = eta +
3 / 11 * (cos(1.5 * pi * (2 * xi - 3) / 3) *
cos(0.5 * pi * (2 * eta - 3) / 3) *
cos(0.5 * pi * (2 * zeta - 3) / 3))

x = xi +
3 / 11 * (cos(0.5 * pi * (2 * xi - 3) / 3) *
cos(2 * pi * (2 * y - 3) / 3) *
cos(0.5 * pi * (2 * zeta - 3) / 3))

z = zeta +
3 / 11 * (cos(0.5 * pi * (2 * x - 3) / 3) *
cos(pi * (2 * y - 3) / 3) *
cos(0.5 * pi * (2 * zeta - 3) / 3))

return SVector(x, y, z)
end

trees_per_dimension = (2, 2, 2)
mesh = P4estMesh(trees_per_dimension,
polydeg = 3,
mapping = mapping,
# coordinates_min = (0.0, 0.0, 0.0),
# coordinates_max = (3.0, 3.0, 3.0),
initial_refinement_level = 2,
periodicity = true)

semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)

###############################################################################
# ODE solvers, callbacks etc.

tspan = (0.0, 0.5)
ode = semidiscretize(semi, tspan)

summary_callback = SummaryCallback()

analysis_interval = 100
analysis_callback = AnalysisCallback(semi, interval = analysis_interval)

alive_callback = AliveCallback(analysis_interval = analysis_interval)

save_solution = SaveSolutionCallback(interval = 100,
save_initial_solution = true,
save_final_solution = true,
solution_variables = cons2prim,
extra_node_variables = (:limiting_coefficient,))

cfl = 0.9
stepsize_callback = StepsizeCallback(cfl = cfl)

glm_speed_callback = GlmSpeedCallback(glm_scale = 0.5, cfl = cfl)

callbacks = CallbackSet(summary_callback,
analysis_callback,
alive_callback,
save_solution,
stepsize_callback,
glm_speed_callback)

###############################################################################
# run the simulation
stage_callbacks = (SubcellLimiterIDPCorrection(), BoundsCheckCallback())

sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks);
dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
ode_default_options()..., callback = callbacks);
1 change: 1 addition & 0 deletions src/callbacks_stage/subcell_bounds_check.jl
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,5 @@ end
end

include("subcell_bounds_check_2d.jl")
include("subcell_bounds_check_3d.jl")
end # @muladd
2 changes: 1 addition & 1 deletion src/callbacks_stage/subcell_bounds_check_2d.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
@muladd begin
#! format: noindent

@inline function check_bounds(u, equations::AbstractEquations{2}, # only works for 2D
@inline function check_bounds(u, equations::AbstractEquations{2},
solver, cache, limiter::SubcellLimiterIDP)
(; local_twosided, positivity, local_onesided) = solver.volume_integral.limiter
(; variable_bounds) = limiter.cache.subcell_limiter_coefficients
Expand Down
Loading
Loading