Path: blob/main/docs/literate/src/files/shock_capturing.jl
5591 views
#src # Shock capturing with flux differencing and stage limiter12# This tutorial contains a short summary of the idea of shock capturing for DGSEM with flux differencing3# and its implementation in [Trixi.jl](https://github.com/trixi-framework/Trixi.jl).4# In the second part, an implementation of a positivity preserving limiter is added to the simulation.56# # Shock capturing with flux differencing78# The following rough explanation is on a very basic level. More information about an entropy stable9# shock-capturing strategy for DGSEM discretizations of advection dominated problems, such as the10# compressible Euler equations or the compressible Navier-Stokes equations, can be found in11# [Hennemann et al. (2021)](https://doi.org/10.1016/j.jcp.2020.109935). In12# [Rueda-RamÃrez et al. (2021)](https://doi.org/10.1016/j.jcp.2021.110580) you find the extension to13# the systems with non-conservative terms, such as the compressible magnetohydrodynamics (MHD) equations.14# Furthermore, the latter paper introduces also second-order subcell finite volume stabilization.1516# The strategy for a shock-capturing method presented by Hennemann et al. is based on a hybrid blending17# of a high-order DG method with a low-order variant. The low-order subcell first order finite volume (FV) method is created18# directly with the Legendre-Gauss-Lobatto (LGL) nodes already used for the high-order DGSEM.19# Then, the final method is a convex combination with regulating indicator $\alpha$ of these two methods.2021# Since the surface integral is equal for both the DG and the subcell FV method, only the volume integral divides22# between the two methods.2324# This strategy for the volume integral is implemented in Trixi.jl under the name of25# [`VolumeIntegralShockCapturingHG`](@ref) with the three parameters of the indicator and the volume fluxes for26# the DG and the subcell FV method.2728# Note, that the DG method is based on the flux differencing formulation. Hence, you have to use a29# two-point flux, such as [`flux_ranocha`](@ref), [`flux_shima_etal`](@ref), [`flux_chandrashekar`](@ref) or [`flux_kennedy_gruber`](@ref),30# for the DG volume flux. We would recommend to use the entropy conserving flux `flux_ranocha` by31# [Ranocha (2018)](https://cuvillier.de/en/shop/publications/7743) for the compressible Euler equations.32# ````julia33# volume_integral = VolumeIntegralShockCapturingHG(indicator_sc;34# volume_flux_dg=volume_flux_dg,35# volume_flux_fv=volume_flux_fv)36# ````3738# The volume integral employing second-order FV stabilization is constructed similarly:39# ````julia40# volume_integral = VolumeIntegralShockCapturingRRG(basis, indicator_sc;41# volume_flux_dg=flux_central,42# volume_flux_fv=flux_lax_friedrichs,43# slope_limiter=minmod)44# ````45# In addition to the parameters of the HG method, the DG `basis` **must** be supplied here.46# The `slope_limiter` keyword argument is optional and defaults to `minmod`.47# A list of supported slope limiters can be found in the reference of [`VolumeIntegralShockCapturingRRG`](@ref).4849# We now focus on a choice of the shock capturing indicator `indicator_sc`, which can be used for both50# the first-order and the second-order FV stabilization.51# A possible indicator is $\alpha_{HG}$ presented by Hennemann et al. (p.10), which depends on the52# current approximation with modal coefficients $\{m_j\}_{j=0}^N$ of a given `variable`.5354# The indicator is calculated for every DG element by itself. First, we calculate a smooth $\alpha$ by55# ```math56# \alpha = \frac{1}{1+\exp(-\frac{s}{\mathbb{T}}(\mathbb{E}-\mathbb{T}))}57# ```58# with the total energy $\mathbb{E}=\max\big(\frac{m_N^2}{\sum_{j=0}^N m_j^2}, \frac{m_{N-1}^2}{\sum_{j=0}^{N-1} m_j^2}\big)$,59# threshold $\mathbb{T}= 0.5 * 10^{-1.8*(N+1)^{1/4}}$ and parameter $s=\ln\big(\frac{1-0.0001}{0.0001}\big)\approx 9.21024$.6061# For computational efficiency, $\alpha_{min}$ is introduced and used for62# ```math63# \tilde{\alpha} = \begin{cases}64# 0, & \text{if } \alpha<\alpha_{min}\\65# \alpha, & \text{if } \alpha_{min}\leq \alpha \leq 1- \alpha_{min}\\66# 1, & \text{if } 1-\alpha_{min}<\alpha.67# \end{cases}68# ```6970# Moreover, the parameter $\alpha_{max}$ sets a maximal value for $\alpha$ by71# ```math72# \alpha = \min\{\tilde{\alpha}, \alpha_{max}\}.73# ```74# This allows to control the maximal dissipation.7576# To remove numerical artifact the final indicator is smoothed with all the neighboring elements'77# indicators. This is activated with `alpha_smooth=true`.78# ```math79# \alpha_{HG} = \max_E \{ \alpha, 0.5 * \alpha_E\},80# ```81# where $E$ are all elements sharing a face with the current element.8283# Furthermore, you can specify the variable used for the calculation. For instance you can choose84# `density`, `pressure` or both with `density_pressure` for the compressible Euler equations.85# For every equation there is also the option to use the first conservation variable with `first`.8687# This indicator is implemented in Trixi.jl and called [`IndicatorHennemannGassner`](@ref) with the parameters88# `equations`, `basis`, `alpha_max`, `alpha_min`, `alpha_smooth` and `variable`.89# ````julia90# indicator_sc = IndicatorHennemannGassner(equations, basis,91# alpha_max=0.5,92# alpha_min=0.001,93# alpha_smooth=true,94# variable=variable)95# ````9697# # Positivity preserving limiter9899# Some numerical solutions are physically meaningless, for instance negative values of pressure100# or density for the compressible Euler equations. This often results in crashed simulations since101# the calculation of numerical fluxes or stable time steps uses mathematical operations like roots or102# logarithms. One option to avoid these cases are a-posteriori positivity preserving limiters.103# Trixi.jl provides the fully-discrete positivity-preserving limiter of104# [Zhang, Shu (2011)](https://doi.org/10.1098/rspa.2011.0153).105106# It works the following way. For every passed (scalar) variable and for every DG element we calculate107# the minimal value $value_\text{min}$. If this value falls below the given threshold $\varepsilon$,108# the approximation is slightly adapted such that the minimal value of the relevant variable lies109# now above the threshold.110# ```math111# \underline{u}^\text{new} = \theta * \underline{u} + (1-\theta) * u_\text{mean}112# ```113# where $\underline{u}$ are the collected pointwise evaluation coefficients in element $e$ and114# $u_\text{mean}$ the integral mean of the quantity in $e$. The new coefficients are a convex combination115# of these two values with factor116# ```math117# \theta = \frac{value_\text{mean} - \varepsilon}{value_\text{mean} - value_\text{min}},118# ```119# where $value_\text{mean}$ is the relevant variable evaluated for the mean value $u_\text{mean}$.120121# The adapted approximation keeps the exact same mean value, but the relevant variable is now greater122# or equal the threshold $\varepsilon$ at every node in every element.123124# We specify the variables the way we did before for the shock capturing variables. For the125# compressible Euler equations `density`, `pressure` or the combined variable `density_pressure`126# are a reasonable choice.127128# You can implement the limiter in Trixi.jl using [`PositivityPreservingLimiterZhangShu`](@ref) with parameters129# `threshold` and `variables`.130# ````julia131# stage_limiter! = PositivityPreservingLimiterZhangShu(thresholds=thresholds,132# variables=variables)133# ````134# Then, the limiter is added to the time integration method in the `solve` function. For instance, like135# ````julia136# CarpenterKennedy2N54(stage_limiter!, williamson_condition=false)137# ````138# or139# ````julia140# SSPRK43(stage_limiter!).141# ````142143# # Simulation with shock capturing and positivity preserving144145# Now, we can run a simulation using the described methods of shock capturing and positivity146# preserving limiters. We want to give an example for the 2D compressible Euler equations.147using OrdinaryDiffEqLowStorageRK148using Trixi149150equations = CompressibleEulerEquations2D(1.4)151152# As our initial condition we use the Sedov blast wave setup.153function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D)154## Set up polar coordinates155inicenter = SVector(0.0, 0.0)156x_norm = x[1] - inicenter[1]157y_norm = x[2] - inicenter[2]158r = sqrt(x_norm^2 + y_norm^2)159160r0 = 0.21875 # = 3.5 * smallest dx (for domain length=4 and max-ref=6)161## r0 = 0.5 # = more reasonable setup162E = 1.0163p0_inner = 3 * (equations.gamma - 1) * E / (3 * pi * r0^2)164p0_outer = 1.0e-5 # = true Sedov setup165## p0_outer = 1.0e-3 # = more reasonable setup166167## Calculate primitive variables168rho = 1.0169v1 = 0.0170v2 = 0.0171p = r > r0 ? p0_outer : p0_inner172173return prim2cons(SVector(rho, v1, v2, p), equations)174end175initial_condition = initial_condition_sedov_blast_wave176#-177basis = LobattoLegendreBasis(3)178179# We set the numerical fluxes and divide between the surface flux and the two volume fluxes for the DG180# and FV method. Here, we are using [`flux_lax_friedrichs`](@ref) and [`flux_ranocha`](@ref).181surface_flux = flux_lax_friedrichs182volume_flux = flux_ranocha183184# Now, we specify the shock capturing indicator $\alpha$.185186# We implement the described indicator of Hennemann, Gassner as explained above with parameters187# `equations`, `basis`, `alpha_max`, `alpha_min`, `alpha_smooth` and `variable`.188# Since density and pressure are the critical variables in this example, we use189# `density_pressure = density * pressure = rho * p` as indicator variable.190indicator_sc = IndicatorHennemannGassner(equations, basis,191alpha_max = 0.5,192alpha_min = 0.001,193alpha_smooth = true,194variable = density_pressure)195196# Now, we can use the defined fluxes and the indicator to implement the volume integral using shock197# capturing.198volume_integral = VolumeIntegralShockCapturingHG(indicator_sc;199volume_flux_dg = volume_flux,200volume_flux_fv = surface_flux)201202# We finalize the discretization by implementing Trixi.jl's `solver`, `mesh`, `semi` and `ode`,203# while `solver` now has the extra parameter `volume_integral`.204solver = DGSEM(basis, surface_flux, volume_integral)205206coordinates_min = (-2.0, -2.0)207coordinates_max = (2.0, 2.0)208mesh = TreeMesh(coordinates_min, coordinates_max,209initial_refinement_level = 6,210n_cells_max = 10_000,211periodicity = true)212213semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver;214boundary_conditions = boundary_condition_periodic)215216tspan = (0.0, 1.0)217ode = semidiscretize(semi, tspan);218219# We add some callbacks to get an solution analysis and use a CFL-based time step size calculation.220analysis_callback = AnalysisCallback(semi, interval = 100)221222stepsize_callback = StepsizeCallback(cfl = 0.8)223224callbacks = CallbackSet(analysis_callback, stepsize_callback);225226# We now run the simulation using the positivity preserving limiter of Zhang and Shu for the variables227# density and pressure.228stage_limiter! = PositivityPreservingLimiterZhangShu(thresholds = (5.0e-6, 5.0e-6),229variables = (Trixi.density, pressure))230231sol = solve(ode, CarpenterKennedy2N54(stage_limiter!, williamson_condition = false);232dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback233ode_default_options()..., callback = callbacks);234235using Plots236plot(sol)237238# # Entropy bounded limiter239240# As argued in the description of the positivity preserving limiter above it might sometimes be241# necessary to apply advanced techniques to ensure a physically meaningful solution.242# Apart from the positivity of pressure and density, the physical entropy of the system should increase243# over the course of a simulation, see e.g. [this](https://doi.org/10.1016/0168-9274(86)90029-2) paper by Tadmor where this property is244# shown for the compressible Euler equations.245# As this is not necessarily the case for the numerical approximation (especially for the high-order, non-diffusive DG discretizations),246# Lv and Ihme devised an a-posteriori limiter in [this paper](https://doi.org/10.1016/j.jcp.2015.04.026) which can be applied after each Runge-Kutta stage.247# This limiter enforces a non-decrease in the physical, thermodynamic entropy $S$248# by bounding the entropy decrease (entropy increase is always tolerated) $\Delta S$ in each grid cell.249#250# This translates into a requirement that the entropy of the limited approximation $S\Big(\mathcal{L}\big[\boldsymbol u(\Delta t) \big] \Big)$ should be251# greater or equal than the previous iterates' entropy $S\big(\boldsymbol u(0) \big)$, enforced at each quadrature point:252# ```math253# S\Big(\mathcal{L}\big[\boldsymbol u(\Delta t, \boldsymbol{x}_i) \big] \Big) \overset{!}{\geq} S\big(\boldsymbol u(0, \boldsymbol{x}_i) \big), \quad i = 1, \dots, (k+1)^d254# ```255# where $k$ denotes the polynomial degree of the element-wise solution and $d$ is the spatial dimension.256# For an ideal gas, the thermodynamic entropy $S(\boldsymbol u) = S(p, \rho)$ is given by257# ```math258# S(p, \rho) = \ln \left( \frac{p}{\rho^\gamma} \right) \: .259# ```260# Thus, the non-decrease in entropy can be reformulated as261# ```math262# p(\boldsymbol{x}_i) - e^{ S\big(\boldsymbol u(0, \boldsymbol{x}_i) \big)} \cdot \rho(\boldsymbol{x}_i)^\gamma \overset{!}{\geq} 0, \quad i = 1, \dots, (k+1)^d \: .263# ```264# In a practical simulation, we might tolerate a maximum (exponentiated) entropy decrease per element, i.e.,265# ```math266# \Delta e^S \coloneqq \min_{i} \left\{ p(\boldsymbol{x}_i) - e^{ S\big(\boldsymbol u(0, \boldsymbol{x}_i) \big)} \cdot \rho(\boldsymbol{x}_i)^\gamma \right\} < c267# ```268# with hyper-parameter $c$ which is to be specified by the user.269# The default value for the corresponding parameter $c=$ `exp_entropy_decrease_max` is set to $-10^{-13}$, i.e., slightly less than zero to270# avoid spurious limiter actions for cells in which the entropy remains effectively constant.271# Other values can be specified by setting the `exp_entropy_decrease_max` keyword in the constructor of the limiter:272# ````julia273# stage_limiter! = EntropyBoundedLimiter(exp_entropy_decrease_max=-1e-9)274# ````275# Smaller values (larger in absolute value) for `exp_entropy_decrease_max` relax the entropy increase requirement and are thus less diffusive.276# On the other hand, for larger values (smaller in absolute value) of `exp_entropy_decrease_max` the limiter acts more often and the solution becomes more diffusive.277#278# In particular, we compute again a limiting parameter $\vartheta \in [0, 1]$ which is then used to blend the279# unlimited nodal values $\boldsymbol u$ with the mean value $\boldsymbol u_{\text{mean}}$ of the element:280# ```math281# \mathcal{L} [\boldsymbol u](\vartheta) \coloneqq (1 - \vartheta) \boldsymbol u + \vartheta \cdot \boldsymbol u_{\text{mean}}282# ```283# For the exact definition of $\vartheta$ the interested user is referred to section 4.4 of the paper by Lv and Ihme.284# Note that therein the limiting parameter is denoted by $\epsilon$, which is not to be confused with the threshold $\varepsilon$ of the Zhang-Shu limiter.285286# As for the positivity preserving limiter, the entropy bounded limiter may be applied after every Runge-Kutta stage.287# Both fixed timestep methods such as [`CarpenterKennedy2N54`](https://docs.sciml.ai/DiffEqDocs/stable/solvers/ode_solve/) and288# adaptive timestep methods such as [`SSPRK43`](https://docs.sciml.ai/DiffEqDocs/stable/solvers/ode_solve/) are supported.289# We would like to remark that of course every `stage_limiter!` can also be used as a `step_limiter!`, i.e.,290# acting only after the full time step has been taken.291292# As an example, we consider a variant of the [1D medium blast wave example](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_1d_dgsem/elixir_euler_blast_wave.jl)293# wherein the shock capturing method discussed above is employed to handle the shock.294# Here, we use a standard DG solver with HLLC surface flux:295using Trixi296297solver = DGSEM(polydeg = 3, surface_flux = flux_hllc)298299# The remaining setup is the same as in the standard example:300301using OrdinaryDiffEqSSPRK302303###############################################################################304# semidiscretization of the compressible Euler equations305306equations = CompressibleEulerEquations1D(1.4)307308function initial_condition_blast_wave(x, t, equations::CompressibleEulerEquations1D)309## Modified From Hennemann & Gassner JCP paper 2020 (Sec. 6.3) -> "medium blast wave"310## Set up polar coordinates311inicenter = SVector(0.0)312x_norm = x[1] - inicenter[1]313r = abs(x_norm)314## The following code is equivalent to315## phi = atan(0.0, x_norm)316## cos_phi = cos(phi)317## in 1D but faster318cos_phi = x_norm > 0 ? one(x_norm) : -one(x_norm)319320## Calculate primitive variables321rho = r > 0.5 ? 1.0 : 1.1691322v1 = r > 0.5 ? 0.0 : 0.1882 * cos_phi323p = r > 0.5 ? 1.0E-3 : 1.245324325return prim2cons(SVector(rho, v1, p), equations)326end327initial_condition = initial_condition_blast_wave328329coordinates_min = (-2.0,)330coordinates_max = (2.0,)331mesh = TreeMesh(coordinates_min, coordinates_max,332initial_refinement_level = 6,333n_cells_max = 10_000,334periodicity = true)335336semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver;337boundary_conditions = boundary_condition_periodic)338339###############################################################################340# ODE solvers, callbacks etc.341342tspan = (0.0, 12.5)343ode = semidiscretize(semi, tspan)344345summary_callback = SummaryCallback()346347analysis_interval = 100348349analysis_callback = AnalysisCallback(semi, interval = analysis_interval)350351alive_callback = AliveCallback(analysis_interval = analysis_interval)352353stepsize_callback = StepsizeCallback(cfl = 0.5)354355callbacks = CallbackSet(summary_callback,356analysis_callback, alive_callback,357stepsize_callback)358359# We specify the `stage_limiter!` supplied to the classic SSPRK33 integrator360# with strict entropy increase enforcement361# (recall that the tolerated exponentiated entropy decrease is set to -1e-13).362stage_limiter! = EntropyBoundedLimiter()363364# We run the simulation with the SSPRK33 method and the entropy bounded limiter:365sol = solve(ode, SSPRK33(stage_limiter!);366dt = 1.0,367callback = callbacks);368369using Plots370plot(sol)371372# ## Package versions373374# These results were obtained using the following versions.375376using InteractiveUtils377versioninfo()378379using Pkg380Pkg.status(["Trixi", "OrdinaryDiffEqLowStorageRK", "OrdinaryDiffEqSSPRK", "Plots"],381mode = PKGMODE_MANIFEST)382383384