Path: blob/main/src/solvers/dgsem/indicators.jl
5591 views
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).1# Since these FMAs can increase the performance of many numerical algorithms,2# we need to opt-in explicitly.3# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.4@muladd begin5#! format: noindent67# Abstract supertype of indicators used for AMR, shock capturing, and8# adaptive volume-integral selection9abstract type AbstractIndicator end1011function create_cache(typ::Type{IndicatorType},12semi) where {IndicatorType <: AbstractIndicator}13return create_cache_indicator_for_amr(typ, mesh_equations_solver_cache(semi)...)14end1516# this method is used when the indicator is constructed as for AMR17function create_cache_indicator_for_amr(typ::Type{IndicatorType},18mesh, equations::AbstractEquations, dg::DGSEM,19cache) where {IndicatorType <:20AbstractIndicator}21return create_cache(typ, equations, dg.basis)22end2324function get_element_variables!(element_variables, indicator::AbstractIndicator,25::AbstractVolumeIntegralShockCapturing)26element_variables[:indicator_shock_capturing] = indicator.cache.alpha27return nothing28end2930"""31IndicatorHennemannGassner(equations::AbstractEquations, basis;32alpha_max=0.5,33alpha_min=0.001,34alpha_smooth=true,35variable)36IndicatorHennemannGassner(semi::AbstractSemidiscretization;37alpha_max=0.5,38alpha_min=0.001,39alpha_smooth=true,40variable)4142Indicator used for shock-capturing (when passing the `equations` and the `basis`)43or adaptive mesh refinement (AMR, when passing the `semi`).4445See also [`VolumeIntegralShockCapturingHG`](@ref).4647## References4849- Hennemann, Gassner (2020)50"A provably entropy stable subcell shock capturing approach for high order split form DG"51[arXiv: 2008.12044](https://arxiv.org/abs/2008.12044)52"""53struct IndicatorHennemannGassner{RealT <: Real, Variable, Cache} <: AbstractIndicator54alpha_max::RealT55alpha_min::RealT56alpha_smooth::Bool57variable::Variable58cache::Cache59end6061# this method is used when the indicator is constructed as for shock-capturing volume integrals62function IndicatorHennemannGassner(equations::AbstractEquations, basis;63alpha_max = 0.5,64alpha_min = 0.001,65alpha_smooth = true,66variable)67alpha_max, alpha_min = promote(alpha_max, alpha_min)68cache = create_cache(IndicatorHennemannGassner, equations, basis)69return IndicatorHennemannGassner{typeof(alpha_max), typeof(variable),70typeof(cache)}(alpha_max,71alpha_min,72alpha_smooth,73variable,74cache)75end7677# this method is used when the indicator is constructed as for AMR78function IndicatorHennemannGassner(semi::AbstractSemidiscretization;79alpha_max = 0.5,80alpha_min = 0.001,81alpha_smooth = true,82variable)83alpha_max, alpha_min = promote(alpha_max, alpha_min)84cache = create_cache(IndicatorHennemannGassner, semi)85return IndicatorHennemannGassner{typeof(alpha_max), typeof(variable),86typeof(cache)}(alpha_max,87alpha_min,88alpha_smooth,89variable,90cache)91end9293function Base.show(io::IO, indicator::IndicatorHennemannGassner)94@nospecialize indicator # reduce precompilation time9596print(io, "IndicatorHennemannGassner(")97print(io, indicator.variable)98print(io, ", alpha_max=", indicator.alpha_max)99print(io, ", alpha_min=", indicator.alpha_min)100print(io, ", alpha_smooth=", indicator.alpha_smooth)101print(io, ")")102return nothing103end104105function Base.show(io::IO, ::MIME"text/plain", indicator::IndicatorHennemannGassner)106@nospecialize indicator # reduce precompilation time107setup = [108"indicator variable" => indicator.variable,109"max. α" => indicator.alpha_max,110"min. α" => indicator.alpha_min,111"smooth α" => (indicator.alpha_smooth ? "yes" : "no")112]113summary_box(io, "IndicatorHennemannGassner", setup)114return nothing115end116117function (indicator_hg::IndicatorHennemannGassner)(u, mesh, equations, dg::DGSEM, cache;118kwargs...)119@unpack alpha_smooth = indicator_hg120@unpack alpha, alpha_tmp = indicator_hg.cache121# TODO: Taal refactor, when to `resize!` stuff changed possibly by AMR?122# Shall we implement `resize!(semi::AbstractSemidiscretization, new_size)`123# or just `resize!` whenever we call the relevant methods as we do now?124resize!(alpha, nelements(dg, cache))125if alpha_smooth126resize!(alpha_tmp, nelements(dg, cache))127end128129# magic parameters130# TODO: Are there better values for Float32?131RealT = real(dg)132threshold = 0.5f0 * 10^(convert(RealT, -1.8) * nnodes(dg)^convert(RealT, 0.25))133o_0001 = convert(RealT, 0.0001)134parameter_s = log((1 - o_0001) / o_0001)135136@threaded for element in eachelement(dg, cache)137# This is dispatched by mesh dimension.138# Use this function barrier and unpack inside to avoid passing closures to139# Polyester.jl with `@batch` (`@threaded`).140# Otherwise, `@threaded` does not work here with Julia ARM on macOS.141# See https://github.com/JuliaSIMD/Polyester.jl/issues/88.142calc_indicator_hennemann_gassner!(indicator_hg, threshold, parameter_s, u,143element, mesh, equations, dg, cache)144end145146if alpha_smooth147apply_smoothing!(mesh, alpha, alpha_tmp, dg, cache)148end149150return alpha151end152153"""154IndicatorLöhner (equivalent to IndicatorLoehner)155156IndicatorLöhner(equations::AbstractEquations, basis;157f_wave=0.2, variable)158IndicatorLöhner(semi::AbstractSemidiscretization;159f_wave=0.2, variable)160161AMR indicator adapted from a FEM indicator by Löhner (1987), also used in the162FLASH code as standard AMR indicator.163The indicator estimates a weighted second derivative of a specified variable locally.164165When constructed to be used for AMR, pass the `semi`. Pass the `equations`,166and `basis` if this indicator should be used for shock capturing.167168## References169170- Löhner (1987)171"An adaptive finite element scheme for transient problems in CFD"172[doi: 10.1016/0045-7825(87)90098-3](https://doi.org/10.1016/0045-7825(87)90098-3)173- [https://flash.rochester.edu/site/flashcode/user_support/flash4_ug_4p62/node59.html#SECTION05163100000000000000](https://flash.rochester.edu/site/flashcode/user_support/flash4_ug_4p62/node59.html#SECTION05163100000000000000)174"""175struct IndicatorLöhner{RealT <: Real, Variable, Cache} <: AbstractIndicator176f_wave::RealT # TODO: Taal documentation177variable::Variable178cache::Cache179end180181# this method is used when the indicator is constructed as for shock-capturing volume integrals182function IndicatorLöhner(equations::AbstractEquations, basis;183f_wave = 0.2, variable)184cache = create_cache(IndicatorLöhner, equations, basis)185return IndicatorLöhner{typeof(f_wave), typeof(variable), typeof(cache)}(f_wave,186variable,187cache)188end189190# this method is used when the indicator is constructed as for AMR191function IndicatorLöhner(semi::AbstractSemidiscretization;192f_wave = 0.2, variable)193cache = create_cache(IndicatorLöhner, semi)194return IndicatorLöhner{typeof(f_wave), typeof(variable), typeof(cache)}(f_wave,195variable,196cache)197end198199function Base.show(io::IO, indicator::IndicatorLöhner)200@nospecialize indicator # reduce precompilation time201202print(io, "IndicatorLöhner(")203print(io, "f_wave=", indicator.f_wave, ", variable=", indicator.variable, ")")204return nothing205end206207function Base.show(io::IO, ::MIME"text/plain", indicator::IndicatorLöhner)208@nospecialize indicator # reduce precompilation time209210if get(io, :compact, false)211show(io, indicator)212else213setup = [214"indicator variable" => indicator.variable,215"f_wave" => indicator.f_wave216]217summary_box(io, "IndicatorLöhner", setup)218end219end220221"""222IndicatorLoehner223224Same as [`IndicatorLöhner`](@ref).225"""226const IndicatorLoehner = IndicatorLöhner227228# dirty Löhner estimate, direction by direction, assuming constant nodes229@inline function local_löhner_estimate(um::Real, u0::Real, up::Real,230löhner::IndicatorLöhner)231num = abs(up - 2 * u0 + um)232den = abs(up - u0) + abs(u0 - um) +233löhner.f_wave * (abs(up) + 2 * abs(u0) + abs(um))234return num / den235end236237"""238IndicatorMax(equations::AbstractEquations, basis; variable)239IndicatorMax(semi::AbstractSemidiscretization; variable)240241A simple indicator returning the maximum of `variable` in an element.242When constructed to be used for AMR, pass the `semi`. Pass the `equations`,243and `basis` if this indicator should be used for shock capturing.244245If an AMR indicator depending not only on a solution variable but also the246space and time is desired, consider using [`IndicatorNodalFunction`](@ref)247instead.248"""249struct IndicatorMax{Variable, Cache <: NamedTuple} <: AbstractIndicator250variable::Variable251cache::Cache252end253254# this method is used when the indicator is constructed as for shock-capturing volume integrals255function IndicatorMax(equations::AbstractEquations, basis;256variable)257cache = create_cache(IndicatorMax, equations, basis)258return IndicatorMax{typeof(variable), typeof(cache)}(variable, cache)259end260261# this method is used when the indicator is constructed as for AMR262function IndicatorMax(semi::AbstractSemidiscretization;263variable)264cache = create_cache(IndicatorMax, semi)265return IndicatorMax{typeof(variable), typeof(cache)}(variable, cache)266end267268function Base.show(io::IO, indicator::IndicatorMax)269@nospecialize indicator # reduce precompilation time270271print(io, "IndicatorMax(")272print(io, "variable=", indicator.variable, ")")273return nothing274end275276function Base.show(io::IO, ::MIME"text/plain", indicator::IndicatorMax)277@nospecialize indicator # reduce precompilation time278279if get(io, :compact, false)280show(io, indicator)281else282setup = [283"indicator variable" => indicator.variable284]285summary_box(io, "IndicatorMax", setup)286end287end288289@doc raw"""290IndicatorEntropyChange(; maximum_entropy_increase::Real = 0.0)291292This indicator checks the difference in mathematical [`entropy`](@ref) (``S``) due to the application293of a volume integral (VI) compared to the true/analytical entropy evolution294(without any dissipation inside the element).295In particular, the indicator computes296```math297\Delta S = \dot{S}_\mathrm{VI} - \dot{S}_\text{true} =298\int_{\Omega_m}299\frac{\partial S}{\partial \boldsymbol{u}} \cdot \dot{\boldsymbol u}_\mathrm{VI}300\mathrm{d} \Omega_m301-302\int_{\partial \Omega_m}303\boldsymbol{\psi} \cdot \hat{\boldsymbol{n}}304\mathrm{d} \partial \Omega_m305```306for the currently processed element/cell ``m``.307Here, ``\dot{\boldsymbol u}_\mathrm{VI}`` is the change in the DG right-hand-side due to the volume integral only.308``\dot{S}_\text{true}`` is the true entropy evolution, which can be computed from the309entropy potential ``\boldsymbol{\psi}`` (see also [`entropy_potential`](@ref)).310311This is discussed in more detail in312- Chen, Shu (2017)313"Entropy stable high order discontinuous Galerkin methods with suitable quadrature rules for hyperbolic conservation laws"314[DOI: 10.1016/j.jcp.2017.05.025](https://doi.org/10.1016/j.jcp.2017.05.025)315- Lin, Chan (2024)316"High order entropy stable discontinuous Galerkin spectral element methods through subcell limiting"317[DOI: 10.1016/j.jcp.2023.112677](https://doi.org/10.1016/j.jcp.2023.112677)318319For ``\Delta S < \sigma \leq 0`` with ``\sigma`` being set to `maximum_entropy_increase`,320the e.g. [`VolumeIntegralWeakForm`](@ref) is more entropy-diffusive than the true entropy change321(which could be recovered with the [`VolumeIntegralFluxDifferencing`](@ref) and an322entropy-conservative flux such as [`flux_ranocha`](@ref), for instance).323324If ``\sigma > 0`` is set, i.e., `maximum_entropy_increase > 0`, the indicator allows for325limited entropy increase, thereby allowing to use e.g. the cheaper weak-form volume integral326even in slightly entropy-producing situations to reduce computational cost.327328Supposed to be used in conjunction with [`VolumeIntegralAdaptive`](@ref) which then selects329a volume integral for every cell/element ``m``.330331The logic behind this indicator is similar to the "companion" scheme332approach proposed in Chapter 5 of333334- Carpenter, Fisher, Nielsen, and Frankel (2014)335"Entropy Stable Spectral Collocation Schemes for the Navier-Stokes Equations: Discontinuous Interfaces"336[DOI: 10.1137/130932193](https://doi.org/10.1137/130932193)337338Here, we thus equip e.g. the flux-differencing volume integral with a "companion" weak-form339volume integral.340However, usage of the entropy potential allows for comparison with the true entropy change.341342!!! note343This indicator is **not implemented as an AMR indicator**, i.e., it is **not344possible** to employ this as the `indicator` in [`ControllerThreeLevel`](@ref),345for instance.346"""347struct IndicatorEntropyChange{RealT <: Real} <:348AbstractIndicator349maximum_entropy_increase::RealT350351function IndicatorEntropyChange(; maximum_entropy_increase = 0.0)352return new{typeof(maximum_entropy_increase)}(maximum_entropy_increase)353end354end355356function Base.show(io::IO, indicator::IndicatorEntropyChange)357@nospecialize indicator # reduce precompilation time358359print(io, "IndicatorEntropyChange(")360print(io, "maximum_entropy_increase=", indicator.maximum_entropy_increase, ")")361362return nothing363end364365function Base.show(io::IO, ::MIME"text/plain", indicator::IndicatorEntropyChange)366@nospecialize indicator # reduce precompilation time367368if get(io, :compact, false)369show(io, indicator)370else371setup = [372"maximum_entropy_increase" => indicator.maximum_entropy_increase373]374summary_box(io, "IndicatorEntropyChange", setup)375end376end377378"""379IndicatorEntropyCorrection(equations::AbstractEquations, basis;380scaling=true)381382Indicator used for entropy correction using subcell FV schemes, where the383blending is determined so that the volume integral entropy production is the384same or more than that of an entropy-conservative (EC) scheme.385386This is intended to guide the convex blending of a `volume_integral_default`387(for example, [`VolumeIntegralWeakForm`](@ref)) and `volume_integral_stabilized`388(for example, [`VolumeIntegralPureLGLFiniteVolume`](@ref) with an entropy stable389finite volume flux).390391The parameter `scaling ≥ 1` in [`IndicatorEntropyCorrection`](@ref) scales the DG-FV blending392parameter ``\\alpha``(see the [tutorial on shock-capturing](https://trixi-framework.github.io/TrixiDocumentation/stable/tutorials/shock_capturing/#Shock-capturing-with-flux-differencing))393by a constant, increasing the amount of the subcell FV added in (up to 1, i.e., pure subcell FV).394This can be used to add shock capturing-like behavior. Note though that ``\\alpha`` is computed395here from the entropy defect, **not** using [`IndicatorHennemannGassner`](@ref).396397The use of `IndicatorEntropyCorrection` requires either398`entropy_potential(u, orientation, equations)` for TreeMesh, or399`entropy_potential(u, normal_direction, equations)` for other mesh types400to be defined.401402"""403struct IndicatorEntropyCorrection{Cache, ScalingT} <: AbstractIndicator404cache::Cache405scaling::ScalingT # either Bool or Real406end407408# this method is used when the indicator is constructed as for shock-capturing volume integrals409function IndicatorEntropyCorrection(equations::AbstractEquations,410basis::LobattoLegendreBasis;411scaling = true) # true = 1 in floating point multiplication412cache = create_cache(IndicatorEntropyCorrection, equations, basis)413return IndicatorEntropyCorrection{typeof(cache), typeof(scaling)}(cache, scaling)414end415416# this method is used when the indicator is constructed as for417# shock-capturing volume integrals.418function create_cache(::Type{IndicatorEntropyCorrection},419equations::AbstractEquations{NDIMS, NVARS},420basis::LobattoLegendreBasis) where {NDIMS, NVARS}421uEltype = real(basis)422AT = Array{uEltype, NDIMS + 1}423424# container for elementwise volume integrals425volume_integral_values_threaded = AT[AT(undef, NVARS,426ntuple(_ -> nnodes(basis), NDIMS)...)427for _ in 1:Threads.maxthreadid()]428429# stores the blending coefficients430alpha = Vector{uEltype}()431432return (; alpha, volume_integral_values_threaded)433end434435function Base.show(io::IO, indicator::IndicatorEntropyCorrection)436@nospecialize indicator # reduce precompilation time437print(io, "IndicatorEntropyCorrection")438return nothing439end440441function Base.show(io::IO, ::MIME"text/plain", indicator::IndicatorEntropyCorrection)442@nospecialize indicator # reduce precompilation time443summary_box(io, "IndicatorEntropyCorrection")444return nothing445end446447"""448IndicatorEntropyCorrectionShockCapturingCombined(; indicator_shock_capturing,449indicator_entropy_correction)450451Indicator used for entropy correction using subcell FV schemes, where the blending452is taken to be the maximum between a blending determined by shock capturing453(`indicator_shock_capturing`) and a blending determined so that the volume integral454entropy production is the same or more than that of an EC scheme (`indicator_entropy_correction`).455456This is intended to guide the convex blending of a `volume_integral_default` (for457example, [`VolumeIntegralWeakForm`](@ref)) and `volume_integral_stabilized` (for458example, [`VolumeIntegralPureLGLFiniteVolume`](@ref) with an entropy stable finite459volume flux).460461The use of `IndicatorEntropyCorrectionShockCapturingCombined` requires either462`entropy_potential(u, orientation, equations)` for TreeMesh, or463`entropy_potential(u, normal_direction, equations)` for other mesh types464to be defined.465"""466struct IndicatorEntropyCorrectionShockCapturingCombined{IndicatorEC, IndicatorSC} <:467AbstractIndicator468indicator_entropy_correction::IndicatorEC469indicator_shock_capturing::IndicatorSC470end471472function IndicatorEntropyCorrectionShockCapturingCombined(; indicator_shock_capturing,473indicator_entropy_correction)474return IndicatorEntropyCorrectionShockCapturingCombined(indicator_entropy_correction,475indicator_shock_capturing)476end477478function Base.show(io::IO, indicator::IndicatorEntropyCorrectionShockCapturingCombined)479@nospecialize indicator # reduce precompilation time480print(io, "IndicatorEntropyCorrectionShockCapturingCombined(")481print(io, indicator.indicator_entropy_correction)482print(io, ", ")483print(io, indicator.indicator_shock_capturing |> typeof |> nameof)484print(io, ")")485return nothing486end487488function Base.show(io::IO, ::MIME"text/plain",489indicator::IndicatorEntropyCorrectionShockCapturingCombined)490@nospecialize indicator # reduce precompilation time491492if get(io, :compact, false)493show(io, indicator)494else495setup = [496"indicator EC" => indicator.indicator_entropy_correction,497"indicator SC" => indicator.indicator_shock_capturing |> typeof |> nameof498]499summary_box(io, "IndicatorEntropyCorrectionShockCapturingCombined", setup)500end501return nothing502end503504"""505IndicatorNodalFunction(f)506507Create an AMR indicator from a solution, space and time dependent indicator function `f(u, x, t)`.508The function `f` is evaluated at the nodal points. The maximum of `f` over all nodes in each element is used as indicator for the element.509The function can be solution independent allowing for user-defined mesh refinement/coarsening varying in space and time, similar to the `refinement_patches` keyword for the [`TreeMesh`](@ref).510511If the function `f` depends only on the solution, you can also use the [`IndicatorMax`](@ref) instead.512"""513struct IndicatorNodalFunction{F, Cache} <: AbstractIndicator514indicator_function::F515cache::Cache516end517518# this method is used when the indicator is constructed as for AMR519function IndicatorNodalFunction(indicator_function, semi::AbstractSemidiscretization)520cache = create_cache(IndicatorNodalFunction, semi)521return IndicatorNodalFunction{typeof(indicator_function), typeof(cache)}(indicator_function,522cache)523end524525# this method is directly used when the indicator is constructed as for shock-capturing volume integrals526# and by the dimension-independent method called for AMR527function create_cache(::Type{IndicatorNodalFunction},528equations::AbstractEquations, basis::LobattoLegendreBasis)529uEltype = real(basis)530alpha = Vector{uEltype}()531532return (; alpha)533end534535function Base.show(io::IO, indicator::IndicatorNodalFunction)536@nospecialize indicator # reduce precompilation time537print(io, "IndicatorNodalFunction")538print(io, indicator.indicator_function)539return nothing540end541542function Base.show(io::IO, ::MIME"text/plain", indicator::IndicatorNodalFunction)543@nospecialize indicator # reduce precompilation time544if get(io, :compact, false)545show(io, indicator)546else547setup = [548"Indicator Function" => indicator.indicator_function549]550summary_box(io, "IndicatorNodalFunction", setup)551end552end553end # @muladd554555556