Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
1638e9c
Add local limiting for conservative variables (3d)
bennibolm Jan 27, 2026
fb6c67f
Add PR number
bennibolm Jan 27, 2026
af49ff5
Add comment
bennibolm Feb 4, 2026
8ed1bf6
Merge branch 'main' into 3d-subcell-locallimiting
JoshuaLampert Feb 4, 2026
3f07708
Merge branch 'main' into 3d-subcell-locallimiting
bennibolm Feb 9, 2026
1174dca
Adapt news.md
bennibolm Feb 9, 2026
6d36f7e
Merge branch 'main' into 3d-subcell-locallimiting
bennibolm Feb 13, 2026
a18113c
Merge branch 'main' into 3d-subcell-locallimiting
bennibolm Feb 13, 2026
100485c
Merge branch 'main' into 3d-subcell-locallimiting
bennibolm Feb 24, 2026
c1218e0
Initial commit for 3D subcell limiting support
bennibolm Sep 24, 2025
9704584
Implement suggestions
bennibolm Sep 24, 2025
29a255b
Apply changes to 3d code (remove `alpha1/2/3`)
bennibolm Oct 1, 2025
3e82932
Reset antidiffusive fluxes after resizing in 3d
bennibolm Oct 1, 2025
df429c8
Nicer line breaks
bennibolm Oct 1, 2025
f1e4bec
Move funcs to p4est files
bennibolm Oct 6, 2025
8d1b742
Move even more
bennibolm Oct 6, 2025
50c681f
Reduce allowed allocs in test
bennibolm Oct 6, 2025
7dbc8cd
Increase allocs number again
bennibolm Oct 6, 2025
e8ab82a
Add full 3d implementation on P4estMesh
bennibolm Oct 23, 2025
fb1d93d
Fix tests after fixing bug in last commit
bennibolm Nov 20, 2025
fdfee60
Add reset of bounds
bennibolm Jan 9, 2026
6a146cf
Remove reset again
bennibolm Jan 13, 2026
081a2b7
Remove `@threaded`
bennibolm Jan 13, 2026
eff3a34
Add reset and `@threaded`
bennibolm Jan 14, 2026
fdef177
Stabilize simulation
bennibolm Jan 14, 2026
128ce52
Reduce run time in test
bennibolm Jan 14, 2026
1b11be6
cl
bennibolm Jan 21, 2026
9a150d2
Fix boundary conditions in elixir
bennibolm Feb 26, 2026
70046b3
Merge branch 'main' into 3d-subcell-limiting
bennibolm Feb 26, 2026
2ab9792
Merge branch 'main' into 3d-subcell-limiting
bennibolm Mar 3, 2026
d2c4ccf
Merge branch 'main' into 3d-subcell-limiting
bennibolm Mar 5, 2026
25773d9
Merge branch 'main' into 3d-subcell-limiting
bennibolm Mar 6, 2026
8c67fbd
Merge branch 'main' into 3d-subcell-limiting
bennibolm Mar 12, 2026
a491cd0
Add 3d TreeMesh implementation (#143)
bennibolm Mar 12, 2026
9347c80
Merge branch 'main' into 3d-subcell-limiting
bennibolm Mar 13, 2026
186cc43
Merge branch 'main' into 3d-subcell-limiting
bennibolm Mar 23, 2026
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
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 = (; 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 = [],
max_iterations_newton = 10)
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
callback = callbacks);
70 changes: 70 additions & 0 deletions examples/tree_3d_dgsem/elixir_euler_convergence_sc_subcell.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using OrdinaryDiffEqLowStorageRK
using Trixi

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

equations = CompressibleEulerEquations3D(1.4)

initial_condition = initial_condition_convergence_test

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 = (0.0, 0.0, 0.0)
coordinates_max = (2.0, 2.0, 2.0)
mesh = TreeMesh(coordinates_min, coordinates_max,
initial_refinement_level = 2,
n_cells_max = 10_000,
periodicity = true)

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

###############################################################################
# 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)

alive_callback = AliveCallback(analysis_interval = analysis_interval)

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

