Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/solvers/dgsem_tree/indicators_2d.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
# this method is directly used when the indicator is constructed as for shock-capturing volume integrals
9
# and by the dimension-independent method called for AMR
10
function create_cache(::Type{IndicatorHennemannGassner},
11
equations::AbstractEquations{2}, basis::LobattoLegendreBasis)
12
uEltype = real(basis)
13
alpha = Vector{uEltype}()
14
alpha_tmp = similar(alpha)
15
16
MA2d = MArray{Tuple{nnodes(basis), nnodes(basis)},
17
uEltype, 2, nnodes(basis)^ndims(equations)}
18
19
indicator_threaded = MA2d[MA2d(undef) for _ in 1:Threads.maxthreadid()]
20
modal_threaded = MA2d[MA2d(undef) for _ in 1:Threads.maxthreadid()]
21
modal_tmp1_threaded = MA2d[MA2d(undef) for _ in 1:Threads.maxthreadid()]
22
23
return (; alpha, alpha_tmp, indicator_threaded, modal_threaded, modal_tmp1_threaded)
24
end
25
26
# Use this function barrier and unpack inside to avoid passing closures to Polyester.jl
27
# with @batch (@threaded).
28
# Otherwise, @threaded does not work here with Julia ARM on macOS.
29
# See https://github.com/JuliaSIMD/Polyester.jl/issues/88.
30
@inline function calc_indicator_hennemann_gassner!(indicator_hg, threshold, parameter_s,
31
u, element, mesh::AbstractMesh{2},
32
equations, dg, cache)
33
@unpack alpha_max, alpha_min, alpha_smooth, variable = indicator_hg
34
@unpack alpha, alpha_tmp, indicator_threaded, modal_threaded,
35
modal_tmp1_threaded = indicator_hg.cache
36
37
indicator = indicator_threaded[Threads.threadid()]
38
modal = modal_threaded[Threads.threadid()]
39
modal_tmp1 = modal_tmp1_threaded[Threads.threadid()]
40
41
# Calculate indicator variables at Gauss-Lobatto nodes
42
for j in eachnode(dg), i in eachnode(dg)
43
u_local = get_node_vars(u, equations, dg, i, j, element)
44
indicator[i, j] = indicator_hg.variable(u_local, equations)
45
end
46
47
# Convert to modal representation
48
multiply_scalar_dimensionwise!(modal, dg.basis.inverse_vandermonde_legendre,
49
indicator, modal_tmp1)
50
51
# Calculate total energies without two highest, without highest, and for all modes
52
total_energy_clip2 = zero(eltype(modal))
53
for j in 1:(nnodes(dg) - 2), i in 1:(nnodes(dg) - 2)
54
total_energy_clip2 += modal[i, j]^2
55
end
56
57
total_energy_clip1 = copy(total_energy_clip2)
58
for i in 1:(nnodes(dg) - 1)
59
total_energy_clip1 += modal[i, nnodes(dg) - 1]^2
60
end
61
for j in 1:(nnodes(dg) - 2) # stop at N-2 to avoid adding the (N-1, N-1) mode twice
62
total_energy_clip1 += modal[nnodes(dg) - 1, j]^2
63
end
64
65
total_energy = copy(total_energy_clip1)
66
for i in 1:nnodes(dg)
67
total_energy += modal[i, nnodes(dg)]^2
68
end
69
for j in 1:(nnodes(dg) - 1) # stop at N-1 to avoid adding the (N, N) mode twice
70
total_energy += modal[nnodes(dg), j]^2
71
end
72
73
# Calculate energy in higher modes
74
if !(iszero(total_energy))
75
energy_frac_1 = (total_energy - total_energy_clip1) / total_energy
76
else
77
energy_frac_1 = zero(total_energy)
78
end
79
if !(iszero(total_energy_clip1))
80
energy_frac_2 = (total_energy_clip1 - total_energy_clip2) / total_energy_clip1
81
else
82
energy_frac_2 = zero(total_energy_clip1)
83
end
84
energy = max(energy_frac_1, energy_frac_2)
85
86
alpha_element = 1 / (1 + exp(-parameter_s / threshold * (energy - threshold)))
87
88
# Take care of the case close to pure DG
89
if alpha_element < alpha_min
90
alpha_element = zero(alpha_element)
91
end
92
93
# Take care of the case close to pure FV
94
if alpha_element > 1 - alpha_min
95
alpha_element = one(alpha_element)
96
end
97
98
# Clip the maximum amount of FV allowed
99
alpha[element] = min(alpha_max, alpha_element)
100
return nothing
101
end
102
103
# Diffuse alpha values by setting each alpha to at least 50% of neighboring elements' alpha
104
function apply_smoothing!(mesh::Union{TreeMesh{2}, P4estMesh{2}, T8codeMesh{2}}, alpha,
105
alpha_tmp, dg,
106
cache)
107
# Copy alpha values such that smoothing is indpedenent of the element access order
108
alpha_tmp .= alpha
109
110
# Loop over interfaces
111
for interface in eachinterface(dg, cache)
112
# Get neighboring element ids
113
left = cache.interfaces.neighbor_ids[1, interface]
114
right = cache.interfaces.neighbor_ids[2, interface]
115
116
# Apply smoothing
117
alpha[left] = max(alpha_tmp[left], 0.5f0 * alpha_tmp[right], alpha[left])
118
alpha[right] = max(alpha_tmp[right], 0.5f0 * alpha_tmp[left], alpha[right])
119
end
120
121
# Loop over L2 mortars
122
for mortar in eachmortar(dg, cache)
123
# Get neighboring element ids
124
lower = cache.mortars.neighbor_ids[1, mortar]
125
upper = cache.mortars.neighbor_ids[2, mortar]
126
large = cache.mortars.neighbor_ids[3, mortar]
127
128
# Apply smoothing
129
alpha[lower] = max(alpha_tmp[lower], 0.5f0 * alpha_tmp[large], alpha[lower])
130
alpha[upper] = max(alpha_tmp[upper], 0.5f0 * alpha_tmp[large], alpha[upper])
131
alpha[large] = max(alpha_tmp[large], 0.5f0 * alpha_tmp[lower], alpha[large])
132
alpha[large] = max(alpha_tmp[large], 0.5f0 * alpha_tmp[upper], alpha[large])
133
end
134
135
return nothing
136
end
137
138
# this method is directly used when the indicator is constructed as for shock-capturing volume integrals
139
# and by the dimension-independent method called for AMR
140
function create_cache(::Union{Type{IndicatorLöhner}, Type{IndicatorMax}},
141
equations::AbstractEquations{2}, basis::LobattoLegendreBasis)
142
uEltype = real(basis)
143
alpha = Vector{uEltype}()
144
145
MA2d = MArray{Tuple{nnodes(basis), nnodes(basis)},
146
uEltype, 2, nnodes(basis)^ndims(equations)}
147
148
indicator_threaded = MA2d[MA2d(undef) for _ in 1:Threads.maxthreadid()]
149
150
return (; alpha, indicator_threaded)
151
end
152
153
function (löhner::IndicatorLöhner)(u::AbstractArray{<:Any, 4},
154
mesh, equations, dg::DGSEM, cache;
155
kwargs...)
156
@assert nnodes(dg)>=3 "IndicatorLöhner only works for nnodes >= 3 (polydeg > 1)"
157
@unpack alpha, indicator_threaded = löhner.cache
158
@unpack variable = löhner
159
resize!(alpha, nelements(dg, cache))
160
161
@threaded for element in eachelement(dg, cache)
162
indicator = indicator_threaded[Threads.threadid()]
163
164
# Calculate indicator variables at Gauss-Lobatto nodes
165
for j in eachnode(dg), i in eachnode(dg)
166
u_local = get_node_vars(u, equations, dg, i, j, element)
167
indicator[i, j] = variable(u_local, equations)
168
end
169
170
estimate = zero(real(dg))
171
for j in eachnode(dg), i in 2:(nnodes(dg) - 1)
172
# x direction
173
u0 = indicator[i, j]
174
up = indicator[i + 1, j]
175
um = indicator[i - 1, j]
176
estimate = max(estimate, local_löhner_estimate(um, u0, up, löhner))
177
end
178
179
for j in 2:(nnodes(dg) - 1), i in eachnode(dg)
180
# y direction
181
u0 = indicator[i, j]
182
up = indicator[i, j + 1]
183
um = indicator[i, j - 1]
184
estimate = max(estimate, local_löhner_estimate(um, u0, up, löhner))
185
end
186
187
# use the maximum as DG element indicator
188
alpha[element] = estimate
189
end
190
191
return alpha
192
end
193
194
function (indicator_max::IndicatorMax)(u::AbstractArray{<:Any, 4},
195
mesh, equations, dg::DGSEM, cache;
196
kwargs...)
197
@unpack alpha, indicator_threaded = indicator_max.cache
198
resize!(alpha, nelements(dg, cache))
199
indicator_variable = indicator_max.variable
200
201
@threaded for element in eachelement(dg, cache)
202
indicator = indicator_threaded[Threads.threadid()]
203
204
# Calculate indicator variables at Gauss-Lobatto nodes
205
for j in eachnode(dg), i in eachnode(dg)
206
u_local = get_node_vars(u, equations, dg, i, j, element)
207
indicator[i, j] = indicator_variable(u_local, equations)
208
end
209
210
alpha[element] = maximum(indicator)
211
end
212
213
return alpha
214
end
215
216
function (indicator::IndicatorNodalFunction)(u::AbstractArray{<:Any, 4},
217
mesh, equations, dg::DGSEM, cache;
218
t, kwargs...)
219
node_coordinates = cache.elements.node_coordinates
220
@unpack alpha = indicator.cache
221
resize!(alpha, nelements(dg, cache))
222
# Extract function to local variable to avoid capturing `indicator` in the threaded loop
223
indicator_function = indicator.indicator_function
224
225
@threaded for element in eachelement(dg, cache)
226
estimate = typemin(eltype(alpha))
227
for j in eachnode(dg), i in eachnode(dg)
228
u_nodal = get_node_vars(u, equations, dg, i, j, element)
229
x_nodal = get_node_coords(node_coordinates, equations, dg,
230
i, j, element)
231
estimate = max(estimate, indicator_function(u_nodal, x_nodal, t))
232
end
233
alpha[element] = estimate
234
end
235
236
return alpha
237
end
238
end # @muladd
239
240