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_2d.jl
5590 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
function create_cache(mesh::Union{StructuredMesh{2}, UnstructuredMesh2D,
9
P4estMesh{2}, T8codeMesh{2}}, 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 = create_f_threaded(mesh, equations, dg,
14
uEltype)
15
16
normal_vectors = NormalVectorContainer2D(mesh, dg, cache_containers)
17
18
cache_subcell_limiting = create_cache_subcell_limiting(mesh, equations,
19
volume_integral, dg,
20
cache_containers, uEltype)
21
22
return (; fstar1_L_threaded, fstar1_R_threaded,
23
fstar2_L_threaded, fstar2_R_threaded,
24
normal_vectors, cache_subcell_limiting...)
25
end
26
27
#=
28
`weak_form_kernel!` is only implemented for conserved terms as
29
non-conservative terms should always be discretized in conjunction with a flux-splitting scheme,
30
see `flux_differencing_kernel!`.
31
This treatment is required to achieve, e.g., entropy-stability or well-balancedness.
32
See also https://github.com/trixi-framework/Trixi.jl/issues/1671#issuecomment-1765644064
33
=#
34
@inline function weak_form_kernel!(du, u,
35
element,
36
::Type{<:Union{StructuredMesh{2},
37
StructuredMeshView{2},
38
UnstructuredMesh2D, P4estMesh{2},
39
P4estMeshView{2}, T8codeMesh{2}}},
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 j in eachnode(dg), i in eachnode(dg)
48
u_node = get_node_vars(u, equations, dg, i, j, element)
49
50
flux1 = flux(u_node, 1, equations)
51
flux2 = flux(u_node, 2, equations)
52
53
# Compute the contravariant flux by taking the scalar product of the
54
# first contravariant vector Ja^1 and the flux vector
55
Ja11, Ja12 = get_contravariant_vector(1, contravariant_vectors, i, j, element)
56
contravariant_flux1 = Ja11 * flux1 + Ja12 * flux2
57
for ii in eachnode(dg)
58
multiply_add_to_node_vars!(du, alpha * derivative_hat[ii, i],
59
contravariant_flux1, equations, dg,
60
ii, j, element)
61
end
62
63
# Compute the contravariant flux by taking the scalar product of the
64
# second contravariant vector Ja^2 and the flux vector
65
Ja21, Ja22 = get_contravariant_vector(2, contravariant_vectors, i, j, element)
66
contravariant_flux2 = Ja21 * flux1 + Ja22 * flux2
67
for jj in eachnode(dg)
68
multiply_add_to_node_vars!(du, alpha * derivative_hat[jj, j],
69
contravariant_flux2, equations, dg,
70
i, jj, element)
71
end
72
end
73
74
return nothing
75
end
76
77
@inline function flux_differencing_kernel!(du, u, element,
78
::Type{<:Union{StructuredMesh{2},
79
StructuredMeshView{2},
80
UnstructuredMesh2D,
81
P4estMesh{2},
82
T8codeMesh{2}}},
83
have_nonconservative_terms::False, equations,
84
volume_flux, dg::DGSEM, cache, alpha = true)
85
@unpack derivative_split = dg.basis
86
@unpack contravariant_vectors = cache.elements
87
88
# Calculate volume integral in one element
89
for j in eachnode(dg), i in eachnode(dg)
90
u_node = get_node_vars(u, equations, dg, i, j, element)
91
92
# pull the contravariant vectors in each coordinate direction
93
Ja1_node = get_contravariant_vector(1, contravariant_vectors, i, j, element)
94
Ja2_node = get_contravariant_vector(2, contravariant_vectors, i, j, element)
95
96
# All diagonal entries of `derivative_split` are zero. Thus, we can skip
97
# the computation of the diagonal terms. In addition, we use the symmetry
98
# of the `volume_flux` to save half of the possible two-point flux
99
# computations.
100
101
# x direction
102
for ii in (i + 1):nnodes(dg)
103
u_node_ii = get_node_vars(u, equations, dg, ii, j, element)
104
# pull the contravariant vectors and compute the average
105
Ja1_node_ii = get_contravariant_vector(1, contravariant_vectors,
106
ii, j, element)
107
# average mapping terms in first coordinate direction,
108
# used as normal vector in the flux computation
109
Ja1_avg = 0.5f0 * (Ja1_node + Ja1_node_ii)
110
# compute the contravariant sharp flux in the direction of the
111
# averaged contravariant vector
112
fluxtilde1 = volume_flux(u_node, u_node_ii, Ja1_avg, equations)
113
multiply_add_to_node_vars!(du, alpha * derivative_split[i, ii], fluxtilde1,
114
equations, dg, i, j, element)
115
multiply_add_to_node_vars!(du, alpha * derivative_split[ii, i], fluxtilde1,
116
equations, dg, ii, j, element)
117
end
118
119
# y direction
120
for jj in (j + 1):nnodes(dg)
121
u_node_jj = get_node_vars(u, equations, dg, i, jj, element)
122
# pull the contravariant vectors and compute the average
123
Ja2_node_jj = get_contravariant_vector(2, contravariant_vectors,
124
i, jj, element)
125
# average mapping terms in second coordinate direction,
126
# used as normal vector in the flux computation
127
Ja2_avg = 0.5f0 * (Ja2_node + Ja2_node_jj)
128
# compute the contravariant sharp flux in the direction of the
129
# averaged contravariant vector
130
fluxtilde2 = volume_flux(u_node, u_node_jj, Ja2_avg, equations)
131
multiply_add_to_node_vars!(du, alpha * derivative_split[j, jj], fluxtilde2,
132
equations, dg, i, j, element)
133
multiply_add_to_node_vars!(du, alpha * derivative_split[jj, j], fluxtilde2,
134
equations, dg, i, jj, element)
135
end
136
end
137
138
return nothing
139
end
140
141
@inline function flux_differencing_kernel!(du, u, element,
142
MeshT::Type{<:Union{StructuredMesh{2},
143
StructuredMeshView{2},
144
UnstructuredMesh2D,
145
P4estMesh{2},
146
T8codeMesh{2}}},
147
have_nonconservative_terms::True, equations,
148
volume_flux, dg::DGSEM, cache, alpha = true)
149
flux_differencing_kernel!(du, u, element, MeshT, have_nonconservative_terms,
150
combine_conservative_and_nonconservative_fluxes(volume_flux,
151
equations),
152
equations,
153
volume_flux,
154
dg, cache, alpha)
155
return nothing
156
end
157
158
@inline function flux_differencing_kernel!(du, u, element,
159
MeshT::Type{<:Union{StructuredMesh{2},
160
StructuredMeshView{2},
161
UnstructuredMesh2D,
162
P4estMesh{2},
163
T8codeMesh{2}}},
164
have_nonconservative_terms::True,
165
combine_conservative_and_nonconservative_fluxes::False,
166
equations,
167
volume_flux, dg::DGSEM, cache, alpha = true)
168
@unpack derivative_split = dg.basis
169
@unpack contravariant_vectors = cache.elements
170
symmetric_flux, nonconservative_flux = volume_flux
171
172
# Apply the symmetric flux as usual
173
flux_differencing_kernel!(du, u, element, MeshT, False(), equations, symmetric_flux,
174
dg, cache, alpha)
175
176
# Calculate the remaining volume terms using the nonsymmetric generalized flux
177
for j in eachnode(dg), i in eachnode(dg)
178
u_node = get_node_vars(u, equations, dg, i, j, element)
179
180
# pull the contravariant vectors in each coordinate direction
181
Ja1_node = get_contravariant_vector(1, contravariant_vectors, i, j, element)
182
Ja2_node = get_contravariant_vector(2, contravariant_vectors, i, j, element)
183
184
# The diagonal terms are zero since the diagonal of `derivative_split`
185
# is zero. We ignore this for now.
186
# In general, nonconservative fluxes can depend on both the contravariant
187
# vectors (normal direction) at the current node and the averaged ones.
188
# Thus, we need to pass both to the nonconservative flux.
189
190
# x direction
191
integral_contribution = zero(u_node)
192
for ii in eachnode(dg)
193
u_node_ii = get_node_vars(u, equations, dg, ii, j, element)
194
# pull the contravariant vectors and compute the average
195
Ja1_node_ii = get_contravariant_vector(1, contravariant_vectors,
196
ii, j, element)
197
# average mapping terms in first coordinate direction,
198
# used as normal vector in the flux computation
199
Ja1_avg = 0.5f0 * (Ja1_node + Ja1_node_ii)
200
# Compute the contravariant nonconservative flux.
201
fluxtilde1 = nonconservative_flux(u_node, u_node_ii, Ja1_avg,
202
equations)
203
integral_contribution = integral_contribution +
204
derivative_split[i, ii] * fluxtilde1
205
end
206
207
# y direction
208
for jj in eachnode(dg)
209
u_node_jj = get_node_vars(u, equations, dg, i, jj, element)
210
# pull the contravariant vectors and compute the average
211
Ja2_node_jj = get_contravariant_vector(2, contravariant_vectors,
212
i, jj, element)
213
# average mapping terms in second coordinate direction,
214
# used as normal vector in the flux computation
215
Ja2_avg = 0.5f0 * (Ja2_node + Ja2_node_jj)
216
# compute the contravariant nonconservative flux in the direction of the
217
# averaged contravariant vector
218
fluxtilde2 = nonconservative_flux(u_node, u_node_jj, Ja2_avg,
219
equations)
220
integral_contribution = integral_contribution +
221
derivative_split[j, jj] * fluxtilde2
222
end
223
224
# The factor 0.5 cancels the factor 2 in the flux differencing form
225
multiply_add_to_node_vars!(du, alpha * 0.5f0, integral_contribution, equations,
226
dg, i, j, element)
227
end
228
229
return nothing
230
end
231
232
@inline function flux_differencing_kernel!(du, u, element,
233
::Type{<:Union{StructuredMesh{2},
234
StructuredMeshView{2},
235
UnstructuredMesh2D,
236
P4estMesh{2},
237
T8codeMesh{2}}},
238
have_nonconservative_terms::True,
239
combine_conservative_and_nonconservative_fluxes::True,
240
equations,
241
volume_flux, dg::DGSEM, cache, alpha = true)
242
@unpack derivative_split = dg.basis
243
@unpack contravariant_vectors = cache.elements
244
245
# Calculate volume integral in one element
246
for j in eachnode(dg), i in eachnode(dg)
247
u_node = get_node_vars(u, equations, dg, i, j, element)
248
249
# pull the contravariant vectors in each coordinate direction
250
Ja1_node = get_contravariant_vector(1, contravariant_vectors, i, j, element)
251
Ja2_node = get_contravariant_vector(2, contravariant_vectors, i, j, element)
252
253
# All diagonal entries of `derivative_split` are zero. Thus, we can skip
254
# the computation of the diagonal terms. In addition, we use the symmetry
255
# of the `volume_flux` to save half of the possible two-point flux
256
# computations.
257
258
# x direction
259
for ii in (i + 1):nnodes(dg)
260
u_node_ii = get_node_vars(u, equations, dg, ii, j, element)
261
# pull the contravariant vectors and compute the average
262
Ja1_node_ii = get_contravariant_vector(1, contravariant_vectors,
263
ii, j, element)
264
# average mapping terms in first coordinate direction,
265
# used as normal vector in the flux computation
266
Ja1_avg = 0.5f0 * (Ja1_node + Ja1_node_ii)
267
# compute the contravariant sharp flux in the direction of the
268
# averaged contravariant vector
269
fluxtilde1_left, fluxtilde1_right = volume_flux(u_node, u_node_ii, Ja1_avg,
270
equations)
271
multiply_add_to_node_vars!(du, alpha * derivative_split[i, ii],
272
fluxtilde1_left,
273
equations, dg, i, j, element)
274
multiply_add_to_node_vars!(du, alpha * derivative_split[ii, i],
275
fluxtilde1_right,
276
equations, dg, ii, j, element)
277
end
278
279
# y direction
280
for jj in (j + 1):nnodes(dg)
281
u_node_jj = get_node_vars(u, equations, dg, i, jj, element)
282
# pull the contravariant vectors and compute the average
283
Ja2_node_jj = get_contravariant_vector(2, contravariant_vectors,
284
i, jj, element)
285
# average mapping terms in second coordinate direction,
286
# used as normal vector in the flux computation
287
Ja2_avg = 0.5f0 * (Ja2_node + Ja2_node_jj)
288
# compute the contravariant sharp flux in the direction of the
289
# averaged contravariant vector
290
fluxtilde2_left, fluxtilde2_right = volume_flux(u_node, u_node_jj, Ja2_avg,
291
equations)
292
multiply_add_to_node_vars!(du, alpha * derivative_split[j, jj],
293
fluxtilde2_left,
294
equations, dg, i, j, element)
295
multiply_add_to_node_vars!(du, alpha * derivative_split[jj, j],
296
fluxtilde2_right,
297
equations, dg, i, jj, element)
298
end
299
end
300
301
return nothing
302
end
303
304
@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, u,
305
::Type{<:Union{StructuredMesh{2}, StructuredMeshView{2},
306
UnstructuredMesh2D,
307
P4estMesh{2}, T8codeMesh{2}}},
308
have_nonconservative_terms::False, equations,
309
volume_flux_fv, dg::DGSEM, element, cache)
310
@unpack normal_vectors_1, normal_vectors_2 = cache.normal_vectors
311
312
for j in eachnode(dg), i in 2:nnodes(dg)
313
u_ll = get_node_vars(u, equations, dg, i - 1, j, element)
314
u_rr = get_node_vars(u, equations, dg, i, j, element)
315
316
# Fetch precomputed freestream-preserving normal vector
317
# We access i - 1 here since the normal vector for i = 1 is not used and stored
318
normal_direction = get_normal_vector(normal_vectors_1, i - 1, j, element)
319
320
# Compute the contravariant flux
321
contravariant_flux = volume_flux_fv(u_ll, u_rr, normal_direction, equations)
322
323
set_node_vars!(fstar1_L, contravariant_flux, equations, dg, i, j)
324
set_node_vars!(fstar1_R, contravariant_flux, equations, dg, i, j)
325
end
326
327
for j in 2:nnodes(dg), i in eachnode(dg)
328
u_ll = get_node_vars(u, equations, dg, i, j - 1, element)
329
u_rr = get_node_vars(u, equations, dg, i, j, element)
330
331
# Fetch precomputed freestream-preserving normal vector
332
# We access j - 1 here since the normal vector for j = 1 is not used and stored
333
normal_direction = get_normal_vector(normal_vectors_2, i, j - 1, element)
334
335
# Compute the contravariant flux by taking the scalar product of the
336
# normal vector and the flux vector
337
contravariant_flux = volume_flux_fv(u_ll, u_rr, normal_direction, equations)
338
339
set_node_vars!(fstar2_L, contravariant_flux, equations, dg, i, j)
340
set_node_vars!(fstar2_R, contravariant_flux, equations, dg, i, j)
341
end
342
343
return nothing
344
end
345
346
@inline function calcflux_fvO2!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, u,
347
::Type{<:Union{StructuredMesh{2}, StructuredMeshView{2},
348
UnstructuredMesh2D,
349
P4estMesh{2}, T8codeMesh{2}}},
350
have_nonconservative_terms::False, equations,
351
volume_flux_fv, dg::DGSEM, element, cache,
352
sc_interface_coords, reconstruction_mode, slope_limiter,
353
cons2recon, recon2cons)
354
@unpack normal_vectors_1, normal_vectors_2 = cache.normal_vectors
355
356
# We compute FV02 fluxes at the (nnodes(dg) - 1) subcell boundaries
357
# See `calcflux_fvO2!` in solvers/dgsem_tree/dg_1d.jl for a schematic
358
359
# The left subcell node values are labelled `_ll` (left-left) and `_lr` (left-right), while
360
# the right subcell node values are labelled `_rl` (right-left) and `_rr` (right-right).
361
for j in eachnode(dg), i in 2:nnodes(dg)
362
## Obtain unlimited values in reconstruction variables ##
363
364
# Note: If i - 2 = 0 we do not go to neighbor element, as one would do in a finite volume scheme.
365
# Here, we keep it purely cell-local, thus overshoots between elements are not strictly ruled out,
366
# **unless** `reconstruction_mode` is set to `reconstruction_O2_inner`
367
u_ll = cons2recon(get_node_vars(u, equations, dg, max(1, i - 2), j, element),
368
equations)
369
u_lr = cons2recon(get_node_vars(u, equations, dg, i - 1, j, element),
370
equations)
371
u_rl = cons2recon(get_node_vars(u, equations, dg, i, j, element),
372
equations)
373
# Note: If i + 1 > nnodes(dg) we do not go to neighbor element, as one would do in a finite volume scheme.
374
# Here, we keep it purely cell-local, thus overshoots between elements are not strictly ruled out,
375
# **unless** `reconstruction_mode` is set to `reconstruction_O2_inner`
376
u_rr = cons2recon(get_node_vars(u, equations, dg, min(nnodes(dg), i + 1), j,
377
element), equations)
378
379
## Reconstruct values at interfaces with limiting ##
380
u_l, u_r = reconstruction_mode(u_ll, u_lr, u_rl, u_rr,
381
sc_interface_coords, i,
382
slope_limiter, dg)
383
384
# Fetch precomputed freestream-preserving normal vector
385
# We access i - 1 here since the normal vector for i = 1 is not used and stored
386
normal_direction = get_normal_vector(normal_vectors_1, i - 1, j, element)
387
388
# Compute the contravariant flux by taking the scalar product of the
389
# normal vector and the flux vector.
390
## Convert reconstruction variables back to conservative variables ##
391
contravariant_flux = volume_flux_fv(recon2cons(u_l, equations),
392
recon2cons(u_r, equations),
393
normal_direction, equations)
394
395
set_node_vars!(fstar1_L, contravariant_flux, equations, dg, i, j)
396
set_node_vars!(fstar1_R, contravariant_flux, equations, dg, i, j)
397
end
398
399
for j in 2:nnodes(dg), i in eachnode(dg)
400
u_ll = cons2recon(get_node_vars(u, equations, dg, i, max(1, j - 2), element),
401
equations)
402
u_lr = cons2recon(get_node_vars(u, equations, dg, i, j - 1, element),
403
equations)
404
u_rl = cons2recon(get_node_vars(u, equations, dg, i, j, element),
405
equations)
406
u_rr = cons2recon(get_node_vars(u, equations, dg, i, min(nnodes(dg), j + 1),
407
element), equations)
408
409
u_l, u_r = reconstruction_mode(u_ll, u_lr, u_rl, u_rr,
410
sc_interface_coords, j,
411
slope_limiter, dg)
412
413
# We access j - 1 here since the normal vector for j = 1 is not used and stored
414
normal_direction = get_normal_vector(normal_vectors_2, i, j - 1, element)
415
416
contravariant_flux = volume_flux_fv(recon2cons(u_l, equations),
417
recon2cons(u_r, equations),
418
normal_direction, equations)
419
420
set_node_vars!(fstar2_L, contravariant_flux, equations, dg, i, j)
421
set_node_vars!(fstar2_R, contravariant_flux, equations, dg, i, j)
422
end
423
424
return nothing
425
end
426
427
@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, u,
428
::Type{<:Union{StructuredMesh{2}, StructuredMesh{2},
429
UnstructuredMesh2D,
430
P4estMesh{2}, T8codeMesh{2}}},
431
have_nonconservative_terms::True, equations,
432
volume_flux_fv, dg::DGSEM, element, cache)
433
@unpack normal_vectors_1, normal_vectors_2 = cache.normal_vectors
434
435
volume_flux, nonconservative_flux = volume_flux_fv
436
437
# Fluxes in x-direction
438
for j in eachnode(dg), i in 2:nnodes(dg)
439
u_ll = get_node_vars(u, equations, dg, i - 1, j, element)
440
u_rr = get_node_vars(u, equations, dg, i, j, element)
441
442
# Fetch precomputed freestream-preserving normal vector
443
# We access i - 1 here since the normal vector for i = 1 is not used and stored
444
normal_direction = get_normal_vector(normal_vectors_1, i - 1, j, element)
445
446
# Compute the conservative part of the contravariant flux
447
ftilde1 = volume_flux(u_ll, u_rr, normal_direction, equations)
448
449
# Compute and add in the nonconservative part
450
# Note the factor 0.5 necessary for the nonconservative fluxes based on
451
# the interpretation of global SBP operators coupled discontinuously via
452
# central fluxes/SATs
453
ftilde1_L = ftilde1 +
454
0.5f0 *
455
nonconservative_flux(u_ll, u_rr, normal_direction, equations)
456
ftilde1_R = ftilde1 +
457
0.5f0 *
458
nonconservative_flux(u_rr, u_ll, normal_direction, equations)
459
460
set_node_vars!(fstar1_L, ftilde1_L, equations, dg, i, j)
461
set_node_vars!(fstar1_R, ftilde1_R, equations, dg, i, j)
462
end
463
464
# Fluxes in y-direction
465
for j in 2:nnodes(dg), i in eachnode(dg)
466
u_ll = get_node_vars(u, equations, dg, i, j - 1, element)
467
u_rr = get_node_vars(u, equations, dg, i, j, element)
468
469
# Fetch precomputed freestream-preserving normal vector
470
# We access j - 1 here since the normal vector for j = 1 is not used and stored
471
normal_direction = get_normal_vector(normal_vectors_2, i, j - 1, element)
472
473
# Compute the conservative part of the contravariant flux
474
ftilde2 = volume_flux(u_ll, u_rr, normal_direction, equations)
475
476
# Compute and add in the nonconservative part
477
# Note the factor 0.5 necessary for the nonconservative fluxes based on
478
# the interpretation of global SBP operators coupled discontinuously via
479
# central fluxes/SATs
480
ftilde2_L = ftilde2 +
481
0.5f0 *
482
nonconservative_flux(u_ll, u_rr, normal_direction, equations)
483
ftilde2_R = ftilde2 +
484
0.5f0 *
485
nonconservative_flux(u_rr, u_ll, normal_direction, equations)
486
487
set_node_vars!(fstar2_L, ftilde2_L, equations, dg, i, j)
488
set_node_vars!(fstar2_R, ftilde2_R, equations, dg, i, j)
489
end
490
491
return nothing
492
end
493
494
function prolong2interfaces!(cache, u,
495
mesh::Union{StructuredMesh{2}, StructuredMeshView{2}},
496
equations, dg::DG)
497
@unpack interfaces_u = cache.elements
498
499
@threaded for element in eachelement(dg, cache)
500
for i in eachnode(dg)
501
# Negative x-direction (direction 1, left/negative x face)
502
for v in eachvariable(equations)
503
interfaces_u[v, i, 1, element] = u[v, 1, i, element]
504
end
505
# Positive x-direction (direction 2, right/positive x face)
506
for v in eachvariable(equations)
507
interfaces_u[v, i, 2, element] = u[v, nnodes(dg), i, element]
508
end
509
# Negative y-direction (direction 3, bottom/negative y face)
510
for v in eachvariable(equations)
511
interfaces_u[v, i, 3, element] = u[v, i, 1, element]
512
end
513
# Positive y-direction (direction 4, top/positive y face)
514
for v in eachvariable(equations)
515
interfaces_u[v, i, 4, element] = u[v, i, nnodes(dg), element]
516
end
517
end
518
end
519
520
return nothing
521
end
522
523
function calc_interface_flux!(surface_flux_values,
524
mesh::Union{StructuredMesh{2}, StructuredMeshView{2}},
525
have_nonconservative_terms, # can be True/False
526
equations, surface_integral, dg::DG, cache)
527
@unpack elements = cache
528
529
@threaded for element in eachelement(dg, cache)
530
# Interfaces in negative directions
531
# Faster version of "for orientation in (1, 2)"
532
533
# Interfaces in x-direction (`orientation` = 1)
534
calc_interface_flux!(elements.surface_flux_values,
535
elements.left_neighbors[1, element],
536
element, 1, mesh,
537
have_nonconservative_terms, equations,
538
surface_integral, dg, cache)
539
540
# Interfaces in y-direction (`orientation` = 2)
541
calc_interface_flux!(elements.surface_flux_values,
542
elements.left_neighbors[2, element],
543
element, 2, mesh,
544
have_nonconservative_terms, equations,
545
surface_integral, dg, cache)
546
end
547
548
return nothing
549
end
550
551
@inline function calc_interface_flux!(surface_flux_values, left_element, right_element,
552
orientation,
553
mesh::Union{StructuredMesh{2},
554
StructuredMeshView{2}},
555
have_nonconservative_terms::False, equations,
556
surface_integral, dg::DG, cache)
557
# This is slow for LSA, but for some reason faster for Euler (see #519)
558
if left_element <= 0 # left_element = 0 at boundaries
559
return nothing
560
end
561
562
@unpack surface_flux = surface_integral
563
@unpack interfaces_u, contravariant_vectors, inverse_jacobian = cache.elements
564
565
right_direction = 2 * orientation
566
left_direction = right_direction - 1
567
568
for i in eachnode(dg)
569
u_ll = get_node_vars(interfaces_u, equations, dg, i, right_direction,
570
left_element)
571
u_rr = get_node_vars(interfaces_u, equations, dg, i, left_direction,
572
right_element)
573
574
if orientation == 1
575
# If the mapping is orientation-reversing, the contravariant vectors' orientation
576
# is reversed as well. The normal vector must be oriented in the direction
577
# from `left_element` to `right_element`, or the numerical flux will be computed
578
# incorrectly (downwind direction).
579
sign_jacobian = sign(inverse_jacobian[1, i, right_element])
580
581
# First contravariant vector Ja^1 as SVector
582
normal_direction = sign_jacobian *
583
get_contravariant_vector(1, contravariant_vectors,
584
1, i, right_element)
585
else # orientation == 2
586
# See above
587
sign_jacobian = sign(inverse_jacobian[i, 1, right_element])
588
589
# Second contravariant vector Ja^2 as SVector
590
normal_direction = sign_jacobian *
591
get_contravariant_vector(2, contravariant_vectors,
592
i, 1, right_element)
593
end
594
595
# If the mapping is orientation-reversing, the normal vector will be reversed (see above).
596
# However, the flux now has the wrong sign, since we need the physical flux in normal direction.
597
flux = sign_jacobian * surface_flux(u_ll, u_rr, normal_direction, equations)
598
599
for v in eachvariable(equations)
600
surface_flux_values[v, i, right_direction, left_element] = flux[v]
601
surface_flux_values[v, i, left_direction, right_element] = flux[v]
602
end
603
end
604
605
return nothing
606
end
607
608
@inline function calc_interface_flux!(surface_flux_values, left_element, right_element,
609
orientation,
610
mesh::Union{StructuredMesh{2},
611
StructuredMeshView{2}},
612
have_nonconservative_terms::True, equations,
613
surface_integral, dg::DG, cache)
614
# See comment on `calc_interface_flux!` with `have_nonconservative_terms::False`
615
if left_element <= 0 # left_element = 0 at boundaries
616
return nothing
617
end
618
619
surface_flux, nonconservative_flux = surface_integral.surface_flux
620
@unpack interfaces_u, contravariant_vectors, inverse_jacobian = cache.elements
621
622
right_direction = 2 * orientation
623
left_direction = right_direction - 1
624
625
for i in eachnode(dg)
626
u_ll = get_node_vars(interfaces_u, equations, dg, i, right_direction,
627
left_element)
628
u_rr = get_node_vars(interfaces_u, equations, dg, i, left_direction,
629
right_element)
630
631
if orientation == 1
632
# If the mapping is orientation-reversing, the contravariant vectors' orientation
633
# is reversed as well. The normal vector must be oriented in the direction
634
# from `left_element` to `right_element`, or the numerical flux will be computed
635
# incorrectly (downwind direction).
636
sign_jacobian = sign(inverse_jacobian[1, i, right_element])
637
638
# First contravariant vector Ja^1 as SVector
639
normal_direction = sign_jacobian *
640
get_contravariant_vector(1, contravariant_vectors,
641
1, i, right_element)
642
else # orientation == 2
643
# See above
644
sign_jacobian = sign(inverse_jacobian[i, 1, right_element])
645
646
# Second contravariant vector Ja^2 as SVector
647
normal_direction = sign_jacobian *
648
get_contravariant_vector(2, contravariant_vectors,
649
i, 1, right_element)
650
end
651
652
# If the mapping is orientation-reversing, the normal vector will be reversed (see above).
653
# However, the flux now has the wrong sign, since we need the physical flux in normal direction.
654
flux = sign_jacobian * surface_flux(u_ll, u_rr, normal_direction, equations)
655
656
# Compute both nonconservative fluxes
657
# Scale with sign_jacobian to ensure that the normal_direction matches that
658
# from the flux above
659
noncons_left = sign_jacobian *
660
nonconservative_flux(u_ll, u_rr, normal_direction, equations)
661
noncons_right = sign_jacobian *
662
nonconservative_flux(u_rr, u_ll, normal_direction, equations)
663
664
for v in eachvariable(equations)
665
# Note the factor 0.5 necessary for the nonconservative fluxes based on
666
# the interpretation of global SBP operators coupled discontinuously via
667
# central fluxes/SATs
668
surface_flux_values[v, i, right_direction, left_element] = flux[v] +
669
0.5f0 *
670
noncons_left[v]
671
surface_flux_values[v, i, left_direction, right_element] = flux[v] +
672
0.5f0 *
673
noncons_right[v]
674
end
675
end
676
677
return nothing
678
end
679
680
function calc_boundary_flux!(cache, t, boundary_conditions::NamedTuple,
681
mesh::Union{StructuredMesh{2}, StructuredMeshView{2}},
682
equations, surface_integral,
683
dg::DG)
684
@unpack surface_flux_values = cache.elements
685
linear_indices = LinearIndices(size(mesh))
686
687
for cell_y in axes(mesh, 2)
688
# Negative x-direction
689
direction = 1
690
element = linear_indices[begin, cell_y]
691
692
for j in eachnode(dg)
693
calc_boundary_flux_by_direction!(surface_flux_values, t, 1,
694
boundary_conditions[direction],
695
mesh,
696
have_nonconservative_terms(equations),
697
equations, surface_integral, dg,
698
cache,
699
direction, (1, j), (j,), element)
700
end
701
702
# Positive x-direction
703
direction = 2
704
element = linear_indices[end, cell_y]
705
706
for j in eachnode(dg)
707
calc_boundary_flux_by_direction!(surface_flux_values, t, 1,
708
boundary_conditions[direction],
709
mesh,
710
have_nonconservative_terms(equations),
711
equations, surface_integral, dg,
712
cache,
713
direction, (nnodes(dg), j), (j,), element)
714
end
715
end
716
717
for cell_x in axes(mesh, 1)
718
# Negative y-direction
719
direction = 3
720
element = linear_indices[cell_x, begin]
721
722
for i in eachnode(dg)
723
calc_boundary_flux_by_direction!(surface_flux_values, t, 2,
724
boundary_conditions[direction],
725
mesh,
726
have_nonconservative_terms(equations),
727
equations, surface_integral, dg,
728
cache,
729
direction, (i, 1), (i,), element)
730
end
731
732
# Positive y-direction
733
direction = 4
734
element = linear_indices[cell_x, end]
735
736
for i in eachnode(dg)
737
calc_boundary_flux_by_direction!(surface_flux_values, t, 2,
738
boundary_conditions[direction],
739
mesh,
740
have_nonconservative_terms(equations),
741
equations, surface_integral, dg,
742
cache,
743
direction, (i, nnodes(dg)), (i,), element)
744
end
745
end
746
747
return nothing
748
end
749
750
function apply_jacobian!(backend::Nothing, du,
751
mesh::Union{StructuredMesh{2}, StructuredMeshView{2},
752
UnstructuredMesh2D, P4estMesh{2}, P4estMeshView{2},
753
T8codeMesh{2}},
754
equations, dg::DG, cache)
755
@unpack inverse_jacobian = cache.elements
756
@threaded for element in eachelement(dg, cache)
757
apply_jacobian_per_element!(du, typeof(mesh), equations, dg, inverse_jacobian,
758
element)
759
end
760
end
761
762
function apply_jacobian!(backend::Backend, du,
763
mesh::Union{StructuredMesh{2}, StructuredMeshView{2},
764
UnstructuredMesh2D, P4estMesh{2}, P4estMeshView{2},
765
T8codeMesh{2}},
766
equations, dg::DG, cache)
767
nelements(dg, cache) == 0 && return nothing
768
@unpack inverse_jacobian = cache.elements
769
kernel! = apply_jacobian_KAkernel!(backend)
770
kernel!(du, typeof(mesh), equations, dg, inverse_jacobian,
771
ndrange = nelements(dg, cache))
772
end
773
774
@kernel function apply_jacobian_KAkernel!(du,
775
MeshT::Type{<:Union{StructuredMesh{2},
776
StructuredMeshView{2},
777
UnstructuredMesh2D,
778
P4estMesh{2},
779
P4estMeshView{2},
780
T8codeMesh{2}}},
781
equations, dg::DG, inverse_jacobian)
782
element = @index(Global)
783
apply_jacobian_per_element!(du, MeshT, equations, dg, inverse_jacobian, element)
784
end
785
786
@inline function apply_jacobian_per_element!(du,
787
::Type{<:Union{StructuredMesh{2},
788
StructuredMeshView{2},
789
UnstructuredMesh2D,
790
P4estMesh{2},
791
P4estMeshView{2},
792
T8codeMesh{2}}},
793
equations, dg::DG, inverse_jacobian,
794
element)
795
for j in eachnode(dg), i in eachnode(dg)
796
# Negative sign included to account for the negated surface and volume terms,
797
# see e.g. the computation of `derivative_hat` in the basis setup and
798
# the comment in `calc_surface_integral!`.
799
factor = -inverse_jacobian[i, j, element]
800
801
for v in eachvariable(equations)
802
du[v, i, j, element] *= factor
803
end
804
end
805
return nothing
806
end
807
end # @muladd
808
809