stepsize_callback = StepsizeCallback(cfl = 0.8)

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
Expand Up @@ -59,11 +59,11 @@ coordinates_max = (1.0, 1.0, 1.0)
mesh = TreeMesh(coordinates_min, coordinates_max,
initial_refinement_level = 3,
n_cells_max = 100_000,
periodicity = true)
periodicity = false)

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

###############################################################################
# ODE solvers, callbacks etc.
Expand Down
186 changes: 186 additions & 0 deletions src/equations/ideal_glm_mhd_3d.jl
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,9 @@ end
# For `VolumeIntegralSubcellLimiting` the nonconservative flux is created as a callable struct to
# enable dispatch on the type of the nonconservative term (symmetric / jump).
"""
flux_nonconservative_powell_local_symmetric(u_ll, u_rr,
orientation::Integer,
equations::IdealGlmMhdEquations3D)
flux_nonconservative_powell_local_symmetric(u_ll, u_rr,
normal_direction::AbstractVector,
equations::IdealGlmMhdEquations3D)
Expand All @@ -389,6 +392,58 @@ compute the subcell fluxes in dg_3d_subcell_limiters.jl.
[DOI: 10.1016/j.jcp.2023.112607](https://doi.org/10.1016/j.jcp.2023.112607).
[arXiv: 2211.14009](https://doi.org/10.48550/arXiv.2211.14009).
"""
@inline function (noncons_flux::FluxNonConservativePowellLocalSymmetric)(u_ll, u_rr,
orientation::Integer,
equations::IdealGlmMhdEquations3D)
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr

v1_ll = rho_v1_ll / rho_ll
v2_ll = rho_v2_ll / rho_ll
v3_ll = rho_v3_ll / rho_ll
v_dot_B_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll

# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)
# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})
psi_avg = (psi_ll + psi_rr) #* 0.5 # The flux is already multiplied by 0.5 wherever it is used in the code
if orientation == 1
B1_avg = (B1_ll + B1_rr) #* 0.5 # The flux is already multiplied by 0.5 wherever it is used in the code
f = SVector(0,
B1_ll * B1_avg,
B2_ll * B1_avg,
B3_ll * B1_avg,
v_dot_B_ll * B1_avg + v1_ll * psi_ll * psi_avg,
v1_ll * B1_avg,
v2_ll * B1_avg,
v3_ll * B1_avg,
v1_ll * psi_avg)
elseif orientation == 2
B2_avg = (B2_ll + B2_rr) #* 0.5 # The flux is already multiplied by 0.5 wherever it is used in the code
f = SVector(0,
B1_ll * B2_avg,
B2_ll * B2_avg,
B3_ll * B2_avg,
v_dot_B_ll * B2_avg + v2_ll * psi_ll * psi_avg,
v1_ll * B2_avg,
v2_ll * B2_avg,
v3_ll * B2_avg,
v2_ll * psi_avg)
else # orientation == 3
B3_avg = (B3_ll + B3_rr) #* 0.5 # The flux is already multiplied by 0.5 wherever it is used in the code
f = SVector(0,
B1_ll * B3_avg,
B2_ll * B3_avg,
B3_ll * B3_avg,
v_dot_B_ll * B3_avg + v3_ll * psi_ll * psi_avg,
v1_ll * B3_avg,
v2_ll * B3_avg,
v3_ll * B3_avg,
v3_ll * psi_avg)
end

return f
end

@inline function (noncons_flux::FluxNonConservativePowellLocalSymmetric)(u_ll, u_rr,
normal_direction::AbstractVector,
equations::IdealGlmMhdEquations3D)
Expand Down Expand Up @@ -428,6 +483,10 @@ compute the subcell fluxes in dg_3d_subcell_limiters.jl.
end

"""
flux_nonconservative_powell_local_symmetric(u_ll, orientation::Integer,
equations::IdealGlmMhdEquations3D,
nonconservative_type::NonConservativeLocal,
nonconservative_term::Integer)
flux_nonconservative_powell_local_symmetric(u_ll, normal_direction_ll::AbstractVector,
equations::IdealGlmMhdEquations3D,
nonconservative_type::NonConservativeLocal,
Expand All @@ -442,6 +501,68 @@ the non-conservative staggered "fluxes" for subcell limiting. See, e.g.,

This function is used to compute the subcell fluxes in dg_3d_subcell_limiters.jl.
"""
@inline function (noncons_flux::FluxNonConservativePowellLocalSymmetric)(u_ll,
orientation::Integer,
equations::IdealGlmMhdEquations3D,
nonconservative_type::NonConservativeLocal,
nonconservative_term::Integer)
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll

