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