Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/solvers/dgsem/indicators.jl
5591 views
1
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
2
# Since these FMAs can increase the performance of many numerical algorithms,
3
# we need to opt-in explicitly.
4
# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
5
@muladd begin
6
#! format: noindent
7
8
# Abstract supertype of indicators used for AMR, shock capturing, and
9
# adaptive volume-integral selection
10
abstract type AbstractIndicator end
11
12
function create_cache(typ::Type{IndicatorType},
13
semi) where {IndicatorType <: AbstractIndicator}
14
return create_cache_indicator_for_amr(typ, mesh_equations_solver_cache(semi)...)
15
end
16
17
# this method is used when the indicator is constructed as for AMR
18
function create_cache_indicator_for_amr(typ::Type{IndicatorType},
19
mesh, equations::AbstractEquations, dg::DGSEM,
20
cache) where {IndicatorType <:
21
AbstractIndicator}
22
return create_cache(typ, equations, dg.basis)
23
end
24
25
function get_element_variables!(element_variables, indicator::AbstractIndicator,
26
::AbstractVolumeIntegralShockCapturing)
27
element_variables[:indicator_shock_capturing] = indicator.cache.alpha
28
return nothing
29
end
30
31
"""
32
IndicatorHennemannGassner(equations::AbstractEquations, basis;
33
alpha_max=0.5,
34
alpha_min=0.001,
35
alpha_smooth=true,
36
variable)
37
IndicatorHennemannGassner(semi::AbstractSemidiscretization;
38
alpha_max=0.5,
39
alpha_min=0.001,
40
alpha_smooth=true,
41
variable)
42
43
Indicator used for shock-capturing (when passing the `equations` and the `basis`)
44
or adaptive mesh refinement (AMR, when passing the `semi`).
45
46
See also [`VolumeIntegralShockCapturingHG`](@ref).
47
48
## References
49
50
- Hennemann, Gassner (2020)
51
"A provably entropy stable subcell shock capturing approach for high order split form DG"
52
[arXiv: 2008.12044](https://arxiv.org/abs/2008.12044)
53
"""
54
struct IndicatorHennemannGassner{RealT <: Real, Variable, Cache} <: AbstractIndicator
55
alpha_max::RealT
56
alpha_min::RealT
57
alpha_smooth::Bool
58
variable::Variable
59
cache::Cache
60
end
61
62
# this method is used when the indicator is constructed as for shock-capturing volume integrals
63
function IndicatorHennemannGassner(equations::AbstractEquations, basis;
64
alpha_max = 0.5,
65
alpha_min = 0.001,
66
alpha_smooth = true,
67
variable)
68
alpha_max, alpha_min = promote(alpha_max, alpha_min)
69
cache = create_cache(IndicatorHennemannGassner, equations, basis)
70
return IndicatorHennemannGassner{typeof(alpha_max), typeof(variable),
71
typeof(cache)}(alpha_max,
72
alpha_min,
73
alpha_smooth,
74
variable,
75
cache)
76
end
77
78
# this method is used when the indicator is constructed as for AMR
79
function IndicatorHennemannGassner(semi::AbstractSemidiscretization;
80
alpha_max = 0.5,
81
alpha_min = 0.001,
82
alpha_smooth = true,
83
variable)
84
alpha_max, alpha_min = promote(alpha_max, alpha_min)
85
cache = create_cache(IndicatorHennemannGassner, semi)
86
return IndicatorHennemannGassner{typeof(alpha_max), typeof(variable),
87
typeof(cache)}(alpha_max,
88
alpha_min,
89
alpha_smooth,
90
variable,
91
cache)
92
end
93
94
function Base.show(io::IO, indicator::IndicatorHennemannGassner)
95
@nospecialize indicator # reduce precompilation time
96
97
print(io, "IndicatorHennemannGassner(")
98
print(io, indicator.variable)
99
print(io, ", alpha_max=", indicator.alpha_max)
100
print(io, ", alpha_min=", indicator.alpha_min)
101
print(io, ", alpha_smooth=", indicator.alpha_smooth)
102
print(io, ")")
103
return nothing
104
end
105
106
function Base.show(io::IO, ::MIME"text/plain", indicator::IndicatorHennemannGassner)
107
@nospecialize indicator # reduce precompilation time
108
setup = [
109
"indicator variable" => indicator.variable,
110
"max. α" => indicator.alpha_max,
111
"min. α" => indicator.alpha_min,
112
"smooth α" => (indicator.alpha_smooth ? "yes" : "no")
113
]
114
summary_box(io, "IndicatorHennemannGassner", setup)
115
return nothing
116
end
117
118
function (indicator_hg::IndicatorHennemannGassner)(u, mesh, equations, dg::DGSEM, cache;
119
kwargs...)
120
@unpack alpha_smooth = indicator_hg
121
@unpack alpha, alpha_tmp = indicator_hg.cache
122
# TODO: Taal refactor, when to `resize!` stuff changed possibly by AMR?
123
# Shall we implement `resize!(semi::AbstractSemidiscretization, new_size)`
124
# or just `resize!` whenever we call the relevant methods as we do now?
125
resize!(alpha, nelements(dg, cache))
126
if alpha_smooth
127
resize!(alpha_tmp, nelements(dg, cache))
128
end
129
130
# magic parameters
131
# TODO: Are there better values for Float32?
132
RealT = real(dg)
133
threshold = 0.5f0 * 10^(convert(RealT, -1.8) * nnodes(dg)^convert(RealT, 0.25))
134
o_0001 = convert(RealT, 0.0001)
135
parameter_s = log((1 - o_0001) / o_0001)
136
137
@threaded for element in eachelement(dg, cache)
138
# This is dispatched by mesh dimension.
139
# Use this function barrier and unpack inside to avoid passing closures to
140
# Polyester.jl with `@batch` (`@threaded`).
141
# Otherwise, `@threaded` does not work here with Julia ARM on macOS.
142
# See https://github.com/JuliaSIMD/Polyester.jl/issues/88.
143
calc_indicator_hennemann_gassner!(indicator_hg, threshold, parameter_s, u,
144
element, mesh, equations, dg, cache)
145
end
146
147
if alpha_smooth
148
apply_smoothing!(mesh, alpha, alpha_tmp, dg, cache)
149
end
150
151
return alpha
152
end
153
154
"""
155
IndicatorLöhner (equivalent to IndicatorLoehner)
156
157
IndicatorLöhner(equations::AbstractEquations, basis;
158
f_wave=0.2, variable)
159
IndicatorLöhner(semi::AbstractSemidiscretization;
160
f_wave=0.2, variable)
161
162
AMR indicator adapted from a FEM indicator by Löhner (1987), also used in the
163
FLASH code as standard AMR indicator.
164
The indicator estimates a weighted second derivative of a specified variable locally.
165
166
When constructed to be used for AMR, pass the `semi`. Pass the `equations`,
167
and `basis` if this indicator should be used for shock capturing.
168
169
## References
170
171
- Löhner (1987)
172
"An adaptive finite element scheme for transient problems in CFD"
173
[doi: 10.1016/0045-7825(87)90098-3](https://doi.org/10.1016/0045-7825(87)90098-3)
174
- [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)
175
"""
176
struct IndicatorLöhner{RealT <: Real, Variable, Cache} <: AbstractIndicator
177
f_wave::RealT # TODO: Taal documentation
178
variable::Variable
179
cache::Cache
180
end
181
182
# this method is used when the indicator is constructed as for shock-capturing volume integrals
183
function IndicatorLöhner(equations::AbstractEquations, basis;
184
f_wave = 0.2, variable)
185
cache = create_cache(IndicatorLöhner, equations, basis)
186
return IndicatorLöhner{typeof(f_wave), typeof(variable), typeof(cache)}(f_wave,
187
variable,
188
cache)
189
end
190
191
# this method is used when the indicator is constructed as for AMR
192
function IndicatorLöhner(semi::AbstractSemidiscretization;
193
f_wave = 0.2, variable)
194
cache = create_cache(IndicatorLöhner, semi)
195
return IndicatorLöhner{typeof(f_wave), typeof(variable), typeof(cache)}(f_wave,
196
variable,
197
cache)
198
end
199
200
function Base.show(io::IO, indicator::IndicatorLöhner)
201
@nospecialize indicator # reduce precompilation time
202
203
print(io, "IndicatorLöhner(")
204
print(io, "f_wave=", indicator.f_wave, ", variable=", indicator.variable, ")")
205
return nothing
206
end
207
208
function Base.show(io::IO, ::MIME"text/plain", indicator::IndicatorLöhner)
209
@nospecialize indicator # reduce precompilation time
210
211
if get(io, :compact, false)
212
show(io, indicator)
213
else
214
setup = [
215
"indicator variable" => indicator.variable,
216
"f_wave" => indicator.f_wave
217
]
218
summary_box(io, "IndicatorLöhner", setup)
219
end
220
end
221
222
"""
223
IndicatorLoehner
224
225
Same as [`IndicatorLöhner`](@ref).
226
"""
227
const IndicatorLoehner = IndicatorLöhner
228
229
# dirty Löhner estimate, direction by direction, assuming constant nodes
230
@inline function local_löhner_estimate(um::Real, u0::Real, up::Real,
231
löhner::IndicatorLöhner)
232
num = abs(up - 2 * u0 + um)
233
den = abs(up - u0) + abs(u0 - um) +
234
löhner.f_wave * (abs(up) + 2 * abs(u0) + abs(um))
235
return num / den
236
end
237
238
"""
239
IndicatorMax(equations::AbstractEquations, basis; variable)
240
IndicatorMax(semi::AbstractSemidiscretization; variable)
241
242
A simple indicator returning the maximum of `variable` in an element.
243
When constructed to be used for AMR, pass the `semi`. Pass the `equations`,
244
and `basis` if this indicator should be used for shock capturing.
245
246
If an AMR indicator depending not only on a solution variable but also the
247
space and time is desired, consider using [`IndicatorNodalFunction`](@ref)
248
instead.
249
"""
250
struct IndicatorMax{Variable, Cache <: NamedTuple} <: AbstractIndicator
251
variable::Variable
252
cache::Cache
253
end
254
255
# this method is used when the indicator is constructed as for shock-capturing volume integrals
256
function IndicatorMax(equations::AbstractEquations, basis;
257
variable)
258
cache = create_cache(IndicatorMax, equations, basis)
259
return IndicatorMax{typeof(variable), typeof(cache)}(variable, cache)
260
end
261
262
# this method is used when the indicator is constructed as for AMR
263
function IndicatorMax(semi::AbstractSemidiscretization;
264
variable)
265
cache = create_cache(IndicatorMax, semi)
266
return IndicatorMax{typeof(variable), typeof(cache)}(variable, cache)
267
end
268
269
function Base.show(io::IO, indicator::IndicatorMax)
270
@nospecialize indicator # reduce precompilation time
271
272
print(io, "IndicatorMax(")
273
print(io, "variable=", indicator.variable, ")")
274
return nothing
275
end
276
277
function Base.show(io::IO, ::MIME"text/plain", indicator::IndicatorMax)
278
@nospecialize indicator # reduce precompilation time
279
280
if get(io, :compact, false)
281
show(io, indicator)
282
else
283
setup = [
284
"indicator variable" => indicator.variable
285
]
286
summary_box(io, "IndicatorMax", setup)
287
end
288
end
289
290
@doc raw"""
291
IndicatorEntropyChange(; maximum_entropy_increase::Real = 0.0)
292
293
This indicator checks the difference in mathematical [`entropy`](@ref) (``S``) due to the application
294
of a volume integral (VI) compared to the true/analytical entropy evolution
295
(without any dissipation inside the element).
296
In particular, the indicator computes
297
```math
298
\Delta S = \dot{S}_\mathrm{VI} - \dot{S}_\text{true} =
299
\int_{\Omega_m}
300
\frac{\partial S}{\partial \boldsymbol{u}} \cdot \dot{\boldsymbol u}_\mathrm{VI}
301
\mathrm{d} \Omega_m
302
-
303
\int_{\partial \Omega_m}
304
\boldsymbol{\psi} \cdot \hat{\boldsymbol{n}}
305
\mathrm{d} \partial \Omega_m
306
```
307
for the currently processed element/cell ``m``.
308
Here, ``\dot{\boldsymbol u}_\mathrm{VI}`` is the change in the DG right-hand-side due to the volume integral only.
309
``\dot{S}_\text{true}`` is the true entropy evolution, which can be computed from the
310
entropy potential ``\boldsymbol{\psi}`` (see also [`entropy_potential`](@ref)).
311
312
This is discussed in more detail in
313
- Chen, Shu (2017)
314
"Entropy stable high order discontinuous Galerkin methods with suitable quadrature rules for hyperbolic conservation laws"
315
[DOI: 10.1016/j.jcp.2017.05.025](https://doi.org/10.1016/j.jcp.2017.05.025)
316
- Lin, Chan (2024)
317
"High order entropy stable discontinuous Galerkin spectral element methods through subcell limiting"
318
[DOI: 10.1016/j.jcp.2023.112677](https://doi.org/10.1016/j.jcp.2023.112677)
319
320
For ``\Delta S < \sigma \leq 0`` with ``\sigma`` being set to `maximum_entropy_increase`,
321
the e.g. [`VolumeIntegralWeakForm`](@ref) is more entropy-diffusive than the true entropy change
322
(which could be recovered with the [`VolumeIntegralFluxDifferencing`](@ref) and an
323
entropy-conservative flux such as [`flux_ranocha`](@ref), for instance).
324
325
If ``\sigma > 0`` is set, i.e., `maximum_entropy_increase > 0`, the indicator allows for
326
limited entropy increase, thereby allowing to use e.g. the cheaper weak-form volume integral
327
even in slightly entropy-producing situations to reduce computational cost.
328
329
Supposed to be used in conjunction with [`VolumeIntegralAdaptive`](@ref) which then selects
330
a volume integral for every cell/element ``m``.
331
332
The logic behind this indicator is similar to the "companion" scheme
333
approach proposed in Chapter 5 of
334
335
- Carpenter, Fisher, Nielsen, and Frankel (2014)
336
"Entropy Stable Spectral Collocation Schemes for the Navier-Stokes Equations: Discontinuous Interfaces"
337
[DOI: 10.1137/130932193](https://doi.org/10.1137/130932193)
338
339
Here, we thus equip e.g. the flux-differencing volume integral with a "companion" weak-form
340
volume integral.
341
However, usage of the entropy potential allows for comparison with the true entropy change.
342
343
!!! note
344
This indicator is **not implemented as an AMR indicator**, i.e., it is **not
345
possible** to employ this as the `indicator` in [`ControllerThreeLevel`](@ref),
346
for instance.
347
"""
348
struct IndicatorEntropyChange{RealT <: Real} <:
349
AbstractIndicator
350
maximum_entropy_increase::RealT
351
352
function IndicatorEntropyChange(; maximum_entropy_increase = 0.0)
353
return new{typeof(maximum_entropy_increase)}(maximum_entropy_increase)
354
end
355
end
356
357
function Base.show(io::IO, indicator::IndicatorEntropyChange)
358
@nospecialize indicator # reduce precompilation time
359
360
print(io, "IndicatorEntropyChange(")
361
print(io, "maximum_entropy_increase=", indicator.maximum_entropy_increase, ")")
362
363
return nothing
364
end
365
366
function Base.show(io::IO, ::MIME"text/plain", indicator::IndicatorEntropyChange)
367
@nospecialize indicator # reduce precompilation time
368
369
if get(io, :compact, false)
370
show(io, indicator)
371
else
372
setup = [
373
"maximum_entropy_increase" => indicator.maximum_entropy_increase
374
]
375
summary_box(io, "IndicatorEntropyChange", setup)
376
end
377
end
378
379
"""
380
IndicatorEntropyCorrection(equations::AbstractEquations, basis;
381
scaling=true)
382
383
Indicator used for entropy correction using subcell FV schemes, where the
384
blending is determined so that the volume integral entropy production is the
385
same or more than that of an entropy-conservative (EC) scheme.
386
387
This is intended to guide the convex blending of a `volume_integral_default`
388
(for example, [`VolumeIntegralWeakForm`](@ref)) and `volume_integral_stabilized`
389
(for example, [`VolumeIntegralPureLGLFiniteVolume`](@ref) with an entropy stable
390
finite volume flux).
391
392
The parameter `scaling ≥ 1` in [`IndicatorEntropyCorrection`](@ref) scales the DG-FV blending
393
parameter ``\\alpha``(see the [tutorial on shock-capturing](https://trixi-framework.github.io/TrixiDocumentation/stable/tutorials/shock_capturing/#Shock-capturing-with-flux-differencing))
394
by a constant, increasing the amount of the subcell FV added in (up to 1, i.e., pure subcell FV).
395
This can be used to add shock capturing-like behavior. Note though that ``\\alpha`` is computed
396
here from the entropy defect, **not** using [`IndicatorHennemannGassner`](@ref).
397
398
The use of `IndicatorEntropyCorrection` requires either
399
`entropy_potential(u, orientation, equations)` for TreeMesh, or
400
`entropy_potential(u, normal_direction, equations)` for other mesh types
401
to be defined.
402
403
"""
404
struct IndicatorEntropyCorrection{Cache, ScalingT} <: AbstractIndicator
405
cache::Cache
406
scaling::ScalingT # either Bool or Real
407
end
408
409
# this method is used when the indicator is constructed as for shock-capturing volume integrals
410
function IndicatorEntropyCorrection(equations::AbstractEquations,
411
basis::LobattoLegendreBasis;
412
scaling = true) # true = 1 in floating point multiplication
413
cache = create_cache(IndicatorEntropyCorrection, equations, basis)
414
return IndicatorEntropyCorrection{typeof(cache), typeof(scaling)}(cache, scaling)
415
end
416
417
# this method is used when the indicator is constructed as for
418
# shock-capturing volume integrals.
419
function create_cache(::Type{IndicatorEntropyCorrection},
420
equations::AbstractEquations{NDIMS, NVARS},
421
basis::LobattoLegendreBasis) where {NDIMS, NVARS}
422
uEltype = real(basis)
423
AT = Array{uEltype, NDIMS + 1}
424
425
# container for elementwise volume integrals
426
volume_integral_values_threaded = AT[AT(undef, NVARS,
427
ntuple(_ -> nnodes(basis), NDIMS)...)
428
for _ in 1:Threads.maxthreadid()]
429
430
# stores the blending coefficients
431
alpha = Vector{uEltype}()
432
433
return (; alpha, volume_integral_values_threaded)
434
end
435
436
function Base.show(io::IO, indicator::IndicatorEntropyCorrection)
437
@nospecialize indicator # reduce precompilation time
438
print(io, "IndicatorEntropyCorrection")
439
return nothing
440
end
441
442
function Base.show(io::IO, ::MIME"text/plain", indicator::IndicatorEntropyCorrection)
443
@nospecialize indicator # reduce precompilation time
444
summary_box(io, "IndicatorEntropyCorrection")
445
return nothing
446
end
447
448
"""
449
IndicatorEntropyCorrectionShockCapturingCombined(; indicator_shock_capturing,
450
indicator_entropy_correction)
451
452
Indicator used for entropy correction using subcell FV schemes, where the blending
453
is taken to be the maximum between a blending determined by shock capturing
454
(`indicator_shock_capturing`) and a blending determined so that the volume integral
455
entropy production is the same or more than that of an EC scheme (`indicator_entropy_correction`).
456
457
This is intended to guide the convex blending of a `volume_integral_default` (for
458
example, [`VolumeIntegralWeakForm`](@ref)) and `volume_integral_stabilized` (for
459
example, [`VolumeIntegralPureLGLFiniteVolume`](@ref) with an entropy stable finite
460
volume flux).
461
462
The use of `IndicatorEntropyCorrectionShockCapturingCombined` requires either
463
`entropy_potential(u, orientation, equations)` for TreeMesh, or
464
`entropy_potential(u, normal_direction, equations)` for other mesh types
465
to be defined.
466
"""
467
struct IndicatorEntropyCorrectionShockCapturingCombined{IndicatorEC, IndicatorSC} <:
468
AbstractIndicator
469
indicator_entropy_correction::IndicatorEC
470
indicator_shock_capturing::IndicatorSC
471
end
472
473
function IndicatorEntropyCorrectionShockCapturingCombined(; indicator_shock_capturing,
474
indicator_entropy_correction)
475
return IndicatorEntropyCorrectionShockCapturingCombined(indicator_entropy_correction,
476
indicator_shock_capturing)
477
end
478
479
function Base.show(io::IO, indicator::IndicatorEntropyCorrectionShockCapturingCombined)
480
@nospecialize indicator # reduce precompilation time
481
print(io, "IndicatorEntropyCorrectionShockCapturingCombined(")
482
print(io, indicator.indicator_entropy_correction)
483
print(io, ", ")
484
print(io, indicator.indicator_shock_capturing |> typeof |> nameof)
485
print(io, ")")
486
return nothing
487
end
488
489
function Base.show(io::IO, ::MIME"text/plain",
490
indicator::IndicatorEntropyCorrectionShockCapturingCombined)
491
@nospecialize indicator # reduce precompilation time
492
493
if get(io, :compact, false)
494
show(io, indicator)
495
else
496
setup = [
497
"indicator EC" => indicator.indicator_entropy_correction,
498
"indicator SC" => indicator.indicator_shock_capturing |> typeof |> nameof
499
]
500
summary_box(io, "IndicatorEntropyCorrectionShockCapturingCombined", setup)
501
end
502
return nothing
503
end
504
505
"""
506
IndicatorNodalFunction(f)
507
508
Create an AMR indicator from a solution, space and time dependent indicator function `f(u, x, t)`.
509
The 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.
510
The 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).
511
512
If the function `f` depends only on the solution, you can also use the [`IndicatorMax`](@ref) instead.
513
"""
514
struct IndicatorNodalFunction{F, Cache} <: AbstractIndicator
515
indicator_function::F
516
cache::Cache
517
end
518
519
# this method is used when the indicator is constructed as for AMR
520
function IndicatorNodalFunction(indicator_function, semi::AbstractSemidiscretization)
521
cache = create_cache(IndicatorNodalFunction, semi)
522
return IndicatorNodalFunction{typeof(indicator_function), typeof(cache)}(indicator_function,
523
cache)
524
end
525
526
# this method is directly used when the indicator is constructed as for shock-capturing volume integrals
527
# and by the dimension-independent method called for AMR
528
function create_cache(::Type{IndicatorNodalFunction},
529
equations::AbstractEquations, basis::LobattoLegendreBasis)
530
uEltype = real(basis)
531
alpha = Vector{uEltype}()
532
533
return (; alpha)
534
end
535
536
function Base.show(io::IO, indicator::IndicatorNodalFunction)
537
@nospecialize indicator # reduce precompilation time
538
print(io, "IndicatorNodalFunction")
539
print(io, indicator.indicator_function)
540
return nothing
541
end
542
543
function Base.show(io::IO, ::MIME"text/plain", indicator::IndicatorNodalFunction)
544
@nospecialize indicator # reduce precompilation time
545
if get(io, :compact, false)
546
show(io, indicator)
547
else
548
setup = [
549
"Indicator Function" => indicator.indicator_function
550
]
551
summary_box(io, "IndicatorNodalFunction", setup)
552
end
553
end
554
end # @muladd
555
556