Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/solvers/dgsem_structured/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
function create_cache(mesh::Union{StructuredMesh{3},
8
P4estMesh{3}, T8codeMesh{3}},
9
equations,
10
volume_integral::AbstractVolumeIntegralSubcell,
11
dg::DG, cache_containers, uEltype)
12
fstar1_L_threaded, fstar1_R_threaded,
13
fstar2_L_threaded, fstar2_R_threaded,
14
fstar3_L_threaded, fstar3_R_threaded = create_f_threaded(mesh, equations, dg,
15
uEltype)
16
17
normal_vectors = NormalVectorContainer3D(mesh, dg, cache_containers)
18
19
cache_subcell_limiting = create_cache_subcell_limiting(mesh, equations,
20
volume_integral, dg,
21
cache_containers, uEltype)
22
23
return (; fstar1_L_threaded, fstar1_R_threaded,
24
fstar2_L_threaded, fstar2_R_threaded,
25
fstar3_L_threaded, fstar3_R_threaded,
26
normal_vectors, cache_subcell_limiting...)
27
end
28
29
#=
30
`weak_form_kernel!` is only implemented for conserved terms as
31
non-conservative terms should always be discretized in conjunction with a flux-splitting scheme,
32
see `flux_differencing_kernel!`.
33
This treatment is required to achieve, e.g., entropy-stability or well-balancedness.
34
See also https://github.com/trixi-framework/Trixi.jl/issues/1671#issuecomment-1765644064
35
=#
36
@inline function weak_form_kernel!(du, u,
37
element,
38
::Type{<:Union{StructuredMesh{3}, P4estMesh{3},
39
T8codeMesh{3}}},
40
have_nonconservative_terms::False, equations,
41
dg::DGSEM, cache, alpha = true)
42
# true * [some floating point value] == [exactly the same floating point value]
43
# This can (hopefully) be optimized away due to constant propagation.
44
@unpack derivative_hat = dg.basis
45
@unpack contravariant_vectors = cache.elements
46
47
for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
48
u_node = get_node_vars(u, equations, dg, i, j, k, element)
49
50
flux1 = flux(u_node, 1, equations)
51
flux2 = flux(u_node, 2, equations)
52
flux3 = flux(u_node, 3, equations)
53
54
# Compute the contravariant flux by taking the scalar product of the
55
# first contravariant vector Ja^1 and the flux vector
56
Ja11, Ja12, Ja13 = get_contravariant_vector(1, contravariant_vectors,
57
i, j, k, element)
58
contravariant_flux1 = Ja11 * flux1 + Ja12 * flux2 + Ja13 * flux3
59
for ii in eachnode(dg)
60
multiply_add_to_node_vars!(du, alpha * derivative_hat[ii, i],
61
contravariant_flux1, equations, dg,
62
ii, j, k, element)
63
end
64
65
# Compute the contravariant flux by taking the scalar product of the
66
# second contravariant vector Ja^2 and the flux vector
67
Ja21, Ja22, Ja23 = get_contravariant_vector(2, contravariant_vectors,
68
i, j, k, element)
69
contravariant_flux2 = Ja21 * flux1 + Ja22 * flux2 + Ja23 * flux3
70
for jj in eachnode(dg)
71
multiply_add_to_node_vars!(du, alpha * derivative_hat[jj, j],
72
contravariant_flux2, equations, dg,
73
i, jj, k, element)
74
end
75
76
# Compute the contravariant flux by taking the scalar product of the
77
# third contravariant vector Ja^3 and the flux vector
78
Ja31, Ja32, Ja33 = get_contravariant_vector(3, contravariant_vectors,
79
i, j, k, element)
80
contravariant_flux3 = Ja31 * flux1 + Ja32 * flux2 + Ja33 * flux3
81
for kk in eachnode(dg)
82
multiply_add_to_node_vars!(du, alpha * derivative_hat[kk, k],
83
contravariant_flux3, equations, dg,
84
i, j, kk, element)
85
end
86
end
87
88
return nothing
89
end
90
91
# flux differencing volume integral on curvilinear hexahedral elements. Averaging of the
92
# mapping terms, stored in `contravariant_vectors`, is peeled apart from the evaluation of
93
# the physical fluxes in each Cartesian direction
94
@inline function flux_differencing_kernel!(du, u, element,
95
::Type{<:Union{StructuredMesh{3},
96
P4estMesh{3},
97
T8codeMesh{3}}},
98
have_nonconservative_terms::False, equations,
99
volume_flux, dg::DGSEM, cache, alpha = true)
100
# true * [some floating point value] == [exactly the same floating point value]
101
# This can (hopefully) be optimized away due to constant propagation.
102
@unpack derivative_split = dg.basis
103
@unpack contravariant_vectors = cache.elements
104
105
# Calculate volume integral in one element
106
for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
107
u_node = get_node_vars(u, equations, dg, i, j, k, element)
108
109
# pull the contravariant vectors in each coordinate direction
110
Ja1_node = get_contravariant_vector(1, contravariant_vectors, i, j, k, element)
111
Ja2_node = get_contravariant_vector(2, contravariant_vectors, i, j, k, element)
112
Ja3_node = get_contravariant_vector(3, contravariant_vectors, i, j, k, element)
113
114
# All diagonal entries of `derivative_split` are zero. Thus, we can skip
115
# the computation of the diagonal terms. In addition, we use the symmetry
116
# of the `volume_flux` to save half of the possible two-point flux
117
# computations.
118
119
# x direction
120
for ii in (i + 1):nnodes(dg)
121
u_node_ii = get_node_vars(u, equations, dg, ii, j, k, element)
122
# pull the contravariant vectors and compute the average
123
Ja1_node_ii = get_contravariant_vector(1, contravariant_vectors,
124
ii, j, k, element)
125
# average mapping terms in first coordinate direction,
126
# used as normal vector in the flux computation
127
Ja1_avg = 0.5f0 * (Ja1_node + Ja1_node_ii)
128
# compute the contravariant sharp flux in the direction of the
129
# averaged contravariant vector
130
fluxtilde1 = volume_flux(u_node, u_node_ii, Ja1_avg, equations)
131
multiply_add_to_node_vars!(du, alpha * derivative_split[i, ii], fluxtilde1,
132
equations, dg, i, j, k, element)
133
multiply_add_to_node_vars!(du, alpha * derivative_split[ii, i], fluxtilde1,
134
equations, dg, ii, j, k, element)
135
end
136
137
# y direction
138
for jj in (j + 1):nnodes(dg)
139
u_node_jj = get_node_vars(u, equations, dg, i, jj, k, element)
140
# pull the contravariant vectors and compute the average
141
Ja2_node_jj = get_contravariant_vector(2, contravariant_vectors,
142
i, jj, k, element)
143
# average mapping terms in second coordinate direction,
144
# used as normal vector in the flux computation
145
Ja2_avg = 0.5f0 * (Ja2_node + Ja2_node_jj)
146
# compute the contravariant sharp flux in the direction of the
147
# averaged contravariant vector
148
fluxtilde2 = volume_flux(u_node, u_node_jj, Ja2_avg, equations)
149
multiply_add_to_node_vars!(du, alpha * derivative_split[j, jj], fluxtilde2,
150
equations, dg, i, j, k, element)
151
multiply_add_to_node_vars!(du, alpha * derivative_split[jj, j], fluxtilde2,
152
equations, dg, i, jj, k, element)
153
end
154
155
# z direction
156
for kk in (k + 1):nnodes(dg)
157
u_node_kk = get_node_vars(u, equations, dg, i, j, kk, element)
158
# pull the contravariant vectors and compute the average
159
Ja3_node_kk = get_contravariant_vector(3, contravariant_vectors,
160
i, j, kk, element)
161
# average mapping terms in third coordinate direction,
162
# used as normal vector in the flux computation
163
Ja3_avg = 0.5f0 * (Ja3_node + Ja3_node_kk)
164
# compute the contravariant sharp flux in the direction of the
165
# averaged contravariant vector
166
fluxtilde3 = volume_flux(u_node, u_node_kk, Ja3_avg, equations)
167
multiply_add_to_node_vars!(du, alpha * derivative_split[k, kk], fluxtilde3,
168
equations, dg, i, j, k, element)
169
multiply_add_to_node_vars!(du, alpha * derivative_split[kk, k], fluxtilde3,
170
equations, dg, i, j, kk, element)
171
end
172
end
173
174
return nothing
175
end
176
177
@inline function flux_differencing_kernel!(du, u, element,
178
MeshT::Type{<:Union{StructuredMesh{3},
179
P4estMesh{3},
180
T8codeMesh{3}}},
181
have_nonconservative_terms::True, equations,
182
volume_flux, dg::DGSEM, cache, alpha = true)
183
flux_differencing_kernel!(du, u, element, MeshT, have_nonconservative_terms,
184
combine_conservative_and_nonconservative_fluxes(volume_flux,
185
equations),
186
equations, volume_flux, dg, cache, alpha)
187
188
return nothing
189
end
190
191
@inline function flux_differencing_kernel!(du, u, element,
192
MeshT::Type{<:Union{StructuredMesh{3},
193
P4estMesh{3},
194
T8codeMesh{3}}},
195
have_nonconservative_terms::True,
196
combine_conservative_and_nonconservative_fluxes::False,
197
equations,
198
volume_flux, dg::DGSEM, cache, alpha = true)
199
@unpack derivative_split = dg.basis
200
@unpack contravariant_vectors = cache.elements
201
symmetric_flux, nonconservative_flux = volume_flux
202
203
# Apply the symmetric flux as usual
204
flux_differencing_kernel!(du, u, element, MeshT, False(), equations, symmetric_flux,
205
dg, cache, alpha)
206
207
# Calculate the remaining volume terms using the nonsymmetric generalized flux
208
for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
209
u_node = get_node_vars(u, equations, dg, i, j, k, element)
210
211
# pull the contravariant vectors in each coordinate direction
212
Ja1_node = get_contravariant_vector(1, contravariant_vectors, i, j, k, element)
213
Ja2_node = get_contravariant_vector(2, contravariant_vectors, i, j, k, element)
214
Ja3_node = get_contravariant_vector(3, contravariant_vectors, i, j, k, element)
215
216
# The diagonal terms are zero since the diagonal of `derivative_split`
217
# is zero. We ignore this for now.
218
# In general, nonconservative fluxes can depend on both the contravariant
219
# vectors (normal direction) at the current node and the averaged ones.
220
# Thus, we need to pass both to the nonconservative flux.
221
222
# x direction
223
integral_contribution = zero(u_node)
224
for ii in eachnode(dg)
225
u_node_ii = get_node_vars(u, equations, dg, ii, j, k, element)
226
# pull the contravariant vectors and compute the average
227
Ja1_node_ii = get_contravariant_vector(1, contravariant_vectors,
228
ii, j, k, element)
229
# average mapping terms in first coordinate direction,
230
# used as normal vector in the flux computation
231
Ja1_avg = 0.5f0 * (Ja1_node + Ja1_node_ii)
232
# compute the contravariant nonconservative flux in the direction of the
233
# averaged contravariant vector
234
fluxtilde1 = nonconservative_flux(u_node, u_node_ii, Ja1_avg,
235
equations)
236
integral_contribution = integral_contribution +
237
derivative_split[i, ii] * fluxtilde1
238
end
239
240
# y direction
241
for jj in eachnode(dg)
242
u_node_jj = get_node_vars(u, equations, dg, i, jj, k, element)
243
# pull the contravariant vectors and compute the average
244
Ja2_node_jj = get_contravariant_vector(2, contravariant_vectors,
245
i, jj, k, element)
246
# average mapping terms in second coordinate direction,
247
# used as normal vector in the flux computation
248
Ja2_avg = 0.5f0 * (Ja2_node + Ja2_node_jj)
249
# compute the contravariant nonconservative flux in the direction of the
250
# averaged contravariant vector
251
fluxtilde2 = nonconservative_flux(u_node, u_node_jj, Ja2_avg,
252
equations)
253
integral_contribution = integral_contribution +
254
derivative_split[j, jj] * fluxtilde2
255
end
256
257
# z direction
258
for kk in eachnode(dg)
259
u_node_kk = get_node_vars(u, equations, dg, i, j, kk, element)
260
# pull the contravariant vectors and compute the average
261
Ja3_node_kk = get_contravariant_vector(3, contravariant_vectors,
262
i, j, kk, element)
263
# average mapping terms in third coordinate direction,
264
# used as normal vector in the flux computation
265
Ja3_avg = 0.5f0 * (Ja3_node + Ja3_node_kk)
266
# compute the contravariant nonconservative flux in the direction of the
267
# averaged contravariant vector
268
fluxtilde3 = nonconservative_flux(u_node, u_node_kk, Ja3_avg,
269
equations)
270
integral_contribution = integral_contribution +
271
derivative_split[k, kk] * fluxtilde3
272
end
273
274
# The factor 0.5 cancels the factor 2 in the flux differencing form
275
multiply_add_to_node_vars!(du, alpha * 0.5f0, integral_contribution, equations,
276
dg, i, j, k, element)
277
end
278
279
return nothing
280
end
281
282
@inline function flux_differencing_kernel!(du, u, element,
283
::Type{<:Union{StructuredMesh{3},
284
P4estMesh{3},
285
T8codeMesh{3}}},
286
have_nonconservative_terms::True,
287
combine_conservative_and_nonconservative_fluxes::True,
288
equations,
289
volume_flux, dg::DGSEM, cache, alpha = true)
290
@unpack derivative_split = dg.basis
291
@unpack contravariant_vectors = cache.elements
292
293
# Calculate volume integral in one element
294
for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
295
u_node = get_node_vars(u, equations, dg, i, j, k, element)
296
297
# pull the contravariant vectors in each coordinate direction
298
Ja1_node = get_contravariant_vector(1, contravariant_vectors, i, j, k, element)
299
Ja2_node = get_contravariant_vector(2, contravariant_vectors, i, j, k, element)
300
Ja3_node = get_contravariant_vector(3, contravariant_vectors, i, j, k, element)
301
302
# All diagonal entries of `derivative_split` are zero. Thus, we can skip
303
# the computation of the diagonal terms. In addition, we use the symmetry
304
# of the `volume_flux` to save half of the possible two-point flux
305
# computations.
306
307
# x direction
308
for ii in (i + 1):nnodes(dg)
309
u_node_ii = get_node_vars(u, equations, dg, ii, j, k, element)
310
# pull the contravariant vectors and compute the average
311
Ja1_node_ii = get_contravariant_vector(1, contravariant_vectors,
312
ii, j, k, element)
313
# average mapping terms in first coordinate direction,
314
# used as normal vector in the flux computation
315
Ja1_avg = 0.5f0 * (Ja1_node + Ja1_node_ii)
316
# compute the contravariant sharp flux in the direction of the
317
# averaged contravariant vector
318
fluxtilde1_left, fluxtilde1_right = volume_flux(u_node, u_node_ii, Ja1_avg,
319
equations)
320
multiply_add_to_node_vars!(du, alpha * derivative_split[i, ii],
321
fluxtilde1_left,
322
equations, dg, i, j, k, element)
323
multiply_add_to_node_vars!(du, alpha * derivative_split[ii, i],
324
fluxtilde1_right,
325
equations, dg, ii, j, k, element)
326
end
327
328
# y direction
329
for jj in (j + 1):nnodes(dg)
330
u_node_jj = get_node_vars(u, equations, dg, i, jj, k, element)
331
# pull the contravariant vectors and compute the average
332
Ja2_node_jj = get_contravariant_vector(2, contravariant_vectors,
333
i, jj, k, element)
334
# average mapping terms in second coordinate direction,
335
# used as normal vector in the flux computation
336
Ja2_avg = 0.5f0 * (Ja2_node + Ja2_node_jj)
337
# compute the contravariant sharp flux in the direction of the
338
# averaged contravariant vector
339
fluxtilde2_left, fluxtilde2_right = volume_flux(u_node, u_node_jj, Ja2_avg,
340
equations)
341
multiply_add_to_node_vars!(du, alpha * derivative_split[j, jj],
342
fluxtilde2_left,
343
equations, dg, i, j, k, element)
344
multiply_add_to_node_vars!(du, alpha * derivative_split[jj, j],
345
fluxtilde2_right,
346
equations, dg, i, jj, k, element)
347
end
348
349
# z direction
350
for kk in (k + 1):nnodes(dg)
351
u_node_kk = get_node_vars(u, equations, dg, i, j, kk, element)
352
# pull the contravariant vectors and compute the average
353
Ja3_node_kk = get_contravariant_vector(3, contravariant_vectors,
354
i, j, kk, element)
355
# average mapping terms in third coordinate direction,
356
# used as normal vector in the flux computation
357
Ja3_avg = 0.5f0 * (Ja3_node + Ja3_node_kk)
358
# compute the contravariant sharp flux in the direction of the
359
# averaged contravariant vector
360
fluxtilde3_left, fluxtilde3_right = volume_flux(u_node, u_node_kk, Ja3_avg,
361
equations)
362
multiply_add_to_node_vars!(du, alpha * derivative_split[k, kk],
363
fluxtilde3_left,
364
equations, dg, i, j, k, element)
365
multiply_add_to_node_vars!(du, alpha * derivative_split[kk, k],
366
fluxtilde3_right,
367
equations, dg, i, j, kk, element)
368
end
369
end
370
return nothing
371
end
372
373
# Compute the normal flux for the FV method on curvilinear subcells, see
374
# Hennemann, Rueda-Ramírez, Hindenlang, Gassner (2020)
375
# "A provably entropy stable subcell shock capturing approach for high order split form DG for the compressible Euler equations"
376
# [arXiv: 2008.12044v2](https://arxiv.org/pdf/2008.12044)
377
@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R,
378
fstar3_L, fstar3_R, u,
379
::Type{<:Union{StructuredMesh{3}, P4estMesh{3},
380
T8codeMesh{3}}},
381
have_nonconservative_terms::False,
382
equations, volume_flux_fv, dg::DGSEM, element, cache)
383
@unpack contravariant_vectors = cache.elements
384
@unpack weights, derivative_matrix = dg.basis
385
@unpack normal_vectors_1, normal_vectors_2, normal_vectors_3 = cache.normal_vectors
386
387
for k in eachnode(dg), j in eachnode(dg), i in 2:nnodes(dg)
388
u_ll = get_node_vars(u, equations, dg, i - 1, j, k, element)
389
u_rr = get_node_vars(u, equations, dg, i, j, k, element)
390
391
# Fetch precomputed freestream-preserving normal vector
392
# We access i - 1 here since the normal vector for i = 1 is not used and stored
393
normal_direction = get_normal_vector(normal_vectors_1,
394
i - 1, j, k, element)
395
396
# Compute the contravariant flux
397
contravariant_flux = volume_flux_fv(u_ll, u_rr, normal_direction, equations)
398
399
set_node_vars!(fstar1_L, contravariant_flux, equations, dg, i, j, k)
400
set_node_vars!(fstar1_R, contravariant_flux, equations, dg, i, j, k)
401
end
402
403
for k in eachnode(dg), j in 2:nnodes(dg), i in eachnode(dg)
404
u_ll = get_node_vars(u, equations, dg, i, j - 1, k, element)
405
u_rr = get_node_vars(u, equations, dg, i, j, k, element)
406
407
# Fetch precomputed freestream-preserving normal vector
408
# We access j - 1 here since the normal vector for j = 1 is not used and stored
409
normal_direction = get_normal_vector(normal_vectors_2,
410
i, j - 1, k, element)
411
412
# Compute the contravariant flux
413
contravariant_flux = volume_flux_fv(u_ll, u_rr, normal_direction, equations)
414
415
set_node_vars!(fstar2_L, contravariant_flux, equations, dg, i, j, k)
416
set_node_vars!(fstar2_R, contravariant_flux, equations, dg, i, j, k)
417
end
418
419
for k in 2:nnodes(dg), j in eachnode(dg), i in eachnode(dg)
420
u_ll = get_node_vars(u, equations, dg, i, j, k - 1, element)
421
u_rr = get_node_vars(u, equations, dg, i, j, k, element)
422
423
# Fetch precomputed freestream-preserving normal vector
424
# We access k - 1 here since the normal vector for k = 1 is not used and stored
425
normal_direction = get_normal_vector(normal_vectors_3,
426
i, j, k - 1, element)
427
428
# Compute the contravariant flux
429
contravariant_flux = volume_flux_fv(u_ll, u_rr, normal_direction, equations)
430
431
set_node_vars!(fstar3_L, contravariant_flux, equations, dg, i, j, k)
432
set_node_vars!(fstar3_R, contravariant_flux, equations, dg, i, j, k)
433
end
434
435
return nothing
436
end
437
438
@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R,
439
fstar3_L, fstar3_R, u,
440
::Type{<:Union{StructuredMesh{3}, P4estMesh{3},
441
T8codeMesh{3}}},
442
have_nonconservative_terms::True,
443
equations, volume_flux_fv, dg::DGSEM, element, cache)
444
@unpack contravariant_vectors = cache.elements
445
@unpack weights, derivative_matrix = dg.basis
446
@unpack normal_vectors_1, normal_vectors_2, normal_vectors_3 = cache.normal_vectors
447
448
volume_flux, nonconservative_flux = volume_flux_fv
449
450
for k in eachnode(dg), j in eachnode(dg), i in 2:nnodes(dg)
451
u_ll = get_node_vars(u, equations, dg, i - 1, j, k, element)
452
u_rr = get_node_vars(u, equations, dg, i, j, k, element)
453
454
# Fetch precomputed freestream-preserving normal vector
455
# We access i - 1 here since the normal vector for i = 1 is not used and stored
456
normal_direction = get_normal_vector(normal_vectors_1,
457
i - 1, j, k, element)
458
459
# Compute the contravariant conservative flux
460
ftilde = volume_flux(u_ll, u_rr, normal_direction, equations)
461
462
# Compute and add in the nonconservative part
463
# Note the factor 0.5 necessary for the nonconservative fluxes based on
464
# the interpretation of global SBP operators coupled discontinuously via
465
# central fluxes/SATs
466
ftilde_L = ftilde +
467
0.5f0 *
468
nonconservative_flux(u_ll, u_rr, normal_direction, equations)
469
ftilde_R = ftilde +
470
0.5f0 *
471
nonconservative_flux(u_rr, u_ll, normal_direction, equations)
472
473
set_node_vars!(fstar1_L, ftilde_L, equations, dg, i, j, k)
474
set_node_vars!(fstar1_R, ftilde_R, equations, dg, i, j, k)
475
end
476
477
for k in eachnode(dg), j in 2:nnodes(dg), i in eachnode(dg)
478
u_ll = get_node_vars(u, equations, dg, i, j - 1, k, element)
479
u_rr = get_node_vars(u, equations, dg, i, j, k, element)
480
481
# Fetch precomputed freestream-preserving normal vector
482
# We access j - 1 here since the normal vector for j = 1 is not used and stored
483
normal_direction = get_normal_vector(normal_vectors_2,
484
i, j - 1, k, element)
485
486
# Compute the contravariant conservative flux
487
ftilde = volume_flux(u_ll, u_rr, normal_direction, equations)
488
489
# Compute and add in the nonconservative part
490
# Note the factor 0.5 necessary for the nonconservative fluxes based on
491
# the interpretation of global SBP operators coupled discontinuously via
492
# central fluxes/SATs
493
ftilde_L = ftilde +
494
0.5f0 *
495
nonconservative_flux(u_ll, u_rr, normal_direction, equations)
496
ftilde_R = ftilde +
497
0.5f0 *
498
nonconservative_flux(u_rr, u_ll, normal_direction, equations)
499
500
set_node_vars!(fstar2_L, ftilde_L, equations, dg, i, j, k)
501
set_node_vars!(fstar2_R, ftilde_R, equations, dg, i, j, k)
502
end
503
504
for k in 2:nnodes(dg), j in eachnode(dg), i in eachnode(dg)
505
u_ll = get_node_vars(u, equations, dg, i, j, k - 1, element)
506
u_rr = get_node_vars(u, equations, dg, i, j, k, element)
507
508
# Fetch precomputed freestream-preserving normal vector
509
# We access k - 1 here since the normal vector for k = 1 is not used and stored
510
normal_direction = get_normal_vector(normal_vectors_3,
511
i, j, k - 1, element)
512
513
# Compute the contravariant conservative flux
514
ftilde = volume_flux(u_ll, u_rr, normal_direction, equations)
515
516
# Compute and add in the nonconservative part
517
# Note the factor 0.5 necessary for the nonconservative fluxes based on
518
# the interpretation of global SBP operators coupled discontinuously via
519
# central fluxes/SATs
520
ftilde_L = ftilde +
521
0.5f0 *
522
nonconservative_flux(u_ll, u_rr, normal_direction, equations)
523
ftilde_R = ftilde +
524
0.5f0 *
525
nonconservative_flux(u_rr, u_ll, normal_direction, equations)
526
527
set_node_vars!(fstar3_L, ftilde_L, equations, dg, i, j, k)
528
set_node_vars!(fstar3_R, ftilde_R, equations, dg, i, j, k)
529
end
530
531
return nothing
532
end
533
534
@inline function calcflux_fvO2!(fstar1_L, fstar1_R, fstar2_L, fstar2_R,
535
fstar3_L, fstar3_R, u,
536
::Type{<:Union{StructuredMesh{3}, P4estMesh{3},
537
T8codeMesh{3}}},
538
have_nonconservative_terms::False, equations,
539
volume_flux_fv, dg::DGSEM, element, cache,
540
sc_interface_coords, reconstruction_mode, slope_limiter,
541
cons2recon, recon2cons)
542
@unpack normal_vectors_1, normal_vectors_2, normal_vectors_3 = cache.normal_vectors
543
544
for k in eachnode(dg), j in eachnode(dg), i in 2:nnodes(dg)
545
u_ll = cons2recon(get_node_vars(u, equations, dg, max(1, i - 2), j, k,
546
element), equations)
547
u_lr = cons2recon(get_node_vars(u, equations, dg, i - 1, j, k,
548
element), equations)
549
u_rl = cons2recon(get_node_vars(u, equations, dg, i, j, k,
550
element), equations)
551
u_rr = cons2recon(get_node_vars(u, equations, dg, min(nnodes(dg), i + 1), j, k,
552
element), equations)
553
554
u_l, u_r = reconstruction_mode(u_ll, u_lr, u_rl, u_rr,
555
sc_interface_coords, i,
556
slope_limiter, dg)
557
558
normal_direction = get_normal_vector(normal_vectors_1, i - 1, j, k, element)
559
560
contravariant_flux = volume_flux_fv(recon2cons(u_l, equations),
561
recon2cons(u_r, equations),
562
normal_direction, equations)
563
564
set_node_vars!(fstar1_L, contravariant_flux, equations, dg, i, j, k)
565
set_node_vars!(fstar1_R, contravariant_flux, equations, dg, i, j, k)
566
end
567
568
for k in eachnode(dg), j in 2:nnodes(dg), i in eachnode(dg)
569
u_ll = cons2recon(get_node_vars(u, equations, dg, i, max(1, j - 2), k,
570
element), equations)
571
u_lr = cons2recon(get_node_vars(u, equations, dg, i, j - 1, k,
572
element), equations)
573
u_rl = cons2recon(get_node_vars(u, equations, dg, i, j, k,
574
element), equations)
575
u_rr = cons2recon(get_node_vars(u, equations, dg, i, min(nnodes(dg), j + 1), k,
576
element), equations)
577
578
u_l, u_r = reconstruction_mode(u_ll, u_lr, u_rl, u_rr,
579
sc_interface_coords, j,
580
slope_limiter, dg)
581
582
normal_direction = get_normal_vector(normal_vectors_2, i, j - 1, k, element)
583
584
contravariant_flux = volume_flux_fv(recon2cons(u_l, equations),
585
recon2cons(u_r, equations),
586
normal_direction, equations)
587
588
set_node_vars!(fstar2_L, contravariant_flux, equations, dg, i, j, k)
589
set_node_vars!(fstar2_R, contravariant_flux, equations, dg, i, j, k)
590
end
591
592
for k in 2:nnodes(dg), j in eachnode(dg), i in eachnode(dg)
593
u_ll = cons2recon(get_node_vars(u, equations, dg, i, j, max(1, k - 2),
594
element), equations)
595
u_lr = cons2recon(get_node_vars(u, equations, dg, i, j, k - 1,
596
element), equations)
597
u_rl = cons2recon(get_node_vars(u, equations, dg, i, j, k,
598
element), equations)
599
u_rr = cons2recon(get_node_vars(u, equations, dg, i, j, min(nnodes(dg), k + 1),
600
element), equations)
601
602
u_l, u_r = reconstruction_mode(u_ll, u_lr, u_rl, u_rr,
603
sc_interface_coords, k,
604
slope_limiter, dg)
605
606
normal_direction = get_normal_vector(normal_vectors_3, i, j, k - 1, element)
607
608
contravariant_flux = volume_flux_fv(recon2cons(u_l, equations),
609
recon2cons(u_r, equations),
610
normal_direction, equations)
611
612
set_node_vars!(fstar3_L, contravariant_flux, equations, dg, i, j, k)
613
set_node_vars!(fstar3_R, contravariant_flux, equations, dg, i, j, k)
614
end
615
616
return nothing
617
end
618
619
function prolong2interfaces!(cache, u, mesh::StructuredMesh{3}, equations, dg::DG)
620
@unpack interfaces_u = cache.elements
621
622
@threaded for element in eachelement(dg, cache)
623
for j in eachnode(dg), i in eachnode(dg)
624
# Negative x-direction (direction 1, left/negative x face)
625
# Face nodes (i, j) correspond to (y, z) directions
626
for v in eachvariable(equations)
627
interfaces_u[v, i, j, 1, element] = u[v, 1, i, j, element]
628
end
629
# Positive x-direction (direction 2, right/positive x face)
630
for v in eachvariable(equations)
631
interfaces_u[v, i, j, 2, element] = u[v, nnodes(dg), i, j, element]
632
end
633
# Negative y-direction (direction 3, bottom/negative y face)
634
# Face nodes (i, j) correspond to (x, z) directions
635
for v in eachvariable(equations)
636
interfaces_u[v, i, j, 3, element] = u[v, i, 1, j, element]
637
end
638
# Positive y-direction (direction 4, top/positive y face)
639
for v in eachvariable(equations)
640
interfaces_u[v, i, j, 4, element] = u[v, i, nnodes(dg), j, element]
641
end
642
# Negative z-direction (direction 5, back/negative z face)
643
# Face nodes (i, j) correspond to (x, y) directions
644
for v in eachvariable(equations)
645
interfaces_u[v, i, j, 5, element] = u[v, i, j, 1, element]
646
end
647
# Positive z-direction (direction 6, front/positive z face)
648
for v in eachvariable(equations)
649
interfaces_u[v, i, j, 6, element] = u[v, i, j, nnodes(dg), element]
650
end
651
end
652
end
653
654
return nothing
655
end
656
657
function calc_interface_flux!(surface_flux_values, mesh::StructuredMesh{3},
658
have_nonconservative_terms, # can be True/False
659
equations, surface_integral, dg::DG, cache)
660
@unpack elements = cache
661
662
@threaded for element in eachelement(dg, cache)
663
# Interfaces in negative directions
664
# Faster version of "for orientation in (1, 2, 3)"
665
666
# Interfaces in x-direction (`orientation` = 1)
667
calc_interface_flux!(elements.surface_flux_values,
668
elements.left_neighbors[1, element],
669
element, 1, mesh,
670
have_nonconservative_terms, equations,
671
surface_integral, dg, cache)
672
673
# Interfaces in y-direction (`orientation` = 2)
674
calc_interface_flux!(elements.surface_flux_values,
675
elements.left_neighbors[2, element],
676
element, 2, mesh,
677
have_nonconservative_terms, equations,
678
surface_integral, dg, cache)
679
680
# Interfaces in z-direction (`orientation` = 3)
681
calc_interface_flux!(elements.surface_flux_values,
682
elements.left_neighbors[3, element],
683
element, 3, mesh,
684
have_nonconservative_terms, equations,
685
surface_integral, dg, cache)
686
end
687
688
return nothing
689
end
690
691
@inline function calc_interface_flux!(surface_flux_values, left_element, right_element,
692
orientation,
693
mesh::StructuredMesh{3},
694
have_nonconservative_terms::False, equations,
695
surface_integral, dg::DG, cache)
696
# This is slow for LSA, but for some reason faster for Euler (see #519)
697
if left_element <= 0 # left_element = 0 at boundaries
698
return surface_flux_values
699
end
700
701
@unpack surface_flux = surface_integral
702
@unpack interfaces_u, contravariant_vectors, inverse_jacobian = cache.elements
703
704
right_direction = 2 * orientation
705
left_direction = right_direction - 1
706
707
for j in eachnode(dg), i in eachnode(dg)
708
u_ll = get_node_vars(interfaces_u, equations, dg, i, j, right_direction,
709
left_element)
710
u_rr = get_node_vars(interfaces_u, equations, dg, i, j, left_direction,
711
right_element)
712
713
if orientation == 1
714
# If the mapping is orientation-reversing, the contravariant vectors' orientation
715
# is reversed as well. The normal vector must be oriented in the direction
716
# from `left_element` to `right_element`, or the numerical flux will be computed
717
# incorrectly (downwind direction).
718
sign_jacobian = sign(inverse_jacobian[1, i, j, right_element])
719
720
# First contravariant vector Ja^1 as SVector
721
normal_direction = sign_jacobian *
722
get_contravariant_vector(1, contravariant_vectors,
723
1, i, j, right_element)
724
elseif orientation == 2
725
# See above
726
sign_jacobian = sign(inverse_jacobian[i, 1, j, right_element])
727
728
# Second contravariant vector Ja^2 as SVector
729
normal_direction = sign_jacobian *
730
get_contravariant_vector(2, contravariant_vectors,
731
i, 1, j, right_element)
732
else # orientation == 3
733
# See above
734
sign_jacobian = sign(inverse_jacobian[i, j, 1, right_element])
735
736
# Third contravariant vector Ja^3 as SVector
737
normal_direction = sign_jacobian *
738
get_contravariant_vector(3, contravariant_vectors,
739
i, j, 1, right_element)
740
end
741
742
# If the mapping is orientation-reversing, the normal vector will be reversed (see above).
743
# However, the flux now has the wrong sign, since we need the physical flux in normal direction.
744
flux = sign_jacobian * surface_flux(u_ll, u_rr, normal_direction, equations)
745
746
for v in eachvariable(equations)
747
surface_flux_values[v, i, j, right_direction, left_element] = flux[v]
748
surface_flux_values[v, i, j, left_direction, right_element] = flux[v]
749
end
750
end
751
752
return nothing
753
end
754
755
@inline function calc_interface_flux!(surface_flux_values, left_element, right_element,
756
orientation,
757
mesh::StructuredMesh{3},
758
have_nonconservative_terms::True, equations,
759
surface_integral, dg::DG, cache)
760
# See comment on `calc_interface_flux!` with `have_nonconservative_terms::False`
761
if left_element <= 0 # left_element = 0 at boundaries
762
return surface_flux_values
763
end
764
765
surface_flux, nonconservative_flux = surface_integral.surface_flux
766
@unpack interfaces_u, contravariant_vectors, inverse_jacobian = cache.elements
767
768
right_direction = 2 * orientation
769
left_direction = right_direction - 1
770
771
for j in eachnode(dg), i in eachnode(dg)
772
u_ll = get_node_vars(interfaces_u, equations, dg, i, j, right_direction,
773
left_element)
774
u_rr = get_node_vars(interfaces_u, equations, dg, i, j, left_direction,
775
right_element)
776
777
if orientation == 1
778
# If the mapping is orientation-reversing, the contravariant vectors' orientation
779
# is reversed as well. The normal vector must be oriented in the direction
780
# from `left_element` to `right_element`, or the numerical flux will be computed
781
# incorrectly (downwind direction).
782
sign_jacobian = sign(inverse_jacobian[1, i, j, right_element])
783
784
# First contravariant vector Ja^1 as SVector
785
normal_direction = sign_jacobian *
786
get_contravariant_vector(1, contravariant_vectors,
787
1, i, j, right_element)
788
elseif orientation == 2
789
# See above
790
sign_jacobian = sign(inverse_jacobian[i, 1, j, right_element])
791
792
# Second contravariant vector Ja^2 as SVector
793
normal_direction = sign_jacobian *
794
get_contravariant_vector(2, contravariant_vectors,
795
i, 1, j, right_element)
796
else # orientation == 3
797
# See above
798
sign_jacobian = sign(inverse_jacobian[i, j, 1, right_element])
799
800
# Third contravariant vector Ja^3 as SVector
801
normal_direction = sign_jacobian *
802
get_contravariant_vector(3, contravariant_vectors,
803
i, j, 1, right_element)
804
end
805
806
# If the mapping is orientation-reversing, the normal vector will be reversed (see above).
807
# However, the flux now has the wrong sign, since we need the physical flux in normal direction.
808
flux = sign_jacobian * surface_flux(u_ll, u_rr, normal_direction, equations)
809
810
# Compute both nonconservative fluxes
811
# Scale with sign_jacobian to ensure that the normal_direction matches that
812
# from the flux above
813
noncons_left = sign_jacobian *
814
nonconservative_flux(u_ll, u_rr, normal_direction, equations)
815
noncons_right = sign_jacobian *
816
nonconservative_flux(u_rr, u_ll, normal_direction, equations)
817
818
for v in eachvariable(equations)
819
# Note the factor 0.5 necessary for the nonconservative fluxes based on
820
# the interpretation of global SBP operators coupled discontinuously via
821
# central fluxes/SATs
822
surface_flux_values[v, i, j, right_direction, left_element] = flux[v] +
823
0.5f0 *
824
noncons_left[v]
825
surface_flux_values[v, i, j, left_direction, right_element] = flux[v] +
826
0.5f0 *
827
noncons_right[v]
828
end
829
end
830
831
return nothing
832
end
833
834
function calc_boundary_flux!(cache, t, boundary_conditions::NamedTuple,
835
mesh::StructuredMesh{3}, equations, surface_integral,
836
dg::DG)
837
@unpack surface_flux_values = cache.elements
838
linear_indices = LinearIndices(size(mesh))
839
840
for cell_z in axes(mesh, 3), cell_y in axes(mesh, 2)
841
# Negative x-direction
842
direction = 1
843
element = linear_indices[begin, cell_y, cell_z]
844
845
for k in eachnode(dg), j in eachnode(dg)
846
calc_boundary_flux_by_direction!(surface_flux_values, t, 1,
847
boundary_conditions[direction],
848
mesh,
849
have_nonconservative_terms(equations),
850
equations, surface_integral, dg,
851
cache,
852
direction, (1, j, k), (j, k), element)
853
end
854
855
# Positive x-direction
856
direction = 2
857
element = linear_indices[end, cell_y, cell_z]
858
859
for k in eachnode(dg), j in eachnode(dg)
860
calc_boundary_flux_by_direction!(surface_flux_values, t, 1,
861
boundary_conditions[direction],
862
mesh,
863
have_nonconservative_terms(equations),
864
equations, surface_integral, dg,
865
cache,
866
direction, (nnodes(dg), j, k), (j, k),
867
element)
868
end
869
end
870
871
for cell_z in axes(mesh, 3), cell_x in axes(mesh, 1)
872
# Negative y-direction
873
direction = 3
874
element = linear_indices[cell_x, begin, cell_z]
875
876
for k in eachnode(dg), i in eachnode(dg)
877
calc_boundary_flux_by_direction!(surface_flux_values, t, 2,
878
boundary_conditions[direction],
879
mesh,
880
have_nonconservative_terms(equations),
881
equations, surface_integral, dg,
882
cache,
883
direction, (i, 1, k), (i, k), element)
884
end
885
886
# Positive y-direction
887
direction = 4
888
element = linear_indices[cell_x, end, cell_z]
889
890
for k in eachnode(dg), i in eachnode(dg)
891
calc_boundary_flux_by_direction!(surface_flux_values, t, 2,
892
boundary_conditions[direction],
893
mesh,
894
have_nonconservative_terms(equations),
895
equations, surface_integral, dg,
896
cache,
897
direction, (i, nnodes(dg), k), (i, k),
898
element)
899
end
900
end
901
902
for cell_y in axes(mesh, 2), cell_x in axes(mesh, 1)
903
# Negative z-direction
904
direction = 5
905
element = linear_indices[cell_x, cell_y, begin]
906
907
for j in eachnode(dg), i in eachnode(dg)
908
calc_boundary_flux_by_direction!(surface_flux_values, t, 3,
909
boundary_conditions[direction],
910
mesh,
911
have_nonconservative_terms(equations),
912
equations, surface_integral, dg,
913
cache,
914
direction, (i, j, 1), (i, j), element)
915
end
916
917
# Positive z-direction
918
direction = 6
919
element = linear_indices[cell_x, cell_y, end]
920
921
for j in eachnode(dg), i in eachnode(dg)
922
calc_boundary_flux_by_direction!(surface_flux_values, t, 3,
923
boundary_conditions[direction],
924
mesh,
925
have_nonconservative_terms(equations),
926
equations, surface_integral, dg,
927
cache,
928
direction, (i, j, nnodes(dg)), (i, j),
929
element)
930
end
931
end
932
933
return nothing
934
end
935
936
function apply_jacobian!(backend::Nothing, du,
937
mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}},
938
equations, dg::DG, cache)
939
@unpack inverse_jacobian = cache.elements
940
@threaded for element in eachelement(dg, cache)
941
apply_jacobian_per_element!(du, typeof(mesh), equations, dg, inverse_jacobian,
942
element)
943
end
944
return nothing
945
end
946
947
function apply_jacobian!(backend::Backend, du,
948
mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}},
949
equations, dg::DG, cache)
950
@unpack inverse_jacobian = cache.elements
951
952
kernel! = apply_jacobian_KAkernel!(backend)
953
kernel!(du, typeof(mesh), equations, dg, inverse_jacobian,
954
ndrange = nelements(cache.elements))
955
return nothing
956
end
957
958
@kernel function apply_jacobian_KAkernel!(du, MeshT, equations, dg::DG,
959
inverse_jacobian)
960
element = @index(Global)
961
apply_jacobian_per_element!(du, MeshT, equations, dg, inverse_jacobian, element)
962
end
963
964
@inline function apply_jacobian_per_element!(du,
965
::Type{<:Union{StructuredMesh{3},
966
P4estMesh{3},
967
T8codeMesh{3}}},
968
equations, dg, inverse_jacobian, element)
969
for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
970
# Negative sign included to account for the negated surface and volume terms,
971
# see e.g. the computation of `derivative_hat` in the basis setup and
972
# the comment in `calc_surface_integral!`.
973
factor = -inverse_jacobian[i, j, k, element]
974
975
for v in eachvariable(equations)
976
du[v, i, j, k, element] *= factor
977
end
978
end
979
return nothing
980
end
981
end # @muladd
982
983