if nonconservative_term == 1
# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)
v1_ll = rho_v1_ll / rho_ll
v2_ll = rho_v2_ll / rho_ll
v3_ll = rho_v3_ll / rho_ll
v_dot_B_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll
f = SVector(0,
B1_ll,
B2_ll,
B3_ll,
v_dot_B_ll,
v1_ll,
v2_ll,
v3_ll,
0)
else #nonconservative_term ==2
# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})
if orientation == 1
v1_ll = rho_v1_ll / rho_ll
f = SVector(0,
0,
0,
0,
v1_ll * psi_ll,
0,
0,
0,
v1_ll)
elseif orientation == 2
v2_ll = rho_v2_ll / rho_ll
f = SVector(0,
0,
0,
0,
v2_ll * psi_ll,
0,
0,
0,
v2_ll)
else #orientation == 3
v3_ll = rho_v3_ll / rho_ll
f = SVector(0,
0,
0,
0,
v3_ll * psi_ll,
0,
0,
0,
v3_ll)
end
end
return f
end

@inline function (noncons_flux::FluxNonConservativePowellLocalSymmetric)(u_ll,
normal_direction_ll::AbstractVector,
equations::IdealGlmMhdEquations3D,
Expand Down Expand Up @@ -487,6 +608,10 @@ This function is used to compute the subcell fluxes in dg_3d_subcell_limiters.jl
end

"""
flux_nonconservative_powell_local_symmetric(u_ll, orientation::Integer,
equations::IdealGlmMhdEquations3D,
nonconservative_type::NonConservativeSymmetric,
nonconservative_term::Integer)
flux_nonconservative_powell_local_symmetric(u_ll, normal_direction_avg::AbstractVector,
equations::IdealGlmMhdEquations3D,
nonconservative_type::NonConservativeSymmetric,
Expand All @@ -501,6 +626,67 @@ the non-conservative staggered "fluxes" for subcell limiting. See, e.g.,

This function is used to compute the subcell fluxes in dg_3d_subcell_limiters.jl.
"""
@inline function (noncons_flux::FluxNonConservativePowellLocalSymmetric)(u_ll, u_rr,
orientation::Integer,
equations::IdealGlmMhdEquations3D,
nonconservative_type::NonConservativeSymmetric,
nonconservative_term::Integer)
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr

if nonconservative_term == 1
# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)
if orientation == 1
B1_avg = (B1_ll + B1_rr)#* 0.5 # The flux is already multiplied by 0.5 wherever it is used in the code
f = SVector(0,
B1_avg,
B1_avg,
B1_avg,
B1_avg,
B1_avg,
B1_avg,
B1_avg,
0)
elseif orientation == 2
B2_avg = (B2_ll + B2_rr)#* 0.5 # The flux is already multiplied by 0.5 wherever it is used in the code
f = SVector(0,
B2_avg,
B2_avg,
B2_avg,
B2_avg,
B2_avg,
B2_avg,
B2_avg,
0)
else # orientation == 3
B3_avg = (B3_ll + B3_rr)#* 0.5 # The flux is already multiplied by 0.5 wherever it is used in the code
f = SVector(0,
B3_avg,
B3_avg,
B3_avg,
B3_avg,
B3_avg,
B3_avg,
B3_avg,
0)
end
else #nonconservative_term == 2
# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})
psi_avg = (psi_ll + psi_rr)#* 0.5 # The flux is already multiplied by 0.5 wherever it is used in the code
f = SVector(0,
0,
0,
0,
psi_avg,
0,
0,
0,
psi_avg)
end

return f
end

@inline function (noncons_flux::FluxNonConservativePowellLocalSymmetric)(u_ll, u_rr,
normal_direction_avg::AbstractVector,
equations::IdealGlmMhdEquations3D,
Expand Down
Loading
Loading