Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/solvers/dgsem_p4est/dg_3d.jl
5616 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
# The methods below are specialized on the mortar type
9
# and called from the basic `create_cache` method at the top.
10
function create_cache(mesh::Union{P4estMesh{3}, T8codeMesh{3}}, equations,
11
mortar_l2::LobattoLegendreMortarL2, uEltype)
12
A4d = Array{uEltype, 4}
13
fstar_primary_threaded = A4d[A4d(undef, nvariables(equations),
14
nnodes(mortar_l2), nnodes(mortar_l2), 4)
15
for _ in 1:Threads.maxthreadid()] |> VecOfArrays
16
fstar_secondary_threaded = A4d[A4d(undef, nvariables(equations),
17
nnodes(mortar_l2), nnodes(mortar_l2), 4)
18
for _ in 1:Threads.maxthreadid()] |> VecOfArrays
19
20
A3d = Array{uEltype, 3}
21
fstar_tmp_threaded = A3d[A3d(undef, nvariables(equations),
22
nnodes(mortar_l2), nnodes(mortar_l2))
23
for _ in 1:Threads.maxthreadid()] |> VecOfArrays
24
u_threaded = A3d[A3d(undef, nvariables(equations),
25
nnodes(mortar_l2), nnodes(mortar_l2))
26
for _ in 1:Threads.maxthreadid()] |> VecOfArrays
27
28
return (; fstar_primary_threaded, fstar_secondary_threaded, fstar_tmp_threaded,
29
u_threaded)
30
end
31
32
# index_to_start_step_3d(index::Symbol, index_range)
33
#
34
# Given a symbolic `index` and an `indexrange` (usually `eachnode(dg)`),
35
# return `index_start, index_step_i, index_step_j`, i.e., a tuple containing
36
# - `index_start`, an index value to begin a loop
37
# - `index_step_i`, an index step to update during an `i` loop
38
# - `index_step_j`, an index step to update during a `j` loop
39
# The resulting indices translate surface indices to volume indices.
40
#
41
# !!! warning
42
# This assumes that loops using the return values are written as
43
#
44
# i_volume_start, i_volume_step_i, i_volume_step_j = index_to_start_step_3d(symbolic_index_i, index_range)
45
# j_volume_start, j_volume_step_i, j_volume_step_j = index_to_start_step_3d(symbolic_index_j, index_range)
46
# k_volume_start, k_volume_step_i, k_volume_step_j = index_to_start_step_3d(symbolic_index_k, index_range)
47
#
48
# i_volume, j_volume, k_volume = i_volume_start, j_volume_start, k_volume_start
49
# for j_surface in index_range
50
# for i_surface in index_range
51
# # do stuff with `(i_surface, j_surface)` and `(i_volume, j_volume, k_volume)`
52
#
53
# i_volume += i_volume_step_i
54
# j_volume += j_volume_step_i
55
# k_volume += k_volume_step_i
56
# end
57
# i_volume += i_volume_step_j
58
# j_volume += j_volume_step_j
59
# k_volume += k_volume_step_j
60
# end
61
@inline function index_to_start_step_3d(index::Symbol, index_range)
62
index_begin = first(index_range)
63
index_end = last(index_range)
64
65
if index === :begin
66
return index_begin, 0, 0
67
elseif index === :end
68
return index_end, 0, 0
69
elseif index === :i_forward
70
return index_begin, 1, index_begin - index_end - 1
71
elseif index === :i_backward
72
return index_end, -1, index_end + 1 - index_begin
73
elseif index === :j_forward
74
return index_begin, 0, 1
75
else # if index === :j_backward
76
return index_end, 0, -1
77
end
78
end
79
80
# Extract the two varying indices from a symbolic index tuple.
81
# For example, `surface_indices((:i_forward, :end, :j_forward)) == (:i_forward, :j_forward)`.
82
@inline function surface_indices(indices::NTuple{3, Symbol})
83
i1, i2, i3 = indices
84
index = i1
85
(index === :begin || index === :end) && return (i2, i3)
86
87
index = i2
88
(index === :begin || index === :end) && return (i1, i3)
89
90
# i3 in (:begin, :end)
91
return (i1, i2)
92
end
93
94
function prolong2interfaces!(backend::Nothing, cache, u,
95
mesh::Union{P4estMesh{3}, T8codeMesh{3}},
96
equations, dg::DG)
97
@unpack interfaces = cache
98
@unpack neighbor_ids, node_indices = cache.interfaces
99
index_range = eachnode(dg)
100
101
@threaded for interface in eachinterface(dg, cache)
102
prolong2interfaces_per_interface!(interfaces.u, u, typeof(mesh), equations,
103
neighbor_ids, node_indices, index_range,
104
interface)
105
end
106
return nothing
107
end
108
109
function prolong2interfaces!(backend::Backend, cache, u,
110
mesh::Union{P4estMesh{3}, T8codeMesh{3}},
111
equations, dg::DG)
112
@unpack interfaces = cache
113
@unpack neighbor_ids, node_indices = cache.interfaces
114
index_range = eachnode(dg)
115
116
kernel! = prolong2interfaces_KAkernel!(backend)
117
kernel!(interfaces.u, u, typeof(mesh), equations, neighbor_ids, node_indices,
118
index_range,
119
ndrange = ninterfaces(interfaces))
120
return nothing
121
end
122
123
@kernel function prolong2interfaces_KAkernel!(interface_u, u, MeshT, equations,
124
neighbor_ids, node_indices, index_range)
125
interface = @index(Global)
126
prolong2interfaces_per_interface!(interface_u, u, MeshT, equations, neighbor_ids,
127
node_indices, index_range, interface)
128
end
129
130
@inline function prolong2interfaces_per_interface!(u_interface, u,
131
::Type{<:Union{P4estMesh{3},
132
T8codeMesh{3}}},
133
equations, neighbor_ids,
134
node_indices,
135
index_range, interface)
136
# Copy solution data from the primary element using "delayed indexing" with
137
# a start value and two step sizes to get the correct face and orientation.
138
# Note that in the current implementation, the interface will be
139
# "aligned at the primary element", i.e., the indices of the primary side
140
# will always run forwards.
141
primary_element = neighbor_ids[1, interface]
142
primary_indices = node_indices[1, interface]
143
144
i_primary_start, i_primary_step_i, i_primary_step_j = index_to_start_step_3d(primary_indices[1],
145
index_range)
146
j_primary_start, j_primary_step_i, j_primary_step_j = index_to_start_step_3d(primary_indices[2],
147
index_range)
148
k_primary_start, k_primary_step_i, k_primary_step_j = index_to_start_step_3d(primary_indices[3],
149
index_range)
150
151
i_primary = i_primary_start
152
j_primary = j_primary_start
153
k_primary = k_primary_start
154
for j in index_range
155
for i in index_range
156
for v in eachvariable(equations)
157
u_interface[1, v, i, j, interface] = u[v,
158
i_primary, j_primary,
159
k_primary,
160
primary_element]
161
end
162
i_primary += i_primary_step_i
163
j_primary += j_primary_step_i
164
k_primary += k_primary_step_i
165
end
166
i_primary += i_primary_step_j
167
j_primary += j_primary_step_j
168
k_primary += k_primary_step_j
169
end
170
171
# Copy solution data from the secondary element using "delayed indexing" with
172
# a start value and two step sizes to get the correct face and orientation.
173
secondary_element = neighbor_ids[2, interface]
174
secondary_indices = node_indices[2, interface]
175
176
i_secondary_start, i_secondary_step_i, i_secondary_step_j = index_to_start_step_3d(secondary_indices[1],
177
index_range)
178
j_secondary_start, j_secondary_step_i, j_secondary_step_j = index_to_start_step_3d(secondary_indices[2],
179
index_range)
180
k_secondary_start, k_secondary_step_i, k_secondary_step_j = index_to_start_step_3d(secondary_indices[3],
181
index_range)
182
183
i_secondary = i_secondary_start
184
j_secondary = j_secondary_start
185
k_secondary = k_secondary_start
186
for j in index_range
187
for i in index_range
188
for v in eachvariable(equations)
189
u_interface[2, v, i, j, interface] = u[v,
190
i_secondary, j_secondary,
191
k_secondary,
192
secondary_element]
193
end
194
i_secondary += i_secondary_step_i
195
j_secondary += j_secondary_step_i
196
k_secondary += k_secondary_step_i
197
end
198
i_secondary += i_secondary_step_j
199
j_secondary += j_secondary_step_j
200
k_secondary += k_secondary_step_j
201
end
202
return nothing
203
end
204
205
function calc_interface_flux!(backend::Nothing, surface_flux_values,
206
mesh::Union{P4estMesh{3}, T8codeMesh{3}},
207
have_nonconservative_terms,
208
equations, surface_integral, dg::DG, cache)
209
@unpack neighbor_ids, node_indices = cache.interfaces
210
@unpack contravariant_vectors = cache.elements
211
index_range = eachnode(dg)
212
213
@threaded for interface in eachinterface(dg, cache)
214
calc_interface_flux_per_interface!(surface_flux_values,
215
typeof(mesh),
216
have_nonconservative_terms,
217
equations, surface_integral, typeof(dg),
218
cache.interfaces.u, neighbor_ids,
219
node_indices,
220
contravariant_vectors, index_range,
221
interface)
222
end
223
return nothing
224
end
225
226
function calc_interface_flux!(backend::Backend, surface_flux_values,
227
mesh::Union{P4estMesh{3}, T8codeMesh{3}},
228
have_nonconservative_terms,
229
equations, surface_integral, dg::DG, cache)
230
@unpack neighbor_ids, node_indices = cache.interfaces
231
@unpack contravariant_vectors = cache.elements
232
index_range = eachnode(dg)
233
234
kernel! = calc_interface_flux_KAkernel!(backend)
235
kernel!(surface_flux_values, typeof(mesh), have_nonconservative_terms, equations,
236
surface_integral, typeof(dg), cache.interfaces.u,
237
neighbor_ids, node_indices, contravariant_vectors, index_range,
238
ndrange = ninterfaces(cache.interfaces))
239
return nothing
240
end
241
242
@kernel function calc_interface_flux_KAkernel!(surface_flux_values, MeshT,
243
have_nonconservative_terms, equations,
244
surface_integral, SolverT, u_interface,
245
neighbor_ids, node_indices,
246
contravariant_vectors, index_range)
247
interface = @index(Global)
248
calc_interface_flux_per_interface!(surface_flux_values,
249
MeshT,
250
have_nonconservative_terms,
251
equations, surface_integral, SolverT,
252
u_interface,
253
neighbor_ids, node_indices,
254
contravariant_vectors,
255
index_range, interface)
256
end
257
258
@inline function calc_interface_flux_per_interface!(surface_flux_values,
259
MeshT::Type{<:Union{P4estMesh{3},
260
T8codeMesh{3}}},
261
have_nonconservative_terms,
262
equations, surface_integral,
263
SolverT::Type{<:DG}, u_interface,
264
neighbor_ids,
265
node_indices, contravariant_vectors,
266
index_range, interface)
267
# Get element and side information on the primary element
268
primary_element = neighbor_ids[1, interface]
269
primary_indices = node_indices[1, interface]
270
primary_direction = indices2direction(primary_indices)
271
272
i_primary_start, i_primary_step_i, i_primary_step_j = index_to_start_step_3d(primary_indices[1],
273
index_range)
274
j_primary_start, j_primary_step_i, j_primary_step_j = index_to_start_step_3d(primary_indices[2],
275
index_range)
276
k_primary_start, k_primary_step_i, k_primary_step_j = index_to_start_step_3d(primary_indices[3],
277
index_range)
278
279
i_primary = i_primary_start
280
j_primary = j_primary_start
281
k_primary = k_primary_start
282
283
# Get element and side information on the secondary element
284
secondary_element = neighbor_ids[2, interface]
285
secondary_indices = node_indices[2, interface]
286
secondary_direction = indices2direction(secondary_indices)
287
secondary_surface_indices = surface_indices(secondary_indices)
288
289
# Get the surface indexing on the secondary element.
290
# Note that the indices of the primary side will always run forward but
291
# the secondary indices might need to run backwards for flipped sides.
292
i_secondary_start, i_secondary_step_i, i_secondary_step_j = index_to_start_step_3d(secondary_surface_indices[1],
293
index_range)
294
j_secondary_start, j_secondary_step_i, j_secondary_step_j = index_to_start_step_3d(secondary_surface_indices[2],
295
index_range)
296
i_secondary = i_secondary_start
297
j_secondary = j_secondary_start
298
299
for j in index_range
300
for i in index_range
301
# Get the normal direction from the primary element.
302
# Note, contravariant vectors at interfaces in negative coordinate direction
303
# are pointing inwards. This is handled by `get_normal_direction`.
304
normal_direction = get_normal_direction(primary_direction,
305
contravariant_vectors,
306
i_primary, j_primary, k_primary,
307
primary_element)
308
309
calc_interface_flux!(surface_flux_values, MeshT, have_nonconservative_terms,
310
equations,
311
surface_integral, SolverT, u_interface,
312
interface, normal_direction,
313
i, j, primary_direction, primary_element,
314
i_secondary, j_secondary, secondary_direction,
315
secondary_element)
316
317
# Increment the primary element indices
318
i_primary += i_primary_step_i
319
j_primary += j_primary_step_i
320
k_primary += k_primary_step_i
321
# Increment the secondary element surface indices
322
i_secondary += i_secondary_step_i
323
j_secondary += j_secondary_step_i
324
end
325
# Increment the primary element indices
326
i_primary += i_primary_step_j
327
j_primary += j_primary_step_j
328
k_primary += k_primary_step_j
329
# Increment the secondary element surface indices
330
i_secondary += i_secondary_step_j
331
j_secondary += j_secondary_step_j
332
end
333
return nothing
334
end
335
336
# Inlined function for interface flux computation for conservative flux terms
337
@inline function calc_interface_flux!(surface_flux_values,
338
::Type{<:Union{P4estMesh{3}, T8codeMesh{3}}},
339
have_nonconservative_terms::False, equations,
340
surface_integral, SolverT::Type{<:DG},
341
u_interface,
342
interface_index, normal_direction,
343
primary_i_node_index, primary_j_node_index,
344
primary_direction_index, primary_element_index,
345
secondary_i_node_index, secondary_j_node_index,
346
secondary_direction_index,
347
secondary_element_index)
348
@unpack surface_flux = surface_integral
349
350
u_ll, u_rr = get_surface_node_vars(u_interface, equations, SolverT,
351
primary_i_node_index,
352
primary_j_node_index, interface_index)
353
354
flux_ = surface_flux(u_ll, u_rr, normal_direction, equations)
355
356
for v in eachvariable(equations)
357
surface_flux_values[v, primary_i_node_index, primary_j_node_index,
358
primary_direction_index, primary_element_index] = flux_[v]
359
surface_flux_values[v, secondary_i_node_index, secondary_j_node_index,
360
secondary_direction_index, secondary_element_index] = -flux_[v]
361
end
362
363
return nothing
364
end
365
366
# Inlined function for interface flux computation for flux + nonconservative terms
367
@inline function calc_interface_flux!(surface_flux_values,
368
MeshT::Type{<:Union{P4estMesh{3}, T8codeMesh{3}}},
369
have_nonconservative_terms::True, equations,
370
surface_integral, SolverT::Type{<:DG},
371
u_interface,
372
interface_index, normal_direction,
373
primary_i_node_index, primary_j_node_index,
374
primary_direction_index, primary_element_index,
375
secondary_i_node_index, secondary_j_node_index,
376
secondary_direction_index,
377
secondary_element_index)
378
calc_interface_flux!(surface_flux_values, MeshT, have_nonconservative_terms,
379
combine_conservative_and_nonconservative_fluxes(surface_integral.surface_flux,
380
equations),
381
equations, surface_integral, SolverT, u_interface,
382
interface_index,
383
normal_direction, primary_i_node_index, primary_j_node_index,
384
primary_direction_index, primary_element_index,
385
secondary_i_node_index, secondary_j_node_index,
386
secondary_direction_index, secondary_element_index)
387
return nothing
388
end
389
390
@inline function calc_interface_flux!(surface_flux_values,
391
::Type{<:Union{P4estMesh{3}, T8codeMesh{3}}},
392
have_nonconservative_terms::True,
393
combine_conservative_and_nonconservative_fluxes::False,
394
equations,
395
surface_integral, SolverT::Type{<:DG},
396
u_interface,
397
interface_index, normal_direction,
398
primary_i_node_index, primary_j_node_index,
399
primary_direction_index, primary_element_index,
400
secondary_i_node_index, secondary_j_node_index,
401
secondary_direction_index,
402
secondary_element_index)
403
surface_flux, nonconservative_flux = surface_integral.surface_flux
404
u_ll, u_rr = get_surface_node_vars(u_interface, equations, SolverT,
405
primary_i_node_index,
406
primary_j_node_index, interface_index)
407
408
flux_ = surface_flux(u_ll, u_rr, normal_direction, equations)
409
410
# Compute both nonconservative fluxes
411
noncons_primary = nonconservative_flux(u_ll, u_rr, normal_direction, equations)
412
noncons_secondary = nonconservative_flux(u_rr, u_ll, normal_direction, equations)
413
414
# Store the flux with nonconservative terms on the primary and secondary elements
415
for v in eachvariable(equations)
416
# Note the factor 0.5 necessary for the nonconservative fluxes based on
417
# the interpretation of global SBP operators coupled discontinuously via
418
# central fluxes/SATs
419
surface_flux_values[v, primary_i_node_index, primary_j_node_index,
420
primary_direction_index, primary_element_index] = flux_[v] +
421
0.5f0 * noncons_primary[v]
422
surface_flux_values[v, secondary_i_node_index, secondary_j_node_index,
423
secondary_direction_index, secondary_element_index] = -(flux_[v] +
424
0.5f0 *
425
noncons_secondary[v])
426
end
427
428
return nothing
429
end
430
431
@inline function calc_interface_flux!(surface_flux_values,
432
::Type{<:Union{P4estMesh{3}, T8codeMesh{3}}},
433
have_nonconservative_terms::True,
434
combine_conservative_and_nonconservative_fluxes::True,
435
equations,
436
surface_integral, SolverT::Type{<:DG},
437
u_interface,
438
interface_index, normal_direction,
439
primary_i_node_index, primary_j_node_index,
440
primary_direction_index, primary_element_index,
441
secondary_i_node_index, secondary_j_node_index,
442
secondary_direction_index,
443
secondary_element_index)
444
@unpack surface_flux = surface_integral
445
u_ll, u_rr = get_surface_node_vars(u_interface, equations, SolverT,
446
primary_i_node_index, primary_j_node_index,
447
interface_index)
448
449
flux_left, flux_right = surface_flux(u_ll, u_rr, normal_direction, equations)
450
451
# Store the flux with nonconservative terms on the primary and secondary elements
452
for v in eachvariable(equations)
453
surface_flux_values[v, primary_i_node_index, primary_j_node_index,
454
primary_direction_index, primary_element_index] = flux_left[v]
455
surface_flux_values[v, secondary_i_node_index, secondary_j_node_index,
456
secondary_direction_index, secondary_element_index] = -flux_right[v]
457
end
458
459
return nothing
460
end
461
462
function prolong2boundaries!(cache, u,
463
mesh::Union{P4estMesh{3}, T8codeMesh{3}},
464
equations, dg::DG)
465
@unpack boundaries = cache
466
index_range = eachnode(dg)
467
468
@threaded for boundary in eachboundary(dg, cache)
469
# Copy solution data from the element using "delayed indexing" with
470
# a start value and two step sizes to get the correct face and orientation.
471
element = boundaries.neighbor_ids[boundary]
472
node_indices = boundaries.node_indices[boundary]
473
474
i_node_start, i_node_step_i, i_node_step_j = index_to_start_step_3d(node_indices[1],
475
index_range)
476
j_node_start, j_node_step_i, j_node_step_j = index_to_start_step_3d(node_indices[2],
477
index_range)
478
k_node_start, k_node_step_i, k_node_step_j = index_to_start_step_3d(node_indices[3],
479
index_range)
480
481
i_node = i_node_start
482
j_node = j_node_start
483
k_node = k_node_start
484
for j in eachnode(dg)
485
for i in eachnode(dg)
486
for v in eachvariable(equations)
487
boundaries.u[v, i, j, boundary] = u[v, i_node, j_node, k_node,
488
element]
489
end
490
i_node += i_node_step_i
491
j_node += j_node_step_i
492
k_node += k_node_step_i
493
end
494
i_node += i_node_step_j
495
j_node += j_node_step_j
496
k_node += k_node_step_j
497
end
498
end
499
500
return nothing
501
end
502
503
function calc_boundary_flux!(cache, t, boundary_condition::BC, boundary_indexing,
504
mesh::Union{P4estMesh{3}, T8codeMesh{3}},
505
equations, surface_integral, dg::DG) where {BC}
506
@unpack boundaries = cache
507
@unpack surface_flux_values = cache.elements
508
index_range = eachnode(dg)
509
510
@threaded for local_index in eachindex(boundary_indexing)
511
# Use the local index to get the global boundary index from the
512
# pre-sorted list
513
boundary = boundary_indexing[local_index]
514
515
# Get information on the adjacent element, compute the surface fluxes,
516
# and store them
517
element = boundaries.neighbor_ids[boundary]
518
node_indices = boundaries.node_indices[boundary]
519
direction = indices2direction(node_indices)
520
521
i_node_start, i_node_step_i, i_node_step_j = index_to_start_step_3d(node_indices[1],
522
index_range)
523
j_node_start, j_node_step_i, j_node_step_j = index_to_start_step_3d(node_indices[2],
524
index_range)
525
k_node_start, k_node_step_i, k_node_step_j = index_to_start_step_3d(node_indices[3],
526
index_range)
527
528
i_node = i_node_start
529
j_node = j_node_start
530
k_node = k_node_start
531
for j in eachnode(dg)
532
for i in eachnode(dg)
533
calc_boundary_flux!(surface_flux_values, t, boundary_condition, mesh,
534
have_nonconservative_terms(equations), equations,
535
surface_integral, dg, cache, i_node, j_node, k_node,
536
i, j, direction, element, boundary)
537
i_node += i_node_step_i
538
j_node += j_node_step_i
539
k_node += k_node_step_i
540
end
541
i_node += i_node_step_j
542
j_node += j_node_step_j
543
k_node += k_node_step_j
544
end
545
end
546
547
return nothing
548
end
549
550
# inlined version of the boundary flux calculation along a physical interface
551
@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,
552
mesh::Union{P4estMesh{3}, T8codeMesh{3}},
553
have_nonconservative_terms::False, equations,
554
surface_integral, dg::DG, cache, i_index, j_index,
555
k_index, i_node_index, j_node_index,
556
direction_index,
557
element_index, boundary_index)
558
@unpack boundaries = cache
559
@unpack node_coordinates, contravariant_vectors = cache.elements
560
@unpack surface_flux = surface_integral
561
562
# Extract solution data from boundary container
563
u_inner = get_node_vars(boundaries.u, equations, dg, i_node_index, j_node_index,
564
boundary_index)
565
566
# Outward-pointing normal direction (not normalized)
567
normal_direction = get_normal_direction(direction_index, contravariant_vectors,
568
i_index, j_index, k_index, element_index)
569
570
# Coordinates at boundary node
571
x = get_node_coords(node_coordinates, equations, dg,
572
i_index, j_index, k_index, element_index)
573
574
flux_ = boundary_condition(u_inner, normal_direction, x, t,
575
surface_flux, equations)
576
577
# Copy flux to element storage in the correct orientation
578
for v in eachvariable(equations)
579
surface_flux_values[v, i_node_index, j_node_index,
580
direction_index, element_index] = flux_[v]
581
end
582
583
return nothing
584
end
585
586
# inlined version of the boundary flux calculation along a physical interface
587
@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,
588
mesh::Union{P4estMesh{3}, T8codeMesh{3}},
589
have_nonconservative_terms::True, equations,
590
surface_integral, dg::DG, cache, i_index, j_index,
591
k_index, i_node_index, j_node_index,
592
direction_index,
593
element_index, boundary_index)
594
calc_boundary_flux!(surface_flux_values, t, boundary_condition, mesh,
595
have_nonconservative_terms,
596
combine_conservative_and_nonconservative_fluxes(surface_integral.surface_flux,
597
equations),
598
equations,
599
surface_integral, dg, cache,
600
i_index, j_index, k_index, i_node_index, j_node_index,
601
direction_index, element_index, boundary_index)
602
return nothing
603
end
604
605
@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,
606
mesh::Union{P4estMesh{3}, T8codeMesh{3}},
607
have_nonconservative_terms::True,
608
combine_conservative_and_nonconservative_fluxes::False,
609
equations,
610
surface_integral, dg::DG, cache, i_index, j_index,
611
k_index, i_node_index, j_node_index,
612
direction_index,
613
element_index, boundary_index)
614
@unpack boundaries = cache
615
@unpack node_coordinates, contravariant_vectors = cache.elements
616
@unpack surface_flux = surface_integral
617
618
# Extract solution data from boundary container
619
u_inner = get_node_vars(boundaries.u, equations, dg, i_node_index, j_node_index,
620
boundary_index)
621
622
# Outward-pointing normal direction (not normalized)
623
normal_direction = get_normal_direction(direction_index, contravariant_vectors,
624
i_index, j_index, k_index, element_index)
625
626
# Coordinates at boundary node
627
x = get_node_coords(node_coordinates, equations, dg,
628
i_index, j_index, k_index, element_index)
629
630
# Call pointwise numerical flux functions for the conservative and nonconservative part
631
# in the normal direction on the boundary
632
flux, noncons_flux = boundary_condition(u_inner, normal_direction, x, t,
633
surface_flux, equations)
634
635
# Copy flux to element storage in the correct orientation
636
for v in eachvariable(equations)
637
# Note the factor 0.5 necessary for the nonconservative fluxes based on
638
# the interpretation of global SBP operators coupled discontinuously via
639
# central fluxes/SATs
640
surface_flux_values[v, i_node_index, j_node_index,
641
direction_index, element_index] = flux[v] + 0.5f0 *
642
noncons_flux[v]
643
end
644
645
return nothing
646
end
647
648
@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,
649
mesh::Union{P4estMesh{3}, T8codeMesh{3}},
650
have_nonconservative_terms::True,
651
combine_conservative_and_nonconservative_fluxes::True,
652
equations,
653
surface_integral, dg::DG, cache, i_index, j_index,
654
k_index, i_node_index, j_node_index,
655
direction_index,
656
element_index, boundary_index)
657
@unpack boundaries = cache
658
@unpack node_coordinates, contravariant_vectors = cache.elements
659
@unpack surface_flux = surface_integral
660
661
# Extract solution data from boundary container
662
u_inner = get_node_vars(boundaries.u, equations, dg, i_node_index, j_node_index,
663
boundary_index)
664
665
# Outward-pointing normal direction (not normalized)
666
normal_direction = get_normal_direction(direction_index, contravariant_vectors,
667
i_index, j_index, k_index, element_index)
668
669
# Coordinates at boundary node
670
x = get_node_coords(node_coordinates, equations, dg,
671
i_index, j_index, k_index, element_index)
672
673
# Call pointwise numerical flux functions for the conservative and nonconservative part
674
# in the normal direction on the boundary
675
flux = boundary_condition(u_inner, normal_direction, x, t,
676
surface_flux, equations)
677
678
# Copy flux to element storage in the correct orientation
679
for v in eachvariable(equations)
680
surface_flux_values[v, i_node_index, j_node_index,
681
direction_index, element_index] = flux[v]
682
end
683
684
return nothing
685
end
686
687
function prolong2mortars!(cache, u,
688
mesh::Union{P4estMesh{3}, T8codeMesh{3}}, equations,
689
mortar_l2::LobattoLegendreMortarL2,
690
dg::DGSEM)
691
@unpack fstar_tmp_threaded = cache
692
@unpack neighbor_ids, node_indices = cache.mortars
693
index_range = eachnode(dg)
694
695
@threaded for mortar in eachmortar(dg, cache)
696
# Copy solution data from the small elements using "delayed indexing" with
697
# a start value and two step sizes to get the correct face and orientation.
698
small_indices = node_indices[1, mortar]
699
700
i_small_start, i_small_step_i, i_small_step_j = index_to_start_step_3d(small_indices[1],
701
index_range)
702
j_small_start, j_small_step_i, j_small_step_j = index_to_start_step_3d(small_indices[2],
703
index_range)
704
k_small_start, k_small_step_i, k_small_step_j = index_to_start_step_3d(small_indices[3],
705
index_range)
706
707
for position in 1:4
708
i_small = i_small_start
709
j_small = j_small_start
710
k_small = k_small_start
711
element = neighbor_ids[position, mortar]
712
for j in eachnode(dg)
713
for i in eachnode(dg)
714
for v in eachvariable(equations)
715
cache.mortars.u[1, v, position, i, j, mortar] = u[v, i_small,
716
j_small,
717
k_small,
718
element]
719
end
720
i_small += i_small_step_i
721
j_small += j_small_step_i
722
k_small += k_small_step_i
723
end
724
i_small += i_small_step_j
725
j_small += j_small_step_j
726
k_small += k_small_step_j
727
end
728
end
729
730
# Buffer to copy solution values of the large element in the correct orientation
731
# before interpolating
732
u_buffer = cache.u_threaded[Threads.threadid()]
733
# temporary buffer for projections
734
fstar_tmp = fstar_tmp_threaded[Threads.threadid()]
735
736
# Copy solution of large element face to buffer in the
737
# correct orientation
738
large_indices = node_indices[2, mortar]
739
740
i_large_start, i_large_step_i, i_large_step_j = index_to_start_step_3d(large_indices[1],
741
index_range)
742
j_large_start, j_large_step_i, j_large_step_j = index_to_start_step_3d(large_indices[2],
743
index_range)
744
k_large_start, k_large_step_i, k_large_step_j = index_to_start_step_3d(large_indices[3],
745
index_range)
746
747
i_large = i_large_start
748
j_large = j_large_start
749
k_large = k_large_start
750
element = neighbor_ids[5, mortar]
751
for j in eachnode(dg)
752
for i in eachnode(dg)
753
for v in eachvariable(equations)
754
u_buffer[v, i, j] = u[v, i_large, j_large, k_large, element]
755
end
756
i_large += i_large_step_i
757
j_large += j_large_step_i
758
k_large += k_large_step_i
759
end
760
i_large += i_large_step_j
761
j_large += j_large_step_j
762
k_large += k_large_step_j
763
end
764
765
# Interpolate large element face data from buffer to small face locations
766
multiply_dimensionwise!(view(cache.mortars.u, 2, :, 1, :, :, mortar),
767
mortar_l2.forward_lower,
768
mortar_l2.forward_lower,
769
u_buffer,
770
fstar_tmp)
771
multiply_dimensionwise!(view(cache.mortars.u, 2, :, 2, :, :, mortar),
772
mortar_l2.forward_upper,
773
mortar_l2.forward_lower,
774
u_buffer,
775
fstar_tmp)
776
multiply_dimensionwise!(view(cache.mortars.u, 2, :, 3, :, :, mortar),
777
mortar_l2.forward_lower,
778
mortar_l2.forward_upper,
779
u_buffer,
780
fstar_tmp)
781
multiply_dimensionwise!(view(cache.mortars.u, 2, :, 4, :, :, mortar),
782
mortar_l2.forward_upper,
783
mortar_l2.forward_upper,
784
u_buffer,
785
fstar_tmp)
786
end
787
788
return nothing
789
end
790
791
function calc_mortar_flux!(surface_flux_values,
792
mesh::Union{P4estMesh{3}, T8codeMesh{3}},
793
have_nonconservative_terms, equations,
794
mortar_l2::LobattoLegendreMortarL2,
795
surface_integral, dg::DG, cache)
796
@unpack neighbor_ids, node_indices = cache.mortars
797
@unpack contravariant_vectors = cache.elements
798
@unpack fstar_primary_threaded, fstar_secondary_threaded, fstar_tmp_threaded = cache
799
index_range = eachnode(dg)
800
801
@threaded for mortar in eachmortar(dg, cache)
802
# Choose thread-specific pre-allocated container
803
fstar_primary = fstar_primary_threaded[Threads.threadid()]
804
fstar_secondary = fstar_secondary_threaded[Threads.threadid()]
805
fstar_tmp = fstar_tmp_threaded[Threads.threadid()]
806
807
# Get index information on the small elements
808
small_indices = node_indices[1, mortar]
809
small_direction = indices2direction(small_indices)
810
811
i_small_start, i_small_step_i, i_small_step_j = index_to_start_step_3d(small_indices[1],
812
index_range)
813
j_small_start, j_small_step_i, j_small_step_j = index_to_start_step_3d(small_indices[2],
814
index_range)
815
k_small_start, k_small_step_i, k_small_step_j = index_to_start_step_3d(small_indices[3],
816
index_range)
817
818
for position in 1:4
819
i_small = i_small_start
820
j_small = j_small_start
821
k_small = k_small_start
822
element = neighbor_ids[position, mortar]
823
for j in eachnode(dg)
824
for i in eachnode(dg)
825
# Get the normal direction on the small element.
826
# Note, contravariant vectors at interfaces in negative coordinate direction
827
# are pointing inwards. This is handled by `get_normal_direction`.
828
normal_direction = get_normal_direction(small_direction,
829
contravariant_vectors,
830
i_small, j_small, k_small,
831
element)
832
833
calc_mortar_flux!(fstar_primary, fstar_secondary, mesh,
834
have_nonconservative_terms, equations,
835
surface_integral, dg, cache,
836
mortar, position, normal_direction,
837
i, j)
838
839
i_small += i_small_step_i
840
j_small += j_small_step_i
841
k_small += k_small_step_i
842
end
843
i_small += i_small_step_j
844
j_small += j_small_step_j
845
k_small += k_small_step_j
846
end
847
end
848
849
# Buffer to interpolate flux values of the large element to before
850
# copying in the correct orientation
851
u_buffer = cache.u_threaded[Threads.threadid()]
852
853
# in calc_interface_flux!, the interface flux is computed once over each
854
# interface using the normal from the "primary" element. The result is then
855
# passed back to the "secondary" element, flipping the sign to account for the
856
# change in the normal direction. For mortars, this sign flip occurs in
857
# "mortar_fluxes_to_elements!" instead.
858
mortar_fluxes_to_elements!(surface_flux_values,
859
mesh, equations, mortar_l2, dg, cache,
860
mortar, fstar_primary, fstar_secondary, u_buffer,
861
fstar_tmp)
862
end
863
864
return nothing
865
end
866
867
# Inlined version of the mortar flux computation on small elements for conservation fluxes
868
@inline function calc_mortar_flux!(fstar_primary, fstar_secondary,
869
mesh::Union{P4estMesh{3}, T8codeMesh{3}},
870
have_nonconservative_terms::False, equations,
871
surface_integral, dg::DG, cache,
872
mortar_index, position_index, normal_direction,
873
i_node_index, j_node_index)
874
@unpack u = cache.mortars
875
@unpack surface_flux = surface_integral
876
877
u_ll, u_rr = get_surface_node_vars(u, equations, dg, position_index,
878
i_node_index, j_node_index, mortar_index)
879
880
flux = surface_flux(u_ll, u_rr, normal_direction, equations)
881
882
# Copy flux to buffer
883
set_node_vars!(fstar_primary, flux, equations, dg,
884
i_node_index, j_node_index, position_index)
885
set_node_vars!(fstar_secondary, flux, equations, dg,
886
i_node_index, j_node_index, position_index)
887
888
return nothing
889
end
890
891
# Inlined version of the mortar flux computation on small elements for conservation fluxes
892
# with nonconservative terms
893
@inline function calc_mortar_flux!(fstar_primary, fstar_secondary,
894
mesh::Union{P4estMesh{3}, T8codeMesh{3}},
895
have_nonconservative_terms::True, equations,
896
surface_integral, dg::DG, cache,
897
mortar_index, position_index, normal_direction,
898
i_node_index, j_node_index)
899
@unpack u = cache.mortars
900
surface_flux, nonconservative_flux = surface_integral.surface_flux
901
902
u_ll, u_rr = get_surface_node_vars(u, equations, dg, position_index, i_node_index,
903
j_node_index, mortar_index)
904
905
# Compute conservative flux
906
flux = surface_flux(u_ll, u_rr, normal_direction, equations)
907
908
# Compute nonconservative flux and add it to the flux scaled by a factor of 0.5 based on
909
# the interpretation of global SBP operators coupled discontinuously via
910
# central fluxes/SATs
911
noncons_primary = nonconservative_flux(u_ll, u_rr, normal_direction, equations)
912
noncons_secondary = nonconservative_flux(u_rr, u_ll, normal_direction, equations)
913
flux_plus_noncons_primary = flux + 0.5f0 * noncons_primary
914
flux_plus_noncons_secondary = flux + 0.5f0 * noncons_secondary
915
916
# Copy to buffer
917
set_node_vars!(fstar_primary, flux_plus_noncons_primary, equations, dg,
918
i_node_index, j_node_index, position_index)
919
set_node_vars!(fstar_secondary, flux_plus_noncons_secondary, equations, dg,
920
i_node_index, j_node_index, position_index)
921
922
return nothing
923
end
924
925
@inline function mortar_fluxes_to_elements!(surface_flux_values,
926
mesh::Union{P4estMesh{3}, T8codeMesh{3}},
927
equations,
928
mortar_l2::LobattoLegendreMortarL2,
929
dg::DGSEM, cache, mortar, fstar_primary,
930
fstar_secondary, u_buffer, fstar_tmp)
931
@unpack neighbor_ids, node_indices = cache.mortars
932
index_range = eachnode(dg)
933
934
# Copy solution small to small
935
small_indices = node_indices[1, mortar]
936
small_direction = indices2direction(small_indices)
937
938
for position in 1:4
939
element = neighbor_ids[position, mortar]
940
for j in eachnode(dg), i in eachnode(dg)
941
for v in eachvariable(equations)
942
surface_flux_values[v, i, j, small_direction, element] = fstar_primary[v,
943
i,
944
j,
945
position]
946
end
947
end
948
end
949
950
# Project small fluxes to large element.
951
multiply_dimensionwise!(u_buffer,
952
mortar_l2.reverse_lower, mortar_l2.reverse_lower,
953
view(fstar_secondary, .., 1),
954
fstar_tmp)
955
add_multiply_dimensionwise!(u_buffer,
956
mortar_l2.reverse_upper, mortar_l2.reverse_lower,
957
view(fstar_secondary, .., 2),
958
fstar_tmp)
959
add_multiply_dimensionwise!(u_buffer,
960
mortar_l2.reverse_lower, mortar_l2.reverse_upper,
961
view(fstar_secondary, .., 3),
962
fstar_tmp)
963
add_multiply_dimensionwise!(u_buffer,
964
mortar_l2.reverse_upper, mortar_l2.reverse_upper,
965
view(fstar_secondary, .., 4),
966
fstar_tmp)
967
968
# The flux is calculated in the outward direction of the small elements,
969
# so the sign must be switched to get the flux in outward direction
970
# of the large element.
971
# The contravariant vectors of the large element (and therefore the normal
972
# vectors of the large element as well) are four times as large as the
973
# contravariant vectors of the small elements. Therefore, the flux needs
974
# to be scaled by a factor of 4 to obtain the flux of the large element.
975
u_buffer .*= -4
976
977
# Copy interpolated flux values from buffer to large element face in the
978
# correct orientation.
979
# Note that the index of the small sides will always run forward but
980
# the index of the large side might need to run backwards for flipped sides.
981
large_element = neighbor_ids[5, mortar]
982
large_indices = node_indices[2, mortar]
983
large_direction = indices2direction(large_indices)
984
large_surface_indices = surface_indices(large_indices)
985
986
i_large_start, i_large_step_i, i_large_step_j = index_to_start_step_3d(large_surface_indices[1],
987
index_range)
988
j_large_start, j_large_step_i, j_large_step_j = index_to_start_step_3d(large_surface_indices[2],
989
index_range)
990
991
# Note that the indices of the small sides will always run forward but
992
# the large indices might need to run backwards for flipped sides.
993
i_large = i_large_start
994
j_large = j_large_start
995
for j in eachnode(dg)
996
for i in eachnode(dg)
997
for v in eachvariable(equations)
998
surface_flux_values[v, i_large, j_large, large_direction, large_element] = u_buffer[v,
999
i,
1000
j]
1001
end
1002
i_large += i_large_step_i
1003
j_large += j_large_step_i
1004
end
1005
i_large += i_large_step_j
1006
j_large += j_large_step_j
1007
end
1008
1009
return nothing
1010
end
1011
1012
function calc_surface_integral!(backend::Nothing, du, u,
1013
mesh::Union{P4estMesh{3}, T8codeMesh{3}},
1014
equations, surface_integral::SurfaceIntegralWeakForm,
1015
dg::DGSEM, cache)
1016
@unpack inverse_weights = dg.basis
1017
@unpack surface_flux_values = cache.elements
1018
1019
@threaded for element in eachelement(dg, cache)
1020
calc_surface_integral_per_element!(du, typeof(mesh),
1021
equations, surface_integral,
1022
dg, inverse_weights[1],
1023
surface_flux_values,
1024
element)
1025
end
1026
return nothing
1027
end
1028
1029
function calc_surface_integral!(backend::Backend, du, u,
1030
mesh::Union{P4estMesh{3}, T8codeMesh{3}},
1031
equations,
1032
surface_integral::SurfaceIntegralWeakForm,
1033
dg::DGSEM, cache)
1034
@unpack inverse_weights = dg.basis
1035
@unpack surface_flux_values = cache.elements
1036
1037
kernel! = calc_surface_integral_KAkernel!(backend)
1038
kernel!(du, typeof(mesh), equations, surface_integral, dg, inverse_weights[1],
1039
surface_flux_values, ndrange = nelements(cache.elements))
1040
return nothing
1041
end
1042
1043
@kernel function calc_surface_integral_KAkernel!(du, MeshT, equations,
1044
surface_integral, dg, factor,
1045
surface_flux_values)
1046
element = @index(Global)
1047
calc_surface_integral_per_element!(du, MeshT,
1048
equations, surface_integral, dg, factor,
1049
surface_flux_values, element)
1050
end
1051
1052
@inline function calc_surface_integral_per_element!(du,
1053
::Type{<:Union{P4estMesh{3},
1054
T8codeMesh{3}}},
1055
equations,
1056
surface_integral::SurfaceIntegralWeakForm,
1057
dg::DGSEM, factor,
1058
surface_flux_values,
1059
element)
1060
# Note that all fluxes have been computed with outward-pointing normal vectors.
1061
# This computes the **negative** surface integral contribution,
1062
# i.e., M^{-1} * boundary_interpolation^T (which is for Gauss-Lobatto DGSEM just M^{-1} * B)
1063
# and the missing "-" is taken care of by `apply_jacobian!`.
1064
#
1065
# We also use explicit assignments instead of `+=` to let `@muladd` turn these
1066
# into FMAs (see comment at the top of the file).
1067
#
1068
# factor = inverse_weights[1]
1069
# For LGL basis: Identical to weighted boundary interpolation at x = ±1
1070
for m in eachnode(dg), l in eachnode(dg)
1071
for v in eachvariable(equations)
1072
# surface at -x
1073
du[v, 1, l, m, element] = (du[v, 1, l, m, element] +
1074
surface_flux_values[v, l, m, 1,
1075
element] *
1076
factor)
1077
1078
# surface at +x
1079
du[v, nnodes(dg), l, m, element] = (du[v, nnodes(dg), l, m, element] +
1080
surface_flux_values[v, l, m, 2,
1081
element] *
1082
factor)
1083
1084
# surface at -y
1085
du[v, l, 1, m, element] = (du[v, l, 1, m, element] +
1086
surface_flux_values[v, l, m, 3,
1087
element] *
1088
factor)
1089
1090
# surface at +y
1091
du[v, l, nnodes(dg), m, element] = (du[v, l, nnodes(dg), m, element] +
1092
surface_flux_values[v, l, m, 4,
1093
element] *
1094
factor)
1095
1096
# surface at -z
1097
du[v, l, m, 1, element] = (du[v, l, m, 1, element] +
1098
surface_flux_values[v, l, m, 5,
1099
element] *
1100
factor)
1101
1102
# surface at +z
1103
du[v, l, m, nnodes(dg), element] = (du[v, l, m, nnodes(dg), element] +
1104
surface_flux_values[v, l, m, 6,
1105
element] *
1106
factor)
1107
end
1108
end
1109
return nothing
1110
end
1111
end # @muladd
1112
1113