Skip to content
8 changes: 6 additions & 2 deletions examples/p4est_3d_dgsem/elixir_euler_sedov_sc_subcell.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ polydeg = 3
basis = LobattoLegendreBasis(polydeg)
limiter_idp = SubcellLimiterIDP(equations, basis;
positivity_variables_cons = ["rho"],
positivity_variables_nonlinear = [pressure])
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)
Expand Down Expand Up @@ -91,7 +95,7 @@ callbacks = CallbackSet(summary_callback,
###############################################################################
# run the simulation

stage_callbacks = (SubcellLimiterIDPCorrection(),)
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
Expand Down
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
109 changes: 109 additions & 0 deletions src/callbacks_stage/subcell_bounds_check_3d.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
# Since these FMAs can increase the performance of many numerical algorithms,
# we need to opt-in explicitly.
# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
@muladd begin
#! format: noindent

@inline function check_bounds(u, equations::AbstractEquations{3},
solver, cache, limiter::SubcellLimiterIDP)
(; local_twosided, positivity, local_onesided) = solver.volume_integral.limiter
(; variable_bounds) = limiter.cache.subcell_limiter_coefficients
(; idp_bounds_delta_local, idp_bounds_delta_global) = limiter.cache

# Note: In order to get the maximum deviation from the target bounds, this bounds check
# requires a reduction in every RK stage and for every enabled limiting option. To make
# this Thread-parallel we are using Polyester.jl's (at least v0.7.10) `@batch reduction`
# functionality.
# Although `@threaded` and `@batch` are currently used equivalently in Trixi.jl, we use
# `@batch` here to allow a possible redefinition of `@threaded` without creating errors here.
# See also https://github.com/trixi-framework/Trixi.jl/pull/1888#discussion_r1537785293.

if local_twosided
for v in limiter.local_twosided_variables_cons
v_string = string(v)
key_min = Symbol(v_string, "_min")
key_max = Symbol(v_string, "_max")
deviation_min = idp_bounds_delta_local[key_min]
deviation_max = idp_bounds_delta_local[key_max]
@batch reduction=((max, deviation_min), (max, deviation_max)) for element in eachelement(solver,
cache)
for k in eachnode(solver), j in eachnode(solver), i in eachnode(solver)
var = u[v, i, j, k, element]
# Note: We always save the absolute deviations >= 0 and therefore use the
# `max` operator for the lower and upper bound. The different directions of
# upper and lower bound are considered in their calculations with a
# different sign.
deviation_min = max(deviation_min,
variable_bounds[key_min][i, j, k, element] -
var)
deviation_max = max(deviation_max,
var -
variable_bounds[key_max][i, j, k, element])
end
end
idp_bounds_delta_local[key_min] = deviation_min
idp_bounds_delta_local[key_max] = deviation_max
end
end
if local_onesided
for (variable, min_or_max) in limiter.local_onesided_variables_nonlinear
key = Symbol(string(variable), "_", string(min_or_max))
deviation = idp_bounds_delta_local[key]
sign_ = min_or_max(1.0, -1.0)
@batch reduction=(max, deviation) for element in eachelement(solver, cache)
for k in eachnode(solver), j in eachnode(solver), i in eachnode(solver)
v = variable(get_node_vars(u, equations, solver, i, j, k, element),
equations)
# Note: We always save the absolute deviations >= 0 and therefore use the
# `max` operator for lower and upper bounds. The different directions of
# upper and lower bounds are considered with `sign_`.
deviation = max(deviation,
sign_ *
(v - variable_bounds[key][i, j, k, element]))
end
end
idp_bounds_delta_local[key] = deviation
end
end
if positivity
for v in limiter.positivity_variables_cons
if v in limiter.local_twosided_variables_cons
continue
end
key = Symbol(string(v), "_min")
deviation = idp_bounds_delta_local[key]
@batch reduction=(max, deviation) for element in eachelement(solver, cache)
for k in eachnode(solver), j in eachnode(solver), i in eachnode(solver)
var = u[v, i, j, k, element]
deviation = max(deviation,
variable_bounds[key][i, j, k, element] - var)
end
end
idp_bounds_delta_local[key] = deviation
end
for variable in limiter.positivity_variables_nonlinear
key = Symbol(string(variable), "_min")
deviation = idp_bounds_delta_local[key]
@batch reduction=(max, deviation) for element in eachelement(solver, cache)
for k in eachnode(solver), j in eachnode(solver), i in eachnode(solver)
var = variable(get_node_vars(u, equations, solver, i, j, k,
element),
equations)
deviation = max(deviation,
variable_bounds[key][i, j, k, element] - var)
end
end
idp_bounds_delta_local[key] = deviation
end
end

for (key, _) in idp_bounds_delta_local
# Update global maximum deviations
idp_bounds_delta_global[key] = max(idp_bounds_delta_global[key],
idp_bounds_delta_local[key])
end

return nothing
end
end # @muladd
Loading
Loading