Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
658f8f8
Add first 3d support for subcell limiting
bennibolm Sep 17, 2025
9d75c6b
Add support for non-periodic meshes
bennibolm Sep 17, 2025
ae914d0
Fix 2d p4est
bennibolm Sep 17, 2025
91f1b83
Support nonconservative terms
bennibolm Sep 18, 2025
08ebe50
Added local*symmetric form of the Powell term for 3D
amrueda Sep 18, 2025
139300e
Add support for nonconservative terms; add test
bennibolm Sep 18, 2025
d69017c
Add `isvalid` for 3D MHD
bennibolm Sep 18, 2025
b2fcff1
Fix typo
bennibolm Sep 18, 2025
f8a6320
Added subcell-limiting-ready 3D fluxes for multi-ion MHD (P4estMesh)
amrueda Sep 18, 2025
5f53301
Fixed redefinition of constant error
amrueda Sep 19, 2025
ad96378
Fixed some bugs
amrueda Sep 19, 2025
57daab6
Added isvalid function for multi-ion MHD
amrueda Sep 19, 2025
79274d9
Elixir with constant subsonic state and boundary treatment (#2574)
Arpit-Babbar Sep 20, 2025
891c906
Refactor dimension-(in)dependent implementation of subcell limiting (…
bennibolm Sep 21, 2025
c5a9a8a
Merge branch 'main' into 3d-subcell-limiting
bennibolm Sep 22, 2025
89a84d4
Unified pressure functions for MHD
amrueda Sep 22, 2025
313cd83
Merge branch '3d-subcell-limiting' into io_simulations_subcell
amrueda Sep 23, 2025
f6e404b
Merge branch 'arr/multi_ion_subcell_3d' into io_simulations_subcell
amrueda Sep 23, 2025
63ff9df
Added a counter-streaming elixir with subcell limiting
amrueda Sep 23, 2025
d83e7bd
Added copies of 2D pressure functions for 3D MHD
amrueda Sep 23, 2025
c42281d
Merge branch '3d-subcell-limiting' into io_simulations_subcell
amrueda Sep 23, 2025
06ee72c
Improved allocations of elixir by using tuple instead of vector
amrueda Sep 24, 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
114 changes: 114 additions & 0 deletions examples/p4est_2d_dgsem/elixir_euler_subsonic_constant.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
using OrdinaryDiffEqSSPRK
using Trixi
using LinearAlgebra: norm

###############################################################################
## Semidiscretization of the compressible Euler equations

equations = CompressibleEulerEquations2D(1.4)
polydeg = 3
solver = DGSEM(polydeg = polydeg, surface_flux = flux_lax_friedrichs)

@inline function initial_condition_subsonic(x_, t, equations::CompressibleEulerEquations2D)
rho, v1, v2, p = (0.5313, 0.0, 0.0, 0.4)

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

initial_condition = initial_condition_subsonic

# Calculate the boundary flux from the inner state while using the pressure from the outer state
# when the flow is subsonic (which is always the case in this example).

# If the naive approach of only using the inner state is used, the errors increase with the
# increase of refinement level, see https://github.com/trixi-framework/Trixi.jl/issues/2530
# These errors arise from the corner points in this test.

# See the reference below for a discussion on inflow/outflow boundary conditions. The subsonic
# outflow boundary conditions are discussed in Section 2.3.
#
# - Jan-Reneé Carlson (2011)
# Inflow/Outflow Boundary Conditions with Application to FUN3D.
# [NASA TM 20110022658](https://ntrs.nasa.gov/citations/20110022658)
@inline function boundary_condition_outflow_general(u_inner,
normal_direction::AbstractVector, x, t,
surface_flux_function,
equations::CompressibleEulerEquations2D)

# This would be for the general case where we need to check the magnitude of the local Mach number
norm_ = norm(normal_direction)
# Normalize the vector without using `normalize` since we need to multiply by the `norm_` later
normal = normal_direction / norm_

# Rotate the internal solution state
u_local = Trixi.rotate_to_x(u_inner, normal, equations)

# Compute the primitive variables
rho_local, v_normal, v_tangent, p_local = cons2prim(u_local, equations)

# Compute local Mach number
a_local = sqrt(equations.gamma * p_local / rho_local)
Mach_local = abs(v_normal / a_local)
if Mach_local <= 1.0 # The `if` is not needed in this elixir but kept for generality
# In general, `p_local` need not be available from the initial condition
p_local = pressure(initial_condition_subsonic(x, t, equations), equations)
end

# Create the `u_surface` solution state where the local pressure is possibly set from an external value
prim = SVector(rho_local, v_normal, v_tangent, p_local)
u_boundary = prim2cons(prim, equations)
u_surface = Trixi.rotate_from_x(u_boundary, normal, equations)

# Compute the flux using the appropriate mixture of internal / external solution states
return flux(u_surface, normal_direction, equations)
end

boundary_conditions = Dict(:x_neg => boundary_condition_outflow_general,
:x_pos => boundary_condition_outflow_general,
:y_neg => boundary_condition_outflow_general,
:y_pos => boundary_condition_outflow_general)

coordinates_min = (0.0, 0.0)
coordinates_max = (1.0, 1.0)

trees_per_dimension = (1, 1)

mesh = P4estMesh(trees_per_dimension, polydeg = polydeg,
coordinates_min = coordinates_min, coordinates_max = coordinates_max,
initial_refinement_level = 3,
periodicity = false)

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

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

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

summary_callback = SummaryCallback()

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

alive_callback = AliveCallback(analysis_interval = 100)

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

stepsize_callback = StepsizeCallback(cfl = 0.5)

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

###############################################################################
## Run the simulation
sol = solve(ode, SSPRK54();
dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
save_everystep = false, callback = callbacks);
111 changes: 111 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,111 @@
using OrdinaryDiffEqLowStorageRK
using Trixi

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

equations = CompressibleEulerEquations3D(1.4)

"""
initial_condition_medium_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

# Up to version 0.13.0, `max_abs_speed_naive` was used as the default wave speed estimate of
# `const flux_lax_friedrichs = FluxLaxFriedrichs(), i.e., `FluxLaxFriedrichs(max_abs_speed = max_abs_speed_naive)`.
# In the `StepsizeCallback`, though, the less diffusive `max_abs_speeds` is employed which is consistent with `max_abs_speed`.
# Thus, we exchanged in PR#2458 the default wave speed used in the LLF flux to `max_abs_speed`.
# To ensure that every example still runs we specify explicitly `FluxLaxFriedrichs(max_abs_speed_naive)`.
# We remark, however, that the now default `max_abs_speed` is in general recommended due to compliance with the
# `StepsizeCallback` (CFL-Condition) and less diffusion.
surface_flux = FluxLaxFriedrichs(max_abs_speed_naive)
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
ode_default_options()..., 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);
Loading
Loading