Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/solvers/dgsem_tree/dg_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
# everything related to a DG semidiscretization in 3D,
9
# currently limited to Lobatto-Legendre nodes
10
11
function create_f_threaded(mesh::AbstractMesh{3}, equations,
12
dg::DG, uEltype)
13
A4d = Array{uEltype, 4}
14
15
f1_L_threaded = A4d[A4d(undef, nvariables(equations),
16
nnodes(dg) + 1, nnodes(dg), nnodes(dg))
17
for _ in 1:Threads.maxthreadid()]
18
f1_R_threaded = A4d[A4d(undef, nvariables(equations),
19
nnodes(dg) + 1, nnodes(dg), nnodes(dg))
20
for _ in 1:Threads.maxthreadid()]
21
f2_L_threaded = A4d[A4d(undef, nvariables(equations),
22
nnodes(dg), nnodes(dg) + 1, nnodes(dg))
23
for _ in 1:Threads.maxthreadid()]
24
f2_R_threaded = A4d[A4d(undef, nvariables(equations),
25
nnodes(dg), nnodes(dg) + 1, nnodes(dg))
26
for _ in 1:Threads.maxthreadid()]
27
f3_L_threaded = A4d[A4d(undef, nvariables(equations),
28
nnodes(dg), nnodes(dg), nnodes(dg) + 1)
29
for _ in 1:Threads.maxthreadid()]
30
f3_R_threaded = A4d[A4d(undef, nvariables(equations),
31
nnodes(dg), nnodes(dg), nnodes(dg) + 1)
32
for _ in 1:Threads.maxthreadid()]
33
34
@threaded for t in eachindex(f1_L_threaded)
35
f1_L_threaded[t][:, 1, :, :] .= zero(uEltype)
36
f1_R_threaded[t][:, 1, :, :] .= zero(uEltype)
37
f1_L_threaded[t][:, nnodes(dg) + 1, :, :] .= zero(uEltype)
38
f1_R_threaded[t][:, nnodes(dg) + 1, :, :] .= zero(uEltype)
39
40
f2_L_threaded[t][:, :, 1, :] .= zero(uEltype)
41
f2_R_threaded[t][:, :, 1, :] .= zero(uEltype)
42
f2_L_threaded[t][:, :, nnodes(dg) + 1, :] .= zero(uEltype)
43
f2_R_threaded[t][:, :, nnodes(dg) + 1, :] .= zero(uEltype)
44
45
f3_L_threaded[t][:, :, :, 1] .= zero(uEltype)
46
f3_R_threaded[t][:, :, :, 1] .= zero(uEltype)
47
f3_L_threaded[t][:, :, :, nnodes(dg) + 1] .= zero(uEltype)
48
f3_R_threaded[t][:, :, :, nnodes(dg) + 1] .= zero(uEltype)
49
end
50
51
return f1_L_threaded, f1_R_threaded,
52
f2_L_threaded, f2_R_threaded,
53
f3_L_threaded, f3_R_threaded
54
end
55
56
function create_cache(mesh::TreeMesh{3},
57
equations,
58
volume_integral::AbstractVolumeIntegralSubcell,
59
dg::DG, cache_containers, uEltype)
60
fstar1_L_threaded, fstar1_R_threaded,
61
fstar2_L_threaded, fstar2_R_threaded,
62
fstar3_L_threaded, fstar3_R_threaded = create_f_threaded(mesh, equations, dg,
63
uEltype)
64
65
cache_subcell_limiting = create_cache_subcell_limiting(mesh, equations,
66
volume_integral, dg,
67
cache_containers, uEltype)
68
69
return (; fstar1_L_threaded, fstar1_R_threaded,
70
fstar2_L_threaded, fstar2_R_threaded,
71
fstar3_L_threaded, fstar3_R_threaded,
72
cache_subcell_limiting...)
73
end
74
75
# The methods below are specialized on the mortar type
76
# and called from the basic `create_cache` method at the top.
77
function create_cache(mesh::TreeMesh{3}, equations,
78
mortar_l2::LobattoLegendreMortarL2, uEltype)
79
A3d = Array{uEltype, 3}
80
fstar_primary_upper_left_threaded = A3d[A3d(undef, nvariables(equations),
81
nnodes(mortar_l2),
82
nnodes(mortar_l2))
83
for _ in 1:Threads.maxthreadid()]
84
fstar_primary_upper_right_threaded = A3d[A3d(undef, nvariables(equations),
85
nnodes(mortar_l2),
86
nnodes(mortar_l2))
87
for _ in 1:Threads.maxthreadid()]
88
fstar_primary_lower_left_threaded = A3d[A3d(undef, nvariables(equations),
89
nnodes(mortar_l2),
90
nnodes(mortar_l2))
91
for _ in 1:Threads.maxthreadid()]
92
fstar_primary_lower_right_threaded = A3d[A3d(undef, nvariables(equations),
93
nnodes(mortar_l2),
94
nnodes(mortar_l2))
95
for _ in 1:Threads.maxthreadid()]
96
fstar_secondary_upper_left_threaded = A3d[A3d(undef, nvariables(equations),
97
nnodes(mortar_l2),
98
nnodes(mortar_l2))
99
for _ in 1:Threads.maxthreadid()]
100
fstar_secondary_upper_right_threaded = A3d[A3d(undef, nvariables(equations),
101
nnodes(mortar_l2),
102
nnodes(mortar_l2))
103
for _ in 1:Threads.maxthreadid()]
104
fstar_secondary_lower_left_threaded = A3d[A3d(undef, nvariables(equations),
105
nnodes(mortar_l2),
106
nnodes(mortar_l2))
107
for _ in 1:Threads.maxthreadid()]
108
fstar_secondary_lower_right_threaded = A3d[A3d(undef, nvariables(equations),
109
nnodes(mortar_l2),
110
nnodes(mortar_l2))
111
for _ in 1:Threads.maxthreadid()]
112
fstar_tmp1_threaded = A3d[A3d(undef, nvariables(equations),
113
nnodes(mortar_l2),
114
nnodes(mortar_l2))
115
for _ in 1:Threads.maxthreadid()]
116
117
cache = (; fstar_primary_upper_left_threaded, fstar_primary_upper_right_threaded,
118
fstar_primary_lower_left_threaded, fstar_primary_lower_right_threaded,
119
fstar_secondary_upper_left_threaded, fstar_secondary_upper_right_threaded,
120
fstar_secondary_lower_left_threaded, fstar_secondary_lower_right_threaded,
121
fstar_tmp1_threaded)
122
123
return cache
124
end
125
126
#=
127
`weak_form_kernel!` is only implemented for conserved terms as
128
non-conservative terms should always be discretized in conjunction with a flux-splitting scheme,
129
see `flux_differencing_kernel!`.
130
This treatment is required to achieve, e.g., entropy-stability or well-balancedness.
131
See also https://github.com/trixi-framework/Trixi.jl/issues/1671#issuecomment-1765644064
132
=#
133
@inline function weak_form_kernel!(du, u,
134
element, ::Type{<:TreeMesh{3}},
135
have_nonconservative_terms::False, equations,
136
dg::DGSEM, cache, alpha = true)
137
# true * [some floating point value] == [exactly the same floating point value]
138
# This can (hopefully) be optimized away due to constant propagation.
139
@unpack derivative_hat = dg.basis
140
141
for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
142
u_node = get_node_vars(u, equations, dg, i, j, k, element)
143
144
flux1 = flux(u_node, 1, equations)
145
for ii in eachnode(dg)
146
multiply_add_to_node_vars!(du, alpha * derivative_hat[ii, i], flux1,
147
equations, dg, ii, j, k, element)
148
end
149
150
flux2 = flux(u_node, 2, equations)
151
for jj in eachnode(dg)
152
multiply_add_to_node_vars!(du, alpha * derivative_hat[jj, j], flux2,
153
equations, dg, i, jj, k, element)
154
end
155
156
flux3 = flux(u_node, 3, equations)
157
for kk in eachnode(dg)
158
multiply_add_to_node_vars!(du, alpha * derivative_hat[kk, k], flux3,
159
equations, dg, i, j, kk, element)
160
end
161
end
162
163
return nothing
164
end
165
166
@inline function flux_differencing_kernel!(du, u, element, ::Type{<:TreeMesh{3}},
167
have_nonconservative_terms::False, equations,
168
volume_flux, dg::DGSEM, cache, alpha = true)
169
# true * [some floating point value] == [exactly the same floating point value]
170
# This can (hopefully) be optimized away due to constant propagation.
171
@unpack derivative_split = dg.basis
172
173
# Calculate volume integral in one element
174
for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
175
u_node = get_node_vars(u, equations, dg, i, j, k, element)
176
177
# All diagonal entries of `derivative_split` are zero. Thus, we can skip
178
# the computation of the diagonal terms. In addition, we use the symmetry
179
# of the `volume_flux` to save half of the possible two-point flux
180
# computations.
181
182
# x direction
183
for ii in (i + 1):nnodes(dg)
184
u_node_ii = get_node_vars(u, equations, dg, ii, j, k, element)
185
flux1 = volume_flux(u_node, u_node_ii, 1, equations)
186
multiply_add_to_node_vars!(du, alpha * derivative_split[i, ii], flux1,
187
equations, dg, i, j, k, element)
188
multiply_add_to_node_vars!(du, alpha * derivative_split[ii, i], flux1,
189
equations, dg, ii, j, k, element)
190
end
191
192
# y direction
193
for jj in (j + 1):nnodes(dg)
194
u_node_jj = get_node_vars(u, equations, dg, i, jj, k, element)
195
flux2 = volume_flux(u_node, u_node_jj, 2, equations)
196
multiply_add_to_node_vars!(du, alpha * derivative_split[j, jj], flux2,
197
equations, dg, i, j, k, element)
198
multiply_add_to_node_vars!(du, alpha * derivative_split[jj, j], flux2,
199
equations, dg, i, jj, k, element)
200
end
201
202
# z direction
203
for kk in (k + 1):nnodes(dg)
204
u_node_kk = get_node_vars(u, equations, dg, i, j, kk, element)
205
flux3 = volume_flux(u_node, u_node_kk, 3, equations)
206
multiply_add_to_node_vars!(du, alpha * derivative_split[k, kk], flux3,
207
equations, dg, i, j, k, element)
208
multiply_add_to_node_vars!(du, alpha * derivative_split[kk, k], flux3,
209
equations, dg, i, j, kk, element)
210
end
211
end
212
213
return nothing
214
end
215
216
@inline function flux_differencing_kernel!(du, u, element, MeshT::Type{<:TreeMesh{3}},
217
have_nonconservative_terms::True, equations,
218
volume_flux, dg::DGSEM, cache, alpha = true)
219
# true * [some floating point value] == [exactly the same floating point value]
220
# This can (hopefully) be optimized away due to constant propagation.
221
@unpack derivative_split = dg.basis
222
symmetric_flux, nonconservative_flux = volume_flux
223
224
# Apply the symmetric flux as usual
225
flux_differencing_kernel!(du, u, element, MeshT, False(), equations, symmetric_flux,
226
dg, cache, alpha)
227
228
# Calculate the remaining volume terms using the nonsymmetric generalized flux
229
for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
230
u_node = get_node_vars(u, equations, dg, i, j, k, element)
231
232
# The diagonal terms are zero since the diagonal of `derivative_split`
233
# is zero. We ignore this for now.
234
235
# x direction
236
integral_contribution = zero(u_node)
237
for ii in eachnode(dg)
238
u_node_ii = get_node_vars(u, equations, dg, ii, j, k, element)
239
noncons_flux1 = nonconservative_flux(u_node, u_node_ii, 1, equations)
240
integral_contribution = integral_contribution +
241
derivative_split[i, ii] * noncons_flux1
242
end
243
244
# y direction
245
for jj in eachnode(dg)
246
u_node_jj = get_node_vars(u, equations, dg, i, jj, k, element)
247
noncons_flux2 = nonconservative_flux(u_node, u_node_jj, 2, equations)
248
integral_contribution = integral_contribution +
249
derivative_split[j, jj] * noncons_flux2
250
end
251
252
# z direction
253
for kk in eachnode(dg)
254
u_node_kk = get_node_vars(u, equations, dg, i, j, kk, element)
255
noncons_flux3 = nonconservative_flux(u_node, u_node_kk, 3, equations)
256
integral_contribution = integral_contribution +
257
derivative_split[k, kk] * noncons_flux3
258
end
259
260
# The factor 0.5 cancels the factor 2 in the flux differencing form
261
multiply_add_to_node_vars!(du, alpha * 0.5f0, integral_contribution, equations,
262
dg, i, j, k, element)
263
end
264
265
return nothing
266
end
267
268
@inline function fv_kernel!(du, u,
269
MeshT::Type{<:Union{TreeMesh{3}, StructuredMesh{3},
270
P4estMesh{3},
271
T8codeMesh{3}}},
272
have_nonconservative_terms, equations,
273
volume_flux_fv, dg::DGSEM, cache, element, alpha = true)
274
@unpack fstar1_L_threaded, fstar1_R_threaded, fstar2_L_threaded, fstar2_R_threaded, fstar3_L_threaded, fstar3_R_threaded = cache
275
@unpack inverse_weights = dg.basis # Plays role of inverse DG-subcell sizes
276
277
# Calculate FV two-point fluxes
278
fstar1_L = fstar1_L_threaded[Threads.threadid()]
279
fstar2_L = fstar2_L_threaded[Threads.threadid()]
280
fstar3_L = fstar3_L_threaded[Threads.threadid()]
281
fstar1_R = fstar1_R_threaded[Threads.threadid()]
282
fstar2_R = fstar2_R_threaded[Threads.threadid()]
283
fstar3_R = fstar3_R_threaded[Threads.threadid()]
284
285
calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, fstar3_L, fstar3_R, u,
286
MeshT, have_nonconservative_terms, equations,
287
volume_flux_fv, dg, element, cache)
288
289
# Calculate FV volume integral contribution
290
for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
291
for v in eachvariable(equations)
292
du[v, i, j, k, element] += (alpha *
293
(inverse_weights[i] *
294
(fstar1_L[v, i + 1, j, k] -
295
fstar1_R[v, i, j, k]) +
296
inverse_weights[j] *
297
(fstar2_L[v, i, j + 1, k] -
298
fstar2_R[v, i, j, k]) +
299
inverse_weights[k] *
300
(fstar3_L[v, i, j, k + 1] -
301
fstar3_R[v, i, j, k])))
302
end
303
end
304
305
return nothing
306
end
307
308
@inline function fvO2_kernel!(du, u,
309
MeshT::Type{<:Union{TreeMesh{3}, StructuredMesh{3},
310
P4estMesh{3},
311
T8codeMesh{3}}},
312
have_nonconservative_terms, equations,
313
volume_flux_fv, dg::DGSEM, cache, element,
314
sc_interface_coords, reconstruction_mode, slope_limiter,
315
cons2recon, recon2cons,
316
alpha = true)
317
@unpack fstar1_L_threaded, fstar1_R_threaded,
318
fstar2_L_threaded, fstar2_R_threaded,
319
fstar3_L_threaded, fstar3_R_threaded = cache
320
@unpack inverse_weights = dg.basis # Plays role of inverse DG-subcell sizes
321
322
# Calculate FV two-point fluxes
323
fstar1_L = fstar1_L_threaded[Threads.threadid()]
324
fstar2_L = fstar2_L_threaded[Threads.threadid()]
325
fstar3_L = fstar3_L_threaded[Threads.threadid()]
326
fstar1_R = fstar1_R_threaded[Threads.threadid()]
327
fstar2_R = fstar2_R_threaded[Threads.threadid()]
328
fstar3_R = fstar3_R_threaded[Threads.threadid()]
329
330
calcflux_fvO2!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, fstar3_L, fstar3_R, u,
331
MeshT, have_nonconservative_terms, equations,
332
volume_flux_fv, dg, element, cache,
333
sc_interface_coords, reconstruction_mode, slope_limiter,
334
cons2recon, recon2cons)
335
336
# Calculate FV volume integral contribution
337
for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
338
for v in eachvariable(equations)
339
du[v, i, j, k, element] += (alpha *
340
(inverse_weights[i] *
341
(fstar1_L[v, i + 1, j, k] -
342
fstar1_R[v, i, j, k]) +
343
inverse_weights[j] *
344
(fstar2_L[v, i, j + 1, k] -
345
fstar2_R[v, i, j, k]) +
346
inverse_weights[k] *
347
(fstar3_L[v, i, j, k + 1] -
348
fstar3_R[v, i, j, k])))
349
end
350
end
351
352
return nothing
353
end
354
355
# Compute the normal flux for the FV method on cartesian subcells, see
356
# Hennemann, Rueda-Ramírez, Hindenlang, Gassner (2020)
357
# "A provably entropy stable subcell shock capturing approach for high order split form DG for the compressible Euler equations"
358
# [arXiv: 2008.12044v2](https://arxiv.org/pdf/2008.12044)
359
@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R,
360
fstar3_L, fstar3_R, u,
361
::Type{<:TreeMesh{3}}, have_nonconservative_terms::False,
362
equations,
363
volume_flux_fv, dg::DGSEM, element, cache)
364
for k in eachnode(dg), j in eachnode(dg), i in 2:nnodes(dg)
365
u_ll = get_node_vars(u, equations, dg, i - 1, j, k, element)
366
u_rr = get_node_vars(u, equations, dg, i, j, k, element)
367
flux = volume_flux_fv(u_ll, u_rr, 1, equations) # orientation 1: x direction
368
set_node_vars!(fstar1_L, flux, equations, dg, i, j, k)
369
set_node_vars!(fstar1_R, flux, equations, dg, i, j, k)
370
end
371
372
for k in eachnode(dg), j in 2:nnodes(dg), i in eachnode(dg)
373
u_ll = get_node_vars(u, equations, dg, i, j - 1, k, element)
374
u_rr = get_node_vars(u, equations, dg, i, j, k, element)
375
flux = volume_flux_fv(u_ll, u_rr, 2, equations) # orientation 2: y direction
376
set_node_vars!(fstar2_L, flux, equations, dg, i, j, k)
377
set_node_vars!(fstar2_R, flux, equations, dg, i, j, k)
378
end
379
380
for k in 2:nnodes(dg), j in eachnode(dg), i in eachnode(dg)
381
u_ll = get_node_vars(u, equations, dg, i, j, k - 1, element)
382
u_rr = get_node_vars(u, equations, dg, i, j, k, element)
383
flux = volume_flux_fv(u_ll, u_rr, 3, equations) # orientation 3: z direction
384
set_node_vars!(fstar3_L, flux, equations, dg, i, j, k)
385
set_node_vars!(fstar3_R, flux, equations, dg, i, j, k)
386
end
387
388
return nothing
389
end
390
391
@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R,
392
fstar3_L, fstar3_R, u,
393
::Type{<:TreeMesh{3}},
394
have_nonconservative_terms::True, equations,
395
volume_flux_fv, dg::DGSEM, element, cache)
396
volume_flux, nonconservative_flux = volume_flux_fv
397
398
for k in eachnode(dg), j in eachnode(dg), i in 2:nnodes(dg)
399
u_ll = get_node_vars(u, equations, dg, i - 1, j, k, element)
400
u_rr = get_node_vars(u, equations, dg, i, j, k, element)
401
402
# Compute conservative part
403
flux = volume_flux(u_ll, u_rr, 1, equations) # orientation 1: x direction
404
405
# Compute nonconservative part
406
# Note the factor 0.5 necessary for the nonconservative fluxes based on
407
# the interpretation of global SBP operators coupled discontinuously via
408
# central fluxes/SATs
409
flux_L = flux + 0.5f0 * nonconservative_flux(u_ll, u_rr, 1, equations)
410
flux_R = flux + 0.5f0 * nonconservative_flux(u_rr, u_ll, 1, equations)
411
412
set_node_vars!(fstar1_L, flux_L, equations, dg, i, j, k)
413
set_node_vars!(fstar1_R, flux_R, equations, dg, i, j, k)
414
end
415
416
for k in eachnode(dg), j in 2:nnodes(dg), i in eachnode(dg)
417
u_ll = get_node_vars(u, equations, dg, i, j - 1, k, element)
418
u_rr = get_node_vars(u, equations, dg, i, j, k, element)
419
420
# Compute conservative part
421
flux = volume_flux(u_ll, u_rr, 2, equations) # orientation 2: y direction
422
423
# Compute nonconservative part
424
# Note the factor 0.5 necessary for the nonconservative fluxes based on
425
# the interpretation of global SBP operators coupled discontinuously via
426
# central fluxes/SATs
427
flux_L = flux + 0.5f0 * nonconservative_flux(u_ll, u_rr, 2, equations)
428
flux_R = flux + 0.5f0 * nonconservative_flux(u_rr, u_ll, 2, equations)
429
430
set_node_vars!(fstar2_L, flux_L, equations, dg, i, j, k)
431
set_node_vars!(fstar2_R, flux_R, equations, dg, i, j, k)
432
end
433
434
for k in 2:nnodes(dg), j in eachnode(dg), i in eachnode(dg)
435
u_ll = get_node_vars(u, equations, dg, i, j, k - 1, element)
436
u_rr = get_node_vars(u, equations, dg, i, j, k, element)
437
438
# Compute conservative part
439
flux = volume_flux(u_ll, u_rr, 3, equations) # orientation 3: z direction
440
441
# Compute nonconservative part
442
# Note the factor 0.5 necessary for the nonconservative fluxes based on
443
# the interpretation of global SBP operators coupled discontinuously via
444
# central fluxes/SATs
445
flux_L = flux + 0.5f0 * nonconservative_flux(u_ll, u_rr, 3, equations)
446
flux_R = flux + 0.5f0 * nonconservative_flux(u_rr, u_ll, 3, equations)
447
448
set_node_vars!(fstar3_L, flux_L, equations, dg, i, j, k)
449
set_node_vars!(fstar3_R, flux_R, equations, dg, i, j, k)
450
end
451
452
return nothing
453
end
454
455
@inline function calcflux_fvO2!(fstar1_L, fstar1_R, fstar2_L, fstar2_R,
456
fstar3_L, fstar3_R, u,
457
::Type{<:TreeMesh{3}},
458
have_nonconservative_terms::False,
459
equations,
460
volume_flux_fv, dg::DGSEM, element, cache,
461
sc_interface_coords, reconstruction_mode, slope_limiter,
462
cons2recon, recon2cons)
463
for k in eachnode(dg), j in eachnode(dg), i in 2:nnodes(dg)
464
u_ll = cons2recon(get_node_vars(u, equations, dg, max(1, i - 2), j, k, element),
465
equations)
466
u_lr = cons2recon(get_node_vars(u, equations, dg, i - 1, j, k, element),
467
equations)
468
u_rl = cons2recon(get_node_vars(u, equations, dg, i, j, k, element),
469
equations)
470
471
u_rr = cons2recon(get_node_vars(u, equations, dg, min(nnodes(dg), i + 1), j, k,
472
element), equations)
473
474
u_l, u_r = reconstruction_mode(u_ll, u_lr, u_rl, u_rr,
475
sc_interface_coords, i,
476
slope_limiter, dg)
477
478
flux = volume_flux_fv(recon2cons(u_l, equations), recon2cons(u_r, equations),
479
1, equations) # orientation 1: x direction
480
481
set_node_vars!(fstar1_L, flux, equations, dg, i, j, k)
482
set_node_vars!(fstar1_R, flux, equations, dg, i, j, k)
483
end
484
485
for k in eachnode(dg), j in 2:nnodes(dg), i in eachnode(dg)
486
u_ll = cons2recon(get_node_vars(u, equations, dg, i, max(1, j - 2), k, element),
487
equations)
488
u_lr = cons2recon(get_node_vars(u, equations, dg, i, j - 1, k, element),
489
equations)
490
u_rl = cons2recon(get_node_vars(u, equations, dg, i, j, k, element),
491
equations)
492
u_rr = cons2recon(get_node_vars(u, equations, dg, i, min(nnodes(dg), j + 1), k,
493
element), equations)
494
495
u_l, u_r = reconstruction_mode(u_ll, u_lr, u_rl, u_rr,
496
sc_interface_coords, j,
497
slope_limiter, dg)
498
499
flux = volume_flux_fv(recon2cons(u_l, equations), recon2cons(u_r, equations),
500
2, equations) # orientation 2: y direction
501
502
set_node_vars!(fstar2_L, flux, equations, dg, i, j, k)
503
set_node_vars!(fstar2_R, flux, equations, dg, i, j, k)
504
end
505
506
for k in 2:nnodes(dg), j in eachnode(dg), i in eachnode(dg)
507
u_ll = cons2recon(get_node_vars(u, equations, dg, i, j, max(1, k - 2), element),
508
equations)
509
u_lr = cons2recon(get_node_vars(u, equations, dg, i, j, k - 1, element),
510
equations)
511
u_rl = cons2recon(get_node_vars(u, equations, dg, i, j, k, element),
512
equations)
513
u_rr = cons2recon(get_node_vars(u, equations, dg, i, j, min(nnodes(dg), k + 1),
514
element), equations)
515
516
u_l, u_r = reconstruction_mode(u_ll, u_lr, u_rl, u_rr,
517
sc_interface_coords, k,
518
slope_limiter, dg)
519
520
flux = volume_flux_fv(recon2cons(u_l, equations), recon2cons(u_r, equations),
521
3, equations) # orientation 3: z direction
522
523
set_node_vars!(fstar3_L, flux, equations, dg, i, j, k)
524
set_node_vars!(fstar3_R, flux, equations, dg, i, j, k)
525
end
526
527
return nothing
528
end
529
530
function prolong2interfaces!(backend::Nothing, cache, u, mesh::TreeMesh{3}, equations,
531
dg::DG)
532
@unpack interfaces = cache
533
@unpack orientations, neighbor_ids = interfaces
534
interfaces_u = interfaces.u
535
536
@threaded for interface in eachinterface(dg, cache)
537
left_element = neighbor_ids[1, interface]
538
right_element = neighbor_ids[2, interface]
539
540
if orientations[interface] == 1
541
# interface in x-direction
542
for k in eachnode(dg), j in eachnode(dg), v in eachvariable(equations)
543
interfaces_u[1, v, j, k, interface] = u[v, nnodes(dg), j, k,
544
left_element]
545
interfaces_u[2, v, j, k, interface] = u[v, 1, j, k,
546
right_element]
547
end
548
elseif orientations[interface] == 2
549
# interface in y-direction
550
for k in eachnode(dg), i in eachnode(dg), v in eachvariable(equations)
551
interfaces_u[1, v, i, k, interface] = u[v, i, nnodes(dg), k,
552
left_element]
553
interfaces_u[2, v, i, k, interface] = u[v, i, 1, k,
554
right_element]
555
end
556
else # if orientations[interface] == 3
557
# interface in z-direction
558
for j in eachnode(dg), i in eachnode(dg), v in eachvariable(equations)
559
interfaces_u[1, v, i, j, interface] = u[v, i, j, nnodes(dg),
560
left_element]
561
interfaces_u[2, v, i, j, interface] = u[v, i, j, 1, right_element]
562
end
563
end
564
end
565
566
return nothing
567
end
568
569
function calc_interface_flux!(backend::Nothing, surface_flux_values,
570
mesh::TreeMesh{3},
571
have_nonconservative_terms::False, equations,
572
surface_integral, dg::DG, cache)
573
@unpack surface_flux = surface_integral
574
@unpack u, neighbor_ids, orientations = cache.interfaces
575
576
@threaded for interface in eachinterface(dg, cache)
577
# Get neighboring elements
578
left_id = neighbor_ids[1, interface]
579
right_id = neighbor_ids[2, interface]
580
581
# Determine interface direction with respect to elements:
582
# orientation = 1: left -> 2, right -> 1
583
# orientation = 2: left -> 4, right -> 3
584
# orientation = 3: left -> 6, right -> 5
585
left_direction = 2 * orientations[interface]
586
right_direction = 2 * orientations[interface] - 1
587
588
for j in eachnode(dg), i in eachnode(dg)
589
# Call pointwise Riemann solver
590
u_ll, u_rr = get_surface_node_vars(u, equations, dg, i, j, interface)
591
flux = surface_flux(u_ll, u_rr, orientations[interface], equations)
592
593
# Copy flux to left and right element storage
594
for v in eachvariable(equations)
595
surface_flux_values[v, i, j, left_direction, left_id] = flux[v]
596
surface_flux_values[v, i, j, right_direction, right_id] = flux[v]
597
end
598
end
599
end
600
601
return nothing
602
end
603
604
function calc_interface_flux!(backend::Nothing, surface_flux_values,
605
mesh::TreeMesh{3},
606
have_nonconservative_terms::True, equations,
607
surface_integral, dg::DG, cache)
608
surface_flux, nonconservative_flux = surface_integral.surface_flux
609
@unpack u, neighbor_ids, orientations = cache.interfaces
610
611
@threaded for interface in eachinterface(dg, cache)
612
# Get neighboring elements
613
left_id = neighbor_ids[1, interface]
614
right_id = neighbor_ids[2, interface]
615
616
# Determine interface direction with respect to elements:
617
# orientation = 1: left -> 2, right -> 1
618
# orientation = 2: left -> 4, right -> 3
619
# orientation = 3: left -> 6, right -> 5
620
left_direction = 2 * orientations[interface]
621
right_direction = 2 * orientations[interface] - 1
622
623
for j in eachnode(dg), i in eachnode(dg)
624
# Call pointwise Riemann solver
625
orientation = orientations[interface]
626
u_ll, u_rr = get_surface_node_vars(u, equations, dg, i, j, interface)
627
flux = surface_flux(u_ll, u_rr, orientation, equations)
628
629
# Compute both nonconservative fluxes
630
noncons_left = nonconservative_flux(u_ll, u_rr, orientation, equations)
631
noncons_right = nonconservative_flux(u_rr, u_ll, orientation, equations)
632
633
# Copy flux to left and right element storage
634
for v in eachvariable(equations)
635
# Note the factor 0.5 necessary for the nonconservative fluxes based on
636
# the interpretation of global SBP operators coupled discontinuously via
637
# central fluxes/SATs
638
surface_flux_values[v, i, j, left_direction, left_id] = flux[v] +
639
0.5f0 *
640
noncons_left[v]
641
surface_flux_values[v, i, j, right_direction, right_id] = flux[v] +
642
0.5f0 *
643
noncons_right[v]
644
end
645
end
646
end
647
648
return nothing
649
end
650
651
function prolong2boundaries!(cache, u,
652
mesh::TreeMesh{3}, equations, dg::DG)
653
@unpack boundaries = cache
654
@unpack orientations, neighbor_sides = boundaries
655
656
@threaded for boundary in eachboundary(dg, cache)
657
element = boundaries.neighbor_ids[boundary]
658
659
if orientations[boundary] == 1
660
# boundary in x-direction
661
if neighbor_sides[boundary] == 1
662
# element in -x direction of boundary
663
for k in eachnode(dg), j in eachnode(dg), v in eachvariable(equations)
664
boundaries.u[1, v, j, k, boundary] = u[v, nnodes(dg), j, k, element]
665
end
666
else # Element in +x direction of boundary
667
for k in eachnode(dg), j in eachnode(dg), v in eachvariable(equations)
668
boundaries.u[2, v, j, k, boundary] = u[v, 1, j, k, element]
669
end
670
end
671
elseif orientations[boundary] == 2
672
# boundary in y-direction
673
if neighbor_sides[boundary] == 1
674
# element in -y direction of boundary
675
for k in eachnode(dg), i in eachnode(dg), v in eachvariable(equations)
676
boundaries.u[1, v, i, k, boundary] = u[v, i, nnodes(dg), k, element]
677
end
678
else
679
# element in +y direction of boundary
680
for k in eachnode(dg), i in eachnode(dg), v in eachvariable(equations)
681
boundaries.u[2, v, i, k, boundary] = u[v, i, 1, k, element]
682
end
683
end
684
else #if orientations[boundary] == 3
685
# boundary in z-direction
686
if neighbor_sides[boundary] == 1
687
# element in -z direction of boundary
688
for j in eachnode(dg), i in eachnode(dg), v in eachvariable(equations)
689
boundaries.u[1, v, i, j, boundary] = u[v, i, j, nnodes(dg), element]
690
end
691
else
692
# element in +z direction of boundary
693
for j in eachnode(dg), i in eachnode(dg), v in eachvariable(equations)
694
boundaries.u[2, v, i, j, boundary] = u[v, i, j, 1, element]
695
end
696
end
697
end
698
end
699
700
return nothing
701
end
702
703
function calc_boundary_flux!(cache, t, boundary_conditions::NamedTuple,
704
mesh::TreeMesh{3}, equations, surface_integral, dg::DG)
705
@unpack surface_flux_values = cache.elements
706
@unpack n_boundaries_per_direction = cache.boundaries
707
708
# Calculate indices
709
lasts = accumulate(+, n_boundaries_per_direction)
710
firsts = lasts - n_boundaries_per_direction .+ 1
711
712
# Calc boundary fluxes in each direction
713
calc_boundary_flux_by_direction!(surface_flux_values, t, boundary_conditions[1],
714
equations, surface_integral, dg, cache,
715
1, firsts[1], lasts[1])
716
calc_boundary_flux_by_direction!(surface_flux_values, t, boundary_conditions[2],
717
equations, surface_integral, dg, cache,
718
2, firsts[2], lasts[2])
719
calc_boundary_flux_by_direction!(surface_flux_values, t, boundary_conditions[3],
720
equations, surface_integral, dg, cache,
721
3, firsts[3], lasts[3])
722
calc_boundary_flux_by_direction!(surface_flux_values, t, boundary_conditions[4],
723
equations, surface_integral, dg, cache,
724
4, firsts[4], lasts[4])
725
calc_boundary_flux_by_direction!(surface_flux_values, t, boundary_conditions[5],
726
equations, surface_integral, dg, cache,
727
5, firsts[5], lasts[5])
728
calc_boundary_flux_by_direction!(surface_flux_values, t, boundary_conditions[6],
729
equations, surface_integral, dg, cache,
730
6, firsts[6], lasts[6])
731
732
return nothing
733
end
734
735
function calc_boundary_flux_by_direction!(surface_flux_values::AbstractArray{<:Any, 5},
736
t,
737
boundary_condition, equations,
738
surface_integral, dg::DG, cache,
739
direction, first_boundary, last_boundary)
740
@unpack surface_flux = surface_integral
741
@unpack u, neighbor_ids, neighbor_sides, node_coordinates, orientations = cache.boundaries
742
743
@threaded for boundary in first_boundary:last_boundary
744
# Get neighboring element
745
neighbor = neighbor_ids[boundary]
746
747
for j in eachnode(dg), i in eachnode(dg)
748
# Get boundary flux
749
u_ll, u_rr = get_surface_node_vars(u, equations, dg, i, j, boundary)
750
if neighbor_sides[boundary] == 1 # Element is on the left, boundary on the right
751
u_inner = u_ll
752
else # Element is on the right, boundary on the left
753
u_inner = u_rr
754
end
755
x = get_node_coords(node_coordinates, equations, dg, i, j, boundary)
756
flux = boundary_condition(u_inner, orientations[boundary], direction, x, t,
757
surface_flux, equations)
758
759
# Copy flux to left and right element storage
760
for v in eachvariable(equations)
761
surface_flux_values[v, i, j, direction, neighbor] = flux[v]
762
end
763
end
764
end
765
766
return nothing
767
end
768
769
function prolong2mortars!(cache, u,
770
mesh::TreeMesh{3}, equations,
771
mortar_l2::LobattoLegendreMortarL2,
772
dg::DGSEM)
773
# temporary buffer for projections
774
@unpack fstar_tmp1_threaded = cache
775
776
@threaded for mortar in eachmortar(dg, cache)
777
fstar_tmp1 = fstar_tmp1_threaded[Threads.threadid()]
778
779
lower_left_element = cache.mortars.neighbor_ids[1, mortar]
780
lower_right_element = cache.mortars.neighbor_ids[2, mortar]
781
upper_left_element = cache.mortars.neighbor_ids[3, mortar]
782
upper_right_element = cache.mortars.neighbor_ids[4, mortar]
783
large_element = cache.mortars.neighbor_ids[5, mortar]
784
785
# Copy solution small to small
786
if cache.mortars.large_sides[mortar] == 1 # -> small elements on right side
787
if cache.mortars.orientations[mortar] == 1
788
# L2 mortars in x-direction
789
for k in eachnode(dg), j in eachnode(dg)
790
for v in eachvariable(equations)
791
cache.mortars.u_upper_left[2, v, j, k, mortar] = u[v, 1, j, k,
792
upper_left_element]
793
cache.mortars.u_upper_right[2, v, j, k, mortar] = u[v, 1, j, k,
794
upper_right_element]
795
cache.mortars.u_lower_left[2, v, j, k, mortar] = u[v, 1, j, k,
796
lower_left_element]
797
cache.mortars.u_lower_right[2, v, j, k, mortar] = u[v, 1, j, k,
798
lower_right_element]
799
end
800
end
801
elseif cache.mortars.orientations[mortar] == 2
802
# L2 mortars in y-direction
803
for k in eachnode(dg), i in eachnode(dg)
804
for v in eachvariable(equations)
805
cache.mortars.u_upper_left[2, v, i, k, mortar] = u[v, i, 1, k,
806
upper_left_element]
807
cache.mortars.u_upper_right[2, v, i, k, mortar] = u[v, i, 1, k,
808
upper_right_element]
809
cache.mortars.u_lower_left[2, v, i, k, mortar] = u[v, i, 1, k,
810
lower_left_element]
811
cache.mortars.u_lower_right[2, v, i, k, mortar] = u[v, i, 1, k,
812
lower_right_element]
813
end
814
end
815
else # orientations[mortar] == 3
816
# L2 mortars in z-direction
817
for j in eachnode(dg), i in eachnode(dg)
818
for v in eachvariable(equations)
819
cache.mortars.u_upper_left[2, v, i, j, mortar] = u[v, i, j, 1,
820
upper_left_element]
821
cache.mortars.u_upper_right[2, v, i, j, mortar] = u[v, i, j, 1,
822
upper_right_element]
823
cache.mortars.u_lower_left[2, v, i, j, mortar] = u[v, i, j, 1,
824
lower_left_element]
825
cache.mortars.u_lower_right[2, v, i, j, mortar] = u[v, i, j, 1,
826
lower_right_element]
827
end
828
end
829
end
830
else # large_sides[mortar] == 2 -> small elements on left side
831
if cache.mortars.orientations[mortar] == 1
832
# L2 mortars in x-direction
833
for k in eachnode(dg), j in eachnode(dg)
834
for v in eachvariable(equations)
835
cache.mortars.u_upper_left[1, v, j, k, mortar] = u[v,
836
nnodes(dg),
837
j, k,
838
upper_left_element]
839
cache.mortars.u_upper_right[1, v, j, k, mortar] = u[v,
840
nnodes(dg),
841
j, k,
842
upper_right_element]
843
cache.mortars.u_lower_left[1, v, j, k, mortar] = u[v,
844
nnodes(dg),
845
j, k,
846
lower_left_element]
847
cache.mortars.u_lower_right[1, v, j, k, mortar] = u[v,
848
nnodes(dg),
849
j, k,
850
lower_right_element]
851
end
852
end
853
elseif cache.mortars.orientations[mortar] == 2
854
# L2 mortars in y-direction
855
for k in eachnode(dg), i in eachnode(dg)
856
for v in eachvariable(equations)
857
cache.mortars.u_upper_left[1, v, i, k, mortar] = u[v, i,
858
nnodes(dg),
859
k,
860
upper_left_element]
861
cache.mortars.u_upper_right[1, v, i, k, mortar] = u[v, i,
862
nnodes(dg),
863
k,
864
upper_right_element]
865
cache.mortars.u_lower_left[1, v, i, k, mortar] = u[v, i,
866
nnodes(dg),
867
k,
868
lower_left_element]
869
cache.mortars.u_lower_right[1, v, i, k, mortar] = u[v, i,
870
nnodes(dg),
871
k,
872
lower_right_element]
873
end
874
end
875
else # if cache.mortars.orientations[mortar] == 3
876
# L2 mortars in z-direction
877
for j in eachnode(dg), i in eachnode(dg)
878
for v in eachvariable(equations)
879
cache.mortars.u_upper_left[1, v, i, j, mortar] = u[v, i, j,
880
nnodes(dg),
881
upper_left_element]
882
cache.mortars.u_upper_right[1, v, i, j, mortar] = u[v, i, j,
883
nnodes(dg),
884
upper_right_element]
885
cache.mortars.u_lower_left[1, v, i, j, mortar] = u[v, i, j,
886
nnodes(dg),
887
lower_left_element]
888
cache.mortars.u_lower_right[1, v, i, j, mortar] = u[v, i, j,
889
nnodes(dg),
890
lower_right_element]
891
end
892
end
893
end
894
end
895
896
# Interpolate large element face data to small interface locations
897
if cache.mortars.large_sides[mortar] == 1 # -> large element on left side
898
leftright = 1
899
if cache.mortars.orientations[mortar] == 1
900
# L2 mortars in x-direction
901
u_large = view(u, :, nnodes(dg), :, :, large_element)
902
element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,
903
mortar, u_large, fstar_tmp1)
904
elseif cache.mortars.orientations[mortar] == 2
905
# L2 mortars in y-direction
906
u_large = view(u, :, :, nnodes(dg), :, large_element)
907
element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,
908
mortar, u_large, fstar_tmp1)
909
else # cache.mortars.orientations[mortar] == 3
910
# L2 mortars in z-direction
911
u_large = view(u, :, :, :, nnodes(dg), large_element)
912
element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,
913
mortar, u_large, fstar_tmp1)
914
end
915
else # large_sides[mortar] == 2 -> large element on right side
916
leftright = 2
917
if cache.mortars.orientations[mortar] == 1
918
# L2 mortars in x-direction
919
u_large = view(u, :, 1, :, :, large_element)
920
element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,
921
mortar, u_large, fstar_tmp1)
922
elseif cache.mortars.orientations[mortar] == 2
923
# L2 mortars in y-direction
924
u_large = view(u, :, :, 1, :, large_element)
925
element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,
926
mortar, u_large, fstar_tmp1)
927
else # cache.mortars.orientations[mortar] == 3
928
# L2 mortars in z-direction
929
u_large = view(u, :, :, :, 1, large_element)
930
element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,
931
mortar, u_large, fstar_tmp1)
932
end
933
end
934
end
935
936
return nothing
937
end
938
939
@inline function element_solutions_to_mortars!(mortars,
940
mortar_l2::LobattoLegendreMortarL2,
941
leftright, mortar,
942
u_large::AbstractArray{<:Any, 3},
943
fstar_tmp1)
944
multiply_dimensionwise!(view(mortars.u_upper_left, leftright, :, :, :, mortar),
945
mortar_l2.forward_lower, mortar_l2.forward_upper, u_large,
946
fstar_tmp1)
947
multiply_dimensionwise!(view(mortars.u_upper_right, leftright, :, :, :, mortar),
948
mortar_l2.forward_upper, mortar_l2.forward_upper, u_large,
949
fstar_tmp1)
950
multiply_dimensionwise!(view(mortars.u_lower_left, leftright, :, :, :, mortar),
951
mortar_l2.forward_lower, mortar_l2.forward_lower, u_large,
952
fstar_tmp1)
953
multiply_dimensionwise!(view(mortars.u_lower_right, leftright, :, :, :, mortar),
954
mortar_l2.forward_upper, mortar_l2.forward_lower, u_large,
955
fstar_tmp1)
956
return nothing
957
end
958
959
function calc_mortar_flux!(surface_flux_values,
960
mesh::TreeMesh{3},
961
have_nonconservative_terms::False, equations,
962
mortar_l2::LobattoLegendreMortarL2,
963
surface_integral, dg::DG, cache)
964
@unpack surface_flux = surface_integral
965
@unpack u_lower_left, u_lower_right, u_upper_left, u_upper_right, orientations = cache.mortars
966
@unpack (fstar_primary_upper_left_threaded, fstar_primary_upper_right_threaded,
967
fstar_primary_lower_left_threaded, fstar_primary_lower_right_threaded,
968
fstar_secondary_upper_left_threaded, fstar_secondary_upper_right_threaded,
969
fstar_secondary_lower_left_threaded, fstar_secondary_lower_right_threaded,
970
fstar_tmp1_threaded) = cache
971
972
@threaded for mortar in eachmortar(dg, cache)
973
# Choose thread-specific pre-allocated container
974
fstar_primary_upper_left = fstar_primary_upper_left_threaded[Threads.threadid()]
975
fstar_primary_upper_right = fstar_primary_upper_right_threaded[Threads.threadid()]
976
fstar_primary_lower_left = fstar_primary_lower_left_threaded[Threads.threadid()]
977
fstar_primary_lower_right = fstar_primary_lower_right_threaded[Threads.threadid()]
978
fstar_secondary_upper_left = fstar_secondary_upper_left_threaded[Threads.threadid()]
979
fstar_secondary_upper_right = fstar_secondary_upper_right_threaded[Threads.threadid()]
980
fstar_secondary_lower_left = fstar_secondary_lower_left_threaded[Threads.threadid()]
981
fstar_secondary_lower_right = fstar_secondary_lower_right_threaded[Threads.threadid()]
982
fstar_tmp1 = fstar_tmp1_threaded[Threads.threadid()]
983
984
# Calculate fluxes
985
orientation = orientations[mortar]
986
calc_fstar!(fstar_primary_upper_left, equations, surface_flux, dg,
987
u_upper_left, mortar, orientation)
988
calc_fstar!(fstar_primary_upper_right, equations, surface_flux, dg,
989
u_upper_right, mortar, orientation)
990
calc_fstar!(fstar_primary_lower_left, equations, surface_flux, dg,
991
u_lower_left, mortar, orientation)
992
calc_fstar!(fstar_primary_lower_right, equations, surface_flux, dg,
993
u_lower_right, mortar, orientation)
994
calc_fstar!(fstar_secondary_upper_left, equations, surface_flux, dg,
995
u_upper_left, mortar, orientation)
996
calc_fstar!(fstar_secondary_upper_right, equations, surface_flux, dg,
997
u_upper_right, mortar, orientation)
998
calc_fstar!(fstar_secondary_lower_left, equations, surface_flux, dg,
999
u_lower_left, mortar, orientation)
1000
calc_fstar!(fstar_secondary_lower_right, equations, surface_flux, dg,
1001
u_lower_right, mortar, orientation)
1002
1003
mortar_fluxes_to_elements!(surface_flux_values,
1004
mesh, equations, mortar_l2, dg, cache, mortar,
1005
fstar_primary_upper_left, fstar_primary_upper_right,
1006
fstar_primary_lower_left, fstar_primary_lower_right,
1007
fstar_secondary_upper_left,
1008
fstar_secondary_upper_right,
1009
fstar_secondary_lower_left,
1010
fstar_secondary_lower_right,
1011
fstar_tmp1)
1012
end
1013
1014
return nothing
1015
end
1016
1017
function calc_mortar_flux!(surface_flux_values,
1018
mesh::TreeMesh{3},
1019
have_nonconservative_terms::True, equations,
1020
mortar_l2::LobattoLegendreMortarL2,
1021
surface_integral, dg::DG, cache)
1022
surface_flux, nonconservative_flux = surface_integral.surface_flux
1023
@unpack u_lower_left, u_lower_right, u_upper_left, u_upper_right, orientations, large_sides = cache.mortars
1024
@unpack (fstar_primary_upper_left_threaded, fstar_primary_upper_right_threaded,
1025
fstar_primary_lower_left_threaded, fstar_primary_lower_right_threaded,
1026
fstar_secondary_upper_left_threaded, fstar_secondary_upper_right_threaded,
1027
fstar_secondary_lower_left_threaded, fstar_secondary_lower_right_threaded,
1028
fstar_tmp1_threaded) = cache
1029
1030
@threaded for mortar in eachmortar(dg, cache)
1031
# Choose thread-specific pre-allocated container
1032
fstar_primary_upper_left = fstar_primary_upper_left_threaded[Threads.threadid()]
1033
fstar_primary_upper_right = fstar_primary_upper_right_threaded[Threads.threadid()]
1034
fstar_primary_lower_left = fstar_primary_lower_left_threaded[Threads.threadid()]
1035
fstar_primary_lower_right = fstar_primary_lower_right_threaded[Threads.threadid()]
1036
fstar_secondary_upper_left = fstar_secondary_upper_left_threaded[Threads.threadid()]
1037
fstar_secondary_upper_right = fstar_secondary_upper_right_threaded[Threads.threadid()]
1038
fstar_secondary_lower_left = fstar_secondary_lower_left_threaded[Threads.threadid()]
1039
fstar_secondary_lower_right = fstar_secondary_lower_right_threaded[Threads.threadid()]
1040
fstar_tmp1 = fstar_tmp1_threaded[Threads.threadid()]
1041
1042
# Calculate fluxes
1043
orientation = orientations[mortar]
1044
calc_fstar!(fstar_primary_upper_left, equations, surface_flux, dg,
1045
u_upper_left, mortar, orientation)
1046
calc_fstar!(fstar_primary_upper_right, equations, surface_flux, dg,
1047
u_upper_right, mortar, orientation)
1048
calc_fstar!(fstar_primary_lower_left, equations, surface_flux, dg,
1049
u_lower_left, mortar, orientation)
1050
calc_fstar!(fstar_primary_lower_right, equations, surface_flux, dg,
1051
u_lower_right, mortar, orientation)
1052
calc_fstar!(fstar_secondary_upper_left, equations, surface_flux, dg,
1053
u_upper_left, mortar, orientation)
1054
calc_fstar!(fstar_secondary_upper_right, equations, surface_flux, dg,
1055
u_upper_right, mortar, orientation)
1056
calc_fstar!(fstar_secondary_lower_left, equations, surface_flux, dg,
1057
u_lower_left, mortar, orientation)
1058
calc_fstar!(fstar_secondary_lower_right, equations, surface_flux, dg,
1059
u_lower_right, mortar, orientation)
1060
1061
# Add nonconservative fluxes.
1062
# These need to be adapted on the geometry (left/right) since the order of
1063
# the arguments matters, based on the global SBP operator interpretation.
1064
# The same interpretation (global SBP operators coupled discontinuously via
1065
# central fluxes/SATs) explains why we need the factor 0.5.
1066
# Alternatively, you can also follow the argumentation of Bohm et al. 2018
1067
# ("nonconservative diamond flux")
1068
if large_sides[mortar] == 1 # -> small elements on right side
1069
for j in eachnode(dg), i in eachnode(dg)
1070
# Pull the left and right solutions
1071
u_upper_left_ll, u_upper_left_rr = get_surface_node_vars(u_upper_left,
1072
equations, dg,
1073
i, j, mortar)
1074
u_upper_right_ll, u_upper_right_rr = get_surface_node_vars(u_upper_right,
1075
equations,
1076
dg, i, j,
1077
mortar)
1078
u_lower_left_ll, u_lower_left_rr = get_surface_node_vars(u_lower_left,
1079
equations, dg,
1080
i, j, mortar)
1081
u_lower_right_ll, u_lower_right_rr = get_surface_node_vars(u_lower_right,
1082
equations,
1083
dg, i, j,
1084
mortar)
1085
# Call pointwise nonconservative term
1086
noncons_primary_upper_left = nonconservative_flux(u_upper_left_ll,
1087
u_upper_left_rr,
1088
orientation,
1089
equations)
1090
noncons_primary_upper_right = nonconservative_flux(u_upper_right_ll,
1091
u_upper_right_rr,
1092
orientation,
1093
equations)
1094
noncons_primary_lower_left = nonconservative_flux(u_lower_left_ll,
1095
u_lower_left_rr,
1096
orientation,
1097
equations)
1098
noncons_primary_lower_right = nonconservative_flux(u_lower_right_ll,
1099
u_lower_right_rr,
1100
orientation,
1101
equations)
1102
noncons_secondary_upper_left = nonconservative_flux(u_upper_left_rr,
1103
u_upper_left_ll,
1104
orientation,
1105
equations)
1106
noncons_secondary_upper_right = nonconservative_flux(u_upper_right_rr,
1107
u_upper_right_ll,
1108
orientation,
1109
equations)
1110
noncons_secondary_lower_left = nonconservative_flux(u_lower_left_rr,
1111
u_lower_left_ll,
1112
orientation,
1113
equations)
1114
noncons_secondary_lower_right = nonconservative_flux(u_lower_right_rr,
1115
u_lower_right_ll,
1116
orientation,
1117
equations)
1118
# Add to primary and secondary temporary storage
1119
multiply_add_to_node_vars!(fstar_primary_upper_left, 0.5f0,
1120
noncons_primary_upper_left,
1121
equations, dg, i, j)
1122
multiply_add_to_node_vars!(fstar_primary_upper_right, 0.5f0,
1123
noncons_primary_upper_right,
1124
equations, dg, i, j)
1125
multiply_add_to_node_vars!(fstar_primary_lower_left, 0.5f0,
1126
noncons_primary_lower_left,
1127
equations, dg, i, j)
1128
multiply_add_to_node_vars!(fstar_primary_lower_right, 0.5f0,
1129
noncons_primary_lower_right,
1130
equations, dg, i, j)
1131
multiply_add_to_node_vars!(fstar_secondary_upper_left, 0.5f0,
1132
noncons_secondary_upper_left,
1133
equations, dg, i, j)
1134
multiply_add_to_node_vars!(fstar_secondary_upper_right, 0.5f0,
1135
noncons_secondary_upper_right,
1136
equations, dg, i, j)
1137
multiply_add_to_node_vars!(fstar_secondary_lower_left, 0.5f0,
1138
noncons_secondary_lower_left,
1139
equations, dg, i, j)
1140
multiply_add_to_node_vars!(fstar_secondary_lower_right, 0.5f0,
1141
noncons_secondary_lower_right,
1142
equations, dg, i, j)
1143
end
1144
else # large_sides[mortar] == 2 -> small elements on the left
1145
for j in eachnode(dg), i in eachnode(dg)
1146
# Pull the left and right solutions
1147
u_upper_left_ll, u_upper_left_rr = get_surface_node_vars(u_upper_left,
1148
equations, dg,
1149
i, j, mortar)
1150
u_upper_right_ll, u_upper_right_rr = get_surface_node_vars(u_upper_right,
1151
equations,
1152
dg, i, j,
1153
mortar)
1154
u_lower_left_ll, u_lower_left_rr = get_surface_node_vars(u_lower_left,
1155
equations, dg,
1156
i, j, mortar)
1157
u_lower_right_ll, u_lower_right_rr = get_surface_node_vars(u_lower_right,
1158
equations,
1159
dg, i, j,
1160
mortar)
1161
# Call pointwise nonconservative term
1162
noncons_primary_upper_left = nonconservative_flux(u_upper_left_rr,
1163
u_upper_left_ll,
1164
orientation,
1165
equations)
1166
noncons_primary_upper_right = nonconservative_flux(u_upper_right_rr,
1167
u_upper_right_ll,
1168
orientation,
1169
equations)
1170
noncons_primary_lower_left = nonconservative_flux(u_lower_left_rr,
1171
u_lower_left_ll,
1172
orientation,
1173
equations)
1174
noncons_primary_lower_right = nonconservative_flux(u_lower_right_rr,
1175
u_lower_right_ll,
1176
orientation,
1177
equations)
1178
noncons_secondary_upper_left = nonconservative_flux(u_upper_left_ll,
1179
u_upper_left_rr,
1180
orientation,
1181
equations)
1182
noncons_secondary_upper_right = nonconservative_flux(u_upper_right_ll,
1183
u_upper_right_rr,
1184
orientation,
1185
equations)
1186
noncons_secondary_lower_left = nonconservative_flux(u_lower_left_ll,
1187
u_lower_left_rr,
1188
orientation,
1189
equations)
1190
noncons_secondary_lower_right = nonconservative_flux(u_lower_right_ll,
1191
u_lower_right_rr,
1192
orientation,
1193
equations)
1194
# Add to primary and secondary temporary storage
1195
multiply_add_to_node_vars!(fstar_primary_upper_left, 0.5f0,
1196
noncons_primary_upper_left,
1197
equations, dg, i, j)
1198
multiply_add_to_node_vars!(fstar_primary_upper_right, 0.5f0,
1199
noncons_primary_upper_right,
1200
equations, dg, i, j)
1201
multiply_add_to_node_vars!(fstar_primary_lower_left, 0.5f0,
1202
noncons_primary_lower_left,
1203
equations, dg, i, j)
1204
multiply_add_to_node_vars!(fstar_primary_lower_right, 0.5f0,
1205
noncons_primary_lower_right,
1206
equations, dg, i, j)
1207
multiply_add_to_node_vars!(fstar_secondary_upper_left, 0.5f0,
1208
noncons_secondary_upper_left,
1209
equations, dg, i, j)
1210
multiply_add_to_node_vars!(fstar_secondary_upper_right, 0.5f0,
1211
noncons_secondary_upper_right,
1212
equations, dg, i, j)
1213
multiply_add_to_node_vars!(fstar_secondary_lower_left, 0.5f0,
1214
noncons_secondary_lower_left,
1215
equations, dg, i, j)
1216
multiply_add_to_node_vars!(fstar_secondary_lower_right, 0.5f0,
1217
noncons_secondary_lower_right,
1218
equations, dg, i, j)
1219
end
1220
end
1221
1222
mortar_fluxes_to_elements!(surface_flux_values,
1223
mesh, equations, mortar_l2, dg, cache, mortar,
1224
fstar_primary_upper_left, fstar_primary_upper_right,
1225
fstar_primary_lower_left, fstar_primary_lower_right,
1226
fstar_secondary_upper_left,
1227
fstar_secondary_upper_right,
1228
fstar_secondary_lower_left,
1229
fstar_secondary_lower_right,
1230
fstar_tmp1)
1231
end
1232
1233
return nothing
1234
end
1235
1236
@inline function calc_fstar!(destination::AbstractArray{<:Any, 3}, equations,
1237
surface_flux, dg::DGSEM,
1238
u_interfaces, interface, orientation)
1239
for j in eachnode(dg), i in eachnode(dg)
1240
# Call pointwise two-point numerical flux function
1241
u_ll, u_rr = get_surface_node_vars(u_interfaces, equations, dg, i, j, interface)
1242
flux = surface_flux(u_ll, u_rr, orientation, equations)
1243
1244
# Copy flux to left and right element storage
1245
set_node_vars!(destination, flux, equations, dg, i, j)
1246
end
1247
1248
return nothing
1249
end
1250
1251
@inline function mortar_fluxes_to_elements!(surface_flux_values,
1252
mesh::TreeMesh{3}, equations,
1253
mortar_l2::LobattoLegendreMortarL2,
1254
dg::DGSEM, cache,
1255
mortar,
1256
fstar_primary_upper_left,
1257
fstar_primary_upper_right,
1258
fstar_primary_lower_left,
1259
fstar_primary_lower_right,
1260
fstar_secondary_upper_left,
1261
fstar_secondary_upper_right,
1262
fstar_secondary_lower_left,
1263
fstar_secondary_lower_right,
1264
fstar_tmp1)
1265
lower_left_element = cache.mortars.neighbor_ids[1, mortar]
1266
lower_right_element = cache.mortars.neighbor_ids[2, mortar]
1267
upper_left_element = cache.mortars.neighbor_ids[3, mortar]
1268
upper_right_element = cache.mortars.neighbor_ids[4, mortar]
1269
large_element = cache.mortars.neighbor_ids[5, mortar]
1270
1271
# Copy flux small to small
1272
if cache.mortars.large_sides[mortar] == 1 # -> small elements on right side
1273
if cache.mortars.orientations[mortar] == 1
1274
# L2 mortars in x-direction
1275
direction = 1
1276
elseif cache.mortars.orientations[mortar] == 2
1277
# L2 mortars in y-direction
1278
direction = 3
1279
else # if cache.mortars.orientations[mortar] == 3
1280
# L2 mortars in z-direction
1281
direction = 5
1282
end
1283
else # large_sides[mortar] == 2 -> small elements on left side
1284
if cache.mortars.orientations[mortar] == 1
1285
# L2 mortars in x-direction
1286
direction = 2
1287
elseif cache.mortars.orientations[mortar] == 2
1288
# L2 mortars in y-direction
1289
direction = 4
1290
else # if cache.mortars.orientations[mortar] == 3
1291
# L2 mortars in z-direction
1292
direction = 6
1293
end
1294
end
1295
surface_flux_values[:, :, :, direction, upper_left_element] .= fstar_primary_upper_left
1296
surface_flux_values[:, :, :, direction, upper_right_element] .= fstar_primary_upper_right
1297
surface_flux_values[:, :, :, direction, lower_left_element] .= fstar_primary_lower_left
1298
surface_flux_values[:, :, :, direction, lower_right_element] .= fstar_primary_lower_right
1299
1300
# Project small fluxes to large element
1301
if cache.mortars.large_sides[mortar] == 1 # -> small elements on right side
1302
if cache.mortars.orientations[mortar] == 1
1303
# L2 mortars in x-direction
1304
direction = 2
1305
elseif cache.mortars.orientations[mortar] == 2
1306
# L2 mortars in y-direction
1307
direction = 4
1308
else # if cache.mortars.orientations[mortar] == 3
1309
# L2 mortars in z-direction
1310
direction = 6
1311
end
1312
else # large_sides[mortar] == 2 -> small elements on left side
1313
if cache.mortars.orientations[mortar] == 1
1314
# L2 mortars in x-direction
1315
direction = 1
1316
elseif cache.mortars.orientations[mortar] == 2
1317
# L2 mortars in y-direction
1318
direction = 3
1319
else # if cache.mortars.orientations[mortar] == 3
1320
# L2 mortars in z-direction
1321
direction = 5
1322
end
1323
end
1324
1325
multiply_dimensionwise!(view(surface_flux_values, :, :, :, direction,
1326
large_element),
1327
mortar_l2.reverse_lower, mortar_l2.reverse_upper,
1328
fstar_secondary_upper_left, fstar_tmp1)
1329
add_multiply_dimensionwise!(view(surface_flux_values, :, :, :, direction,
1330
large_element),
1331
mortar_l2.reverse_upper, mortar_l2.reverse_upper,
1332
fstar_secondary_upper_right, fstar_tmp1)
1333
add_multiply_dimensionwise!(view(surface_flux_values, :, :, :, direction,
1334
large_element),
1335
mortar_l2.reverse_lower, mortar_l2.reverse_lower,
1336
fstar_secondary_lower_left, fstar_tmp1)
1337
add_multiply_dimensionwise!(view(surface_flux_values, :, :, :, direction,
1338
large_element),
1339
mortar_l2.reverse_upper, mortar_l2.reverse_lower,
1340
fstar_secondary_lower_right, fstar_tmp1)
1341
1342
return nothing
1343
end
1344
1345
function calc_surface_integral!(backend::Nothing, du, u,
1346
mesh::Union{TreeMesh{3}, StructuredMesh{3}},
1347
equations, surface_integral::SurfaceIntegralWeakForm,
1348
dg::DGSEM, cache)
1349
@unpack inverse_weights = dg.basis
1350
@unpack surface_flux_values = cache.elements
1351
1352
# This computes the **negative** surface integral contribution,
1353
# i.e., M^{-1} * boundary_interpolation^T (which is for Gauss-Lobatto DGSEM just M^{-1} * B)
1354
# and the missing "-" is taken care of by `apply_jacobian!`.
1355
#
1356
# We also use explicit assignments instead of `+=` and `-=` to let `@muladd`
1357
# turn these into FMAs (see comment at the top of the file).
1358
factor = inverse_weights[1] # For LGL basis: Identical to weighted boundary interpolation at x = ±1
1359
@threaded for element in eachelement(dg, cache)
1360
for m in eachnode(dg), l in eachnode(dg)
1361
for v in eachvariable(equations)
1362
# surface at -x
1363
du[v, 1, l, m, element] = (du[v, 1, l, m, element] -
1364
surface_flux_values[v, l, m, 1,
1365
element] *
1366
factor)
1367
1368
# surface at +x
1369
du[v, nnodes(dg), l, m, element] = (du[v, nnodes(dg), l, m, element] +
1370
surface_flux_values[v, l, m, 2,
1371
element] *
1372
factor)
1373
1374
# surface at -y
1375
du[v, l, 1, m, element] = (du[v, l, 1, m, element] -
1376
surface_flux_values[v, l, m, 3,
1377
element] *
1378
factor)
1379
1380
# surface at +y
1381
du[v, l, nnodes(dg), m, element] = (du[v, l, nnodes(dg), m, element] +
1382
surface_flux_values[v, l, m, 4,
1383
element] *
1384
factor)
1385
1386
# surface at -z
1387
du[v, l, m, 1, element] = (du[v, l, m, 1, element] -
1388
surface_flux_values[v, l, m, 5,
1389
element] *
1390
factor)
1391
1392
# surface at +z
1393
du[v, l, m, nnodes(dg), element] = (du[v, l, m, nnodes(dg), element] +
1394
surface_flux_values[v, l, m, 6,
1395
element] *
1396
factor)
1397
end
1398
end
1399
end
1400
1401
return nothing
1402
end
1403
1404
function apply_jacobian!(backend::Nothing, du, mesh::TreeMesh{3},
1405
equations, dg::DG, cache)
1406
@unpack inverse_jacobian = cache.elements
1407
1408
@threaded for element in eachelement(dg, cache)
1409
# Negative sign included to account for the negated surface and volume terms,
1410
# see e.g. the computation of `derivative_hat` in the basis setup and
1411
# the comment in `calc_surface_integral!`.
1412
factor = -inverse_jacobian[element]
1413
1414
for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
1415
for v in eachvariable(equations)
1416
du[v, i, j, k, element] *= factor
1417
end
1418
end
1419
end
1420
1421
return nothing
1422
end
1423
1424
# Need dimension specific version to avoid error at dispatching
1425
function calc_sources!(du, u, t, source_terms::Nothing,
1426
equations::AbstractEquations{3}, dg::DG, cache)
1427
return nothing
1428
end
1429
1430
function calc_sources!(du, u, t, source_terms,
1431
equations::AbstractEquations{3}, dg::DG, cache)
1432
@unpack node_coordinates = cache.elements
1433
1434
@threaded for element in eachelement(dg, cache)
1435
for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
1436
u_local = get_node_vars(u, equations, dg, i, j, k, element)
1437
x_local = get_node_coords(node_coordinates, equations, dg,
1438
i, j, k, element)
1439
du_local = source_terms(u_local, x_local, t, equations)
1440
add_to_node_vars!(du, du_local, equations, dg, i, j, k, element)
1441
end
1442
end
1443
1444
return nothing
1445
end
1446
end # @muladd
1447
1448