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_2d.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{2}, P4estMeshView{2}, T8codeMesh{2}},
11
equations,
12
mortar_l2::LobattoLegendreMortarL2, uEltype)
13
MA2d = MArray{Tuple{nvariables(equations), nnodes(mortar_l2)},
14
uEltype, 2,
15
nvariables(equations) * nnodes(mortar_l2)}
16
fstar_primary_upper_threaded = MA2d[MA2d(undef) for _ in 1:Threads.maxthreadid()]
17
fstar_primary_lower_threaded = MA2d[MA2d(undef) for _ in 1:Threads.maxthreadid()]
18
fstar_secondary_upper_threaded = MA2d[MA2d(undef) for _ in 1:Threads.maxthreadid()]
19
fstar_secondary_lower_threaded = MA2d[MA2d(undef) for _ in 1:Threads.maxthreadid()]
20
u_threaded = MA2d[MA2d(undef) for _ in 1:Threads.maxthreadid()]
21
22
cache = (; fstar_primary_upper_threaded, fstar_primary_lower_threaded,
23
fstar_secondary_upper_threaded, fstar_secondary_lower_threaded,
24
u_threaded)
25
26
return cache
27
end
28
29
# index_to_start_step_2d(index::Symbol, index_range)
30
#
31
# Given a symbolic `index` and an `indexrange` (usually `eachnode(dg)`),
32
# return `index_start, index_step`, i.e., a tuple containing
33
# - `index_start`, an index value to begin a loop
34
# - `index_step`, an index step to update during a loop
35
# The resulting indices translate surface indices to volume indices.
36
#
37
# !!! warning
38
# This assumes that loops using the return values are written as
39
#
40
# i_volume_start, i_volume_step = index_to_start_step_2d(symbolic_index_i, index_range)
41
# j_volume_start, j_volume_step = index_to_start_step_2d(symbolic_index_j, index_range)
42
#
43
# i_volume, j_volume = i_volume_start, j_volume_start
44
# for i_surface in index_range
45
# # do stuff with `i_surface` and `(i_volume, j_volume)`
46
#
47
# i_volume += i_volume_step
48
# j_volume += j_volume_step
49
# end
50
@inline function index_to_start_step_2d(index::Symbol, index_range)
51
index_begin = first(index_range)
52
index_end = last(index_range)
53
54
if index === :begin
55
return index_begin, 0
56
elseif index === :end
57
return index_end, 0
58
elseif index === :i_forward
59
return index_begin, 1
60
else # if index === :i_backward
61
return index_end, -1
62
end
63
end
64
65
# Infer interpolation side, i.e., left (1) or right (2) for an element.
66
# Required for boundary interpolation with Gauss-Legendre nodes.
67
@inline function interpolation_side(index)
68
return (index === :begin) ? 1 : 2
69
end
70
71
function prolong2interfaces!(backend::Nothing, cache, u,
72
mesh::Union{P4estMesh{2}, P4estMeshView{2},
73
T8codeMesh{2}},
74
equations, dg::DGSEM{<:LobattoLegendreBasis})
75
@unpack interfaces = cache
76
@unpack neighbor_ids, node_indices = cache.interfaces
77
index_range = eachnode(dg)
78
79
@threaded for interface in eachinterface(dg, cache)
80
prolong2interfaces_per_interface!(interfaces.u, u, interface,
81
typeof(mesh), equations,
82
neighbor_ids, node_indices, index_range)
83
end
84
return nothing
85
end
86
87
function prolong2interfaces!(backend::Backend, cache, u,
88
mesh::Union{P4estMesh{2}, P4estMeshView{2},
89
T8codeMesh{2}},
90
equations, dg::DGSEM{<:LobattoLegendreBasis})
91
@unpack interfaces = cache
92
ninterfaces(interfaces) == 0 && return nothing
93
@unpack neighbor_ids, node_indices = cache.interfaces
94
index_range = eachnode(dg)
95
96
kernel! = prolong2interfaces_KAkernel!(backend)
97
kernel!(interfaces.u, u, typeof(mesh), equations, neighbor_ids, node_indices,
98
index_range, ndrange = ninterfaces(interfaces))
99
return nothing
100
end
101
102
@kernel function prolong2interfaces_KAkernel!(interfaces_u, u,
103
MeshT::Type{<:Union{P4estMesh{2},
104
P4estMeshView{2},
105
T8codeMesh{2}}},
106
equations, neighbor_ids,
107
node_indices, index_range)
108
interface = @index(Global)
109
prolong2interfaces_per_interface!(interfaces_u, u, interface, MeshT, equations,
110
neighbor_ids, node_indices, index_range)
111
end
112
113
# Version for Gauss-Lobatto-Legendre
114
@inline function prolong2interfaces_per_interface!(interfaces_u, u, interface,
115
::Type{<:Union{P4estMesh{2},
116
P4estMeshView{2},
117
T8codeMesh{2}}},
118
equations, neighbor_ids,
119
node_indices, index_range)
120
primary_element = neighbor_ids[1, interface]
121
primary_indices = node_indices[1, interface]
122
123
i_primary_start, i_primary_step = index_to_start_step_2d(primary_indices[1],
124
index_range)
125
j_primary_start, j_primary_step = index_to_start_step_2d(primary_indices[2],
126
index_range)
127
128
i_primary = i_primary_start
129
j_primary = j_primary_start
130
for i in index_range
131
for v in eachvariable(equations)
132
interfaces_u[1, v, i, interface] = u[v, i_primary, j_primary,
133
primary_element]
134
end
135
i_primary += i_primary_step
136
j_primary += j_primary_step
137
end
138
139
# Copy solution data from the secondary element using "delayed indexing" with
140
# a start value and a step size to get the correct face and orientation.
141
secondary_element = neighbor_ids[2, interface]
142
secondary_indices = node_indices[2, interface]
143
144
i_secondary_start, i_secondary_step = index_to_start_step_2d(secondary_indices[1],
145
index_range)
146
j_secondary_start, j_secondary_step = index_to_start_step_2d(secondary_indices[2],
147
index_range)
148
149
i_secondary = i_secondary_start
150
j_secondary = j_secondary_start
151
for i in index_range
152
for v in eachvariable(equations)
153
interfaces_u[2, v, i, interface] = u[v, i_secondary, j_secondary,
154
secondary_element]
155
end
156
i_secondary += i_secondary_step
157
j_secondary += j_secondary_step
158
end
159
160
return nothing
161
end
162
163
function prolong2interfaces!(backend::Nothing, cache, u,
164
mesh::Union{P4estMesh{2}, P4estMeshView{2}},
165
equations, dg::DGSEM{<:GaussLegendreBasis})
166
@unpack interfaces = cache
167
@unpack neighbor_ids, node_indices = cache.interfaces
168
@unpack boundary_interpolation = dg.basis
169
index_range = eachnode(dg)
170
171
@threaded for interface in eachinterface(dg, cache)
172
prolong2interfaces_per_interface!(interfaces.u, u, interface,
173
typeof(mesh), equations,
174
neighbor_ids, node_indices, index_range,
175
boundary_interpolation)
176
end
177
178
return nothing
179
end
180
181
# Version for Gauss-Legendre, which requires passing in the boundary interpolation matrix
182
@inline function prolong2interfaces_per_interface!(interfaces_u, u, interface,
183
::Type{<:Union{P4estMesh{2},
184
P4estMeshView{2}}},
185
equations, neighbor_ids,
186
node_indices, index_range,
187
boundary_interpolation)
188
# Interpolate solution data from the primary element to the interface.
189
primary_element = neighbor_ids[1, interface]
190
primary_indices = node_indices[1, interface]
191
192
i_primary_start, i_primary_step = index_to_start_step_2d(primary_indices[1],
193
index_range)
194
j_primary_start, j_primary_step = index_to_start_step_2d(primary_indices[2],
195
index_range)
196
# The index direction is identified based on `{i,j}_{primary, secondary}_step`.
197
# For step = 0, the direction identified by this index is normal to the face.
198
# For step != 0 (1 or -1), the direction identified by this index is tangential to the face.
199
200
# Note that in the current implementation, the interface will be
201
# "aligned at the primary element", i.e., the index of the primary side
202
# will always run forwards.
203
204
i_primary = i_primary_start
205
j_primary = j_primary_start
206
207
if i_primary_step == 0
208
# i is the normal direction (constant), j varies along the surface
209
# => Interpolate in first/normal direction
210
# Interpolation side is governed by element orientation
211
side = interpolation_side(primary_indices[1])
212
for i in index_range
213
for v in eachvariable(equations)
214
u_primary = zero(eltype(interfaces_u))
215
for ii in index_range
216
u_primary = (u_primary +
217
u[v, ii, j_primary, primary_element] *
218
boundary_interpolation[ii, side])
219
end
220
interfaces_u[1, v, i, interface] = u_primary
221
end
222
j_primary += j_primary_step # incrementing j_primary suffices
223
end
224
else # j_primary_step == 0
225
# j is the normal direction (constant), i varies along the surface
226
# => Interpolate in second/normal direction
227
# Interpolation side is governed by element orientation
228
side = interpolation_side(primary_indices[2])
229
for i in index_range
230
for v in eachvariable(equations)
231
u_primary = zero(eltype(interfaces_u))
232
for jj in index_range
233
u_primary = (u_primary +
234
u[v, i_primary, jj, primary_element] *
235
boundary_interpolation[jj, side])
236
end
237
interfaces_u[1, v, i, interface] = u_primary
238
end
239
i_primary += i_primary_step # incrementing i_primary suffices
240
end
241
end
242
243
# Interpolate solution data from the secondary element to the interface.
244
secondary_element = neighbor_ids[2, interface]
245
secondary_indices = node_indices[2, interface]
246
247
i_secondary_start, i_secondary_step = index_to_start_step_2d(secondary_indices[1],
248
index_range)
249
j_secondary_start, j_secondary_step = index_to_start_step_2d(secondary_indices[2],
250
index_range)
251
252
i_secondary = i_secondary_start
253
j_secondary = j_secondary_start
254
if i_secondary_step == 0
255
side = interpolation_side(secondary_indices[1])
256
for i in index_range
257
for v in eachvariable(equations)
258
u_secondary = zero(eltype(interfaces_u))
259
for ii in index_range
260
u_secondary = (u_secondary +
261
u[v, ii, j_secondary, secondary_element] *
262
boundary_interpolation[ii, side])
263
end
264
interfaces_u[2, v, i, interface] = u_secondary
265
end
266
j_secondary += j_secondary_step # incrementing j_secondary suffices
267
end
268
else # j_secondary_step == 0
269
side = interpolation_side(secondary_indices[2])
270
for i in index_range
271
for v in eachvariable(equations)
272
u_secondary = zero(eltype(interfaces_u))
273
for jj in index_range
274
u_secondary = (u_secondary +
275
u[v, i_secondary, jj, secondary_element] *
276
boundary_interpolation[jj, side])
277
end
278
interfaces_u[2, v, i, interface] = u_secondary
279
end
280
i_secondary += i_secondary_step # incrementing i_secondary suffices
281
end
282
end
283
284
return nothing
285
end
286
287
function calc_interface_flux!(backend::Nothing, surface_flux_values,
288
mesh::Union{P4estMesh{2}, P4estMeshView{2},
289
T8codeMesh{2}},
290
have_nonconservative_terms,
291
equations, surface_integral,
292
dg::DGSEM{<:LobattoLegendreBasis}, cache)
293
@unpack neighbor_ids, node_indices = cache.interfaces
294
# Take for Gauss-Lobatto-Legendre (GLL) the interface normals from the outer volume nodes, i.e.,
295
# element data.
296
@unpack contravariant_vectors = cache.elements
297
index_range = eachnode(dg)
298
299
@threaded for interface in eachinterface(dg, cache)
300
calc_interface_flux_per_interface!(surface_flux_values, typeof(mesh),
301
have_nonconservative_terms,
302
equations, surface_integral, typeof(dg),
303
cache.interfaces.u, interface,
304
neighbor_ids, node_indices,
305
contravariant_vectors, index_range)
306
end
307
308
return nothing
309
end
310
311
function calc_interface_flux!(backend::Backend, surface_flux_values,
312
mesh::Union{P4estMesh{2}, P4estMeshView{2},
313
T8codeMesh{2}},
314
have_nonconservative_terms,
315
equations, surface_integral,
316
dg::DGSEM{<:LobattoLegendreBasis}, cache)
317
ninterfaces(cache.interfaces) == 0 && return nothing
318
@unpack neighbor_ids, node_indices = cache.interfaces
319
@unpack contravariant_vectors = cache.elements
320
index_range = eachnode(dg)
321
322
kernel! = calc_interface_flux_KAkernel!(backend)
323
kernel!(surface_flux_values, typeof(mesh), have_nonconservative_terms,
324
equations, surface_integral, typeof(dg), cache.interfaces.u,
325
neighbor_ids, node_indices, contravariant_vectors, index_range,
326
ndrange = ninterfaces(cache.interfaces))
327
328
return nothing
329
end
330
331
@kernel function calc_interface_flux_KAkernel!(surface_flux_values,
332
MeshT::Type{<:Union{P4estMesh{2},
333
P4estMeshView{2},
334
T8codeMesh{2}}},
335
have_nonconservative_terms,
336
equations, surface_integral,
337
SolverT::Type{<:DG}, u_interface,
338
neighbor_ids, node_indices,
339
contravariant_vectors, index_range)
340
interface = @index(Global)
341
calc_interface_flux_per_interface!(surface_flux_values, MeshT,
342
have_nonconservative_terms, equations,
343
surface_integral, SolverT, u_interface,
344
interface, neighbor_ids, node_indices,
345
contravariant_vectors, index_range)
346
end
347
348
@inline function calc_interface_flux_per_interface!(surface_flux_values,
349
MeshT::Type{<:Union{P4estMesh{2},
350
P4estMeshView{2},
351
T8codeMesh{2}}},
352
have_nonconservative_terms,
353
equations, surface_integral,
354
SolverT::Type{<:DGSEM{<:LobattoLegendreBasis}},
355
u_interface, interface,
356
neighbor_ids,
357
node_indices, contravariant_vectors,
358
index_range)
359
index_end = last(index_range)
360
361
# Get element and side index information on the primary element
362
primary_element = neighbor_ids[1, interface]
363
primary_indices = node_indices[1, interface]
364
primary_direction = indices2direction(primary_indices)
365
366
# Create the local i,j indexing on the primary element used to pull normal direction information
367
i_primary_start, i_primary_step = index_to_start_step_2d(primary_indices[1],
368
index_range)
369
j_primary_start, j_primary_step = index_to_start_step_2d(primary_indices[2],
370
index_range)
371
372
i_primary = i_primary_start
373
j_primary = j_primary_start
374
375
# Get element and side index information on the secondary element
376
secondary_element = neighbor_ids[2, interface]
377
secondary_indices = node_indices[2, interface]
378
secondary_direction = indices2direction(secondary_indices)
379
380
# Initiate the secondary index to be used in the surface for loop.
381
# This index on the primary side will always run forward but
382
# the secondary index might need to run backwards for flipped sides.
383
if :i_backward in secondary_indices
384
node_secondary = index_end
385
node_secondary_step = -1
386
else
387
node_secondary = 1
388
node_secondary_step = 1
389
end
390
391
for node in index_range
392
# Get the normal direction on the primary element.
393
# Contravariant vectors at interfaces in negative coordinate direction
394
# are pointing inwards. This is handled by `get_normal_direction`.
395
normal_direction = get_normal_direction(primary_direction,
396
contravariant_vectors,
397
i_primary, j_primary,
398
primary_element)
399
400
calc_interface_flux!(surface_flux_values, MeshT, have_nonconservative_terms,
401
equations, surface_integral, SolverT, u_interface,
402
interface, normal_direction, node,
403
primary_direction, primary_element,
404
node_secondary,
405
secondary_direction, secondary_element)
406
407
# Increment primary element indices to pull the normal direction
408
i_primary += i_primary_step
409
j_primary += j_primary_step
410
# Increment the surface node index along the secondary element
411
node_secondary += node_secondary_step
412
end
413
414
return nothing
415
end
416
417
function calc_interface_flux!(backend::Nothing, surface_flux_values,
418
mesh::Union{P4estMesh{2}, P4estMeshView{2}},
419
have_nonconservative_terms,
420
equations, surface_integral,
421
dg::DGSEM{<:GaussLegendreBasis}, cache)
422
@unpack neighbor_ids, node_indices = cache.interfaces
423
# Take for Gauss-Legendre (GL) the interface normals from the interfaces, i.e.,
424
# interface data.
425
@unpack normal_directions = cache.interfaces
426
index_range = eachnode(dg)
427
428
@threaded for interface in eachinterface(dg, cache)
429
calc_interface_flux_per_interface!(surface_flux_values, typeof(mesh),
430
have_nonconservative_terms,
431
equations, surface_integral, typeof(dg),
432
cache.interfaces.u, interface,
433
neighbor_ids, node_indices,
434
normal_directions, index_range)
435
end
436
437
return nothing
438
end
439
440
@inline function calc_interface_flux_per_interface!(surface_flux_values,
441
MeshT::Type{<:Union{P4estMesh{2},
442
P4estMeshView{2}}},
443
have_nonconservative_terms,
444
equations, surface_integral,
445
SolverT::Type{<:DGSEM{<:GaussLegendreBasis}},
446
u_interface, interface,
447
neighbor_ids,
448
node_indices, normal_directions,
449
index_range)
450
index_end = last(index_range)
451
452
# Get element and side index information on the primary element
453
primary_element = neighbor_ids[1, interface]
454
primary_indices = node_indices[1, interface]
455
primary_direction = indices2direction(primary_indices)
456
457
# Get element and side index information on the secondary element
458
secondary_element = neighbor_ids[2, interface]
459
secondary_indices = node_indices[2, interface]
460
secondary_direction = indices2direction(secondary_indices)
461
462
# Initiate the secondary index to be used in the surface for loop.
463
# This index on the primary side will always run forward but
464
# the secondary index might need to run backwards for flipped sides.
465
if :i_backward in secondary_indices
466
node_secondary = index_end
467
node_secondary_step = -1
468
else
469
node_secondary = 1
470
node_secondary_step = 1
471
end
472
473
for node in index_range
474
# This seems to be faster than `@views normal_direction = normal_directions[:, node, interface]`
475
normal_direction = SVector(normal_directions[1, node, interface],
476
normal_directions[2, node, interface])
477
478
calc_interface_flux!(surface_flux_values, MeshT, have_nonconservative_terms,
479
equations, surface_integral, SolverT, u_interface,
480
interface, normal_direction, node,
481
primary_direction, primary_element,
482
node_secondary,
483
secondary_direction, secondary_element)
484
485
# Increment the surface node index along the secondary element
486
node_secondary += node_secondary_step
487
end
488
489
return nothing
490
end
491
492
# Inlined version of the interface flux computation for conservation laws
493
@inline function calc_interface_flux!(surface_flux_values,
494
::Type{<:Union{P4estMesh{2},
495
P4estMeshView{2},
496
T8codeMesh{2}}},
497
have_nonconservative_terms::False, equations,
498
surface_integral, SolverT::Type{<:DG},
499
u_interface, interface_index,
500
normal_direction, primary_node_index,
501
primary_direction_index,
502
primary_element_index,
503
secondary_node_index,
504
secondary_direction_index,
505
secondary_element_index)
506
@unpack surface_flux = surface_integral
507
508
u_ll, u_rr = get_surface_node_vars(u_interface, equations, SolverT,
509
primary_node_index,
510
interface_index)
511
512
flux_ = surface_flux(u_ll, u_rr, normal_direction, equations)
513
514
for v in eachvariable(equations)
515
surface_flux_values[v, primary_node_index, primary_direction_index, primary_element_index] = flux_[v]
516
surface_flux_values[v, secondary_node_index, secondary_direction_index, secondary_element_index] = -flux_[v]
517
end
518
519
return nothing
520
end
521
522
# Inlined version of the interface flux computation for equations with conservative and nonconservative terms
523
@inline function calc_interface_flux!(surface_flux_values,
524
MeshT::Type{<:Union{P4estMesh{2}, T8codeMesh{2}}},
525
have_nonconservative_terms::True, equations,
526
surface_integral, SolverT::Type{<:DG},
527
u_interface, interface_index,
528
normal_direction, primary_node_index,
529
primary_direction_index,
530
primary_element_index,
531
secondary_node_index,
532
secondary_direction_index,
533
secondary_element_index)
534
@unpack surface_flux = surface_integral
535
calc_interface_flux!(surface_flux_values, MeshT, have_nonconservative_terms,
536
combine_conservative_and_nonconservative_fluxes(surface_flux,
537
equations),
538
equations,
539
surface_integral, SolverT, u_interface,
540
interface_index, normal_direction,
541
primary_node_index, primary_direction_index,
542
primary_element_index,
543
secondary_node_index, secondary_direction_index,
544
secondary_element_index)
545
return nothing
546
end
547
548
@inline function calc_interface_flux!(surface_flux_values,
549
::Type{<:Union{P4estMesh{2}, T8codeMesh{2}}},
550
have_nonconservative_terms::True,
551
combine_conservative_and_nonconservative_fluxes::False,
552
equations,
553
surface_integral, SolverT::Type{<:DG},
554
u_interface, interface_index, normal_direction,
555
primary_node_index, primary_direction_index,
556
primary_element_index,
557
secondary_node_index, secondary_direction_index,
558
secondary_element_index)
559
surface_flux, nonconservative_flux = surface_integral.surface_flux
560
561
u_ll, u_rr = get_surface_node_vars(u_interface, equations, SolverT,
562
primary_node_index, interface_index)
563
564
flux_ = surface_flux(u_ll, u_rr, normal_direction, equations)
565
566
# Compute both nonconservative fluxes
567
noncons_primary = nonconservative_flux(u_ll, u_rr, normal_direction, equations)
568
noncons_secondary = nonconservative_flux(u_rr, u_ll, normal_direction, equations)
569
570
# Store the flux with nonconservative terms on the primary and secondary elements
571
for v in eachvariable(equations)
572
# Note the factor 0.5 necessary for the nonconservative fluxes based on
573
# the interpretation of global SBP operators coupled discontinuously via
574
# central fluxes/SATs
575
surface_flux_values[v, primary_node_index, primary_direction_index, primary_element_index] = flux_[v] +
576
0.5f0 *
577
noncons_primary[v]
578
surface_flux_values[v, secondary_node_index, secondary_direction_index, secondary_element_index] = -(flux_[v] +
579
0.5f0 *
580
noncons_secondary[v])
581
end
582
583
return nothing
584
end
585
586
@inline function calc_interface_flux!(surface_flux_values,
587
::Type{<:Union{P4estMesh{2}, T8codeMesh{2}}},
588
have_nonconservative_terms::True,
589
combine_conservative_and_nonconservative_fluxes::True,
590
equations,
591
surface_integral, SolverT::Type{<:DG},
592
u_interface, interface_index, normal_direction,
593
primary_node_index, primary_direction_index,
594
primary_element_index,
595
secondary_node_index, secondary_direction_index,
596
secondary_element_index)
597
@unpack surface_flux = surface_integral
598
599
u_ll, u_rr = get_surface_node_vars(u_interface, equations, SolverT,
600
primary_node_index, interface_index)
601
602
flux_left, flux_right = surface_flux(u_ll, u_rr, normal_direction, equations)
603
604
for v in eachvariable(equations)
605
surface_flux_values[v, primary_node_index, primary_direction_index, primary_element_index] = flux_left[v]
606
surface_flux_values[v, secondary_node_index, secondary_direction_index, secondary_element_index] = -flux_right[v]
607
end
608
609
return nothing
610
end
611
612
function prolong2boundaries!(cache, u,
613
mesh::Union{P4estMesh{2}, P4estMeshView{2}, T8codeMesh{2}},
614
equations, dg::DG)
615
@unpack boundaries = cache
616
index_range = eachnode(dg)
617
618
@threaded for boundary in eachboundary(dg, cache)
619
# Copy solution data from the element using "delayed indexing" with
620
# a start value and a step size to get the correct face and orientation.
621
element = boundaries.neighbor_ids[boundary]
622
node_indices = boundaries.node_indices[boundary]
623
624
i_node_start, i_node_step = index_to_start_step_2d(node_indices[1], index_range)
625
j_node_start, j_node_step = index_to_start_step_2d(node_indices[2], index_range)
626
627
i_node = i_node_start
628
j_node = j_node_start
629
for i in eachnode(dg)
630
for v in eachvariable(equations)
631
boundaries.u[v, i, boundary] = u[v, i_node, j_node, element]
632
end
633
i_node += i_node_step
634
j_node += j_node_step
635
end
636
end
637
638
return nothing
639
end
640
641
# We require this function definition, as the function calls for the
642
# coupled simulations pass the u_parent variable
643
# Note: Since the implementation is identical, we forward to the original function
644
function prolong2boundaries!(cache, u, u_parent, semis,
645
mesh::P4estMeshView{2},
646
equations, surface_integral, dg::DG)
647
return prolong2boundaries!(cache, u, mesh, equations, dg)
648
end
649
650
function calc_boundary_flux!(cache, t, boundary_condition::BC, boundary_indexing,
651
mesh::Union{P4estMesh{2}, T8codeMesh{2}},
652
equations, surface_integral, dg::DG) where {BC}
653
@unpack boundaries = cache
654
@unpack surface_flux_values = cache.elements
655
index_range = eachnode(dg)
656
657
@threaded for local_index in eachindex(boundary_indexing)
658
# Use the local index to get the global boundary index from the pre-sorted list
659
boundary = boundary_indexing[local_index]
660
661
# Get information on the adjacent element, compute the surface fluxes,
662
# and store them
663
element = boundaries.neighbor_ids[boundary]
664
node_indices = boundaries.node_indices[boundary]
665
direction = indices2direction(node_indices)
666
667
i_node_start, i_node_step = index_to_start_step_2d(node_indices[1], index_range)
668
j_node_start, j_node_step = index_to_start_step_2d(node_indices[2], index_range)
669
670
i_node = i_node_start
671
j_node = j_node_start
672
for node in eachnode(dg)
673
calc_boundary_flux!(surface_flux_values, t, boundary_condition,
674
mesh, have_nonconservative_terms(equations),
675
equations, surface_integral, dg, cache,
676
i_node, j_node,
677
node, direction, element, boundary)
678
679
i_node += i_node_step
680
j_node += j_node_step
681
end
682
end
683
684
return nothing
685
end
686
687
# inlined version of the boundary flux calculation along a physical interface
688
@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,
689
mesh::Union{P4estMesh{2}, T8codeMesh{2}},
690
have_nonconservative_terms::False, equations,
691
surface_integral, dg::DG, cache,
692
i_index, j_index,
693
node_index, direction_index, element_index,
694
boundary_index)
695
@unpack boundaries = cache
696
@unpack node_coordinates, contravariant_vectors = cache.elements
697
@unpack surface_flux = surface_integral
698
699
# Extract solution data from boundary container
700
u_inner = get_node_vars(boundaries.u, equations, dg, node_index, boundary_index)
701
702
# Outward-pointing normal direction (not normalized)
703
normal_direction = get_normal_direction(direction_index, contravariant_vectors,
704
i_index, j_index, element_index)
705
706
# Coordinates at boundary node
707
x = get_node_coords(node_coordinates, equations, dg,
708
i_index, j_index, element_index)
709
710
flux_ = boundary_condition(u_inner, normal_direction, x, t, surface_flux, equations)
711
712
# Copy flux to element storage in the correct orientation
713
for v in eachvariable(equations)
714
surface_flux_values[v, node_index, direction_index, element_index] = flux_[v]
715
end
716
717
return nothing
718
end
719
720
# inlined version of the boundary flux calculation along a physical interface
721
@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,
722
mesh::P4estMeshView{2},
723
nonconservative_terms::False, equations,
724
surface_integral, dg::DG, cache,
725
i_index, j_index,
726
node_index, direction_index, element_index,
727
boundary_index, u_parent)
728
@unpack boundaries = cache
729
@unpack contravariant_vectors = cache.elements
730
@unpack surface_flux = surface_integral
731
732
# Extract solution data from boundary container
733
u_inner = get_node_vars(boundaries.u, equations, dg, node_index, boundary_index)
734
735
# Outward-pointing normal direction (not normalized)
736
normal_direction = get_normal_direction(direction_index, contravariant_vectors,
737
i_index, j_index, element_index)
738
739
flux_ = boundary_condition(u_inner, mesh, equations, cache, i_index, j_index,
740
element_index, normal_direction, surface_flux,
741
normal_direction, u_parent)
742
743
# Copy flux to element storage in the correct orientation
744
for v in eachvariable(equations)
745
surface_flux_values[v, node_index, direction_index, element_index] = flux_[v]
746
end
747
return nothing
748
end
749
750
# inlined version of the boundary flux with nonconservative terms calculation along a physical interface
751
@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,
752
mesh::Union{P4estMesh{2}, T8codeMesh{2}},
753
have_nonconservative_terms::True, equations,
754
surface_integral, dg::DG, cache,
755
i_index, j_index,
756
node_index, direction_index, element_index,
757
boundary_index)
758
@unpack surface_flux = surface_integral
759
calc_boundary_flux!(surface_flux_values, t, boundary_condition,
760
mesh, have_nonconservative_terms,
761
combine_conservative_and_nonconservative_fluxes(surface_flux,
762
equations),
763
equations, surface_integral, dg, cache,
764
i_index, j_index,
765
node_index, direction_index, element_index, boundary_index)
766
return nothing
767
end
768
769
@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,
770
mesh::Union{P4estMesh{2}, T8codeMesh{2}},
771
have_nonconservative_terms::True,
772
combine_conservative_and_nonconservative_fluxes::False,
773
equations,
774
surface_integral, dg::DG, cache,
775
i_index, j_index,
776
node_index, direction_index, element_index,
777
boundary_index)
778
@unpack boundaries = cache
779
@unpack node_coordinates, contravariant_vectors = cache.elements
780
781
# Extract solution data from boundary container
782
u_inner = get_node_vars(boundaries.u, equations, dg, node_index, boundary_index)
783
784
# Outward-pointing normal direction (not normalized)
785
normal_direction = get_normal_direction(direction_index, contravariant_vectors,
786
i_index, j_index, element_index)
787
788
# Coordinates at boundary node
789
x = get_node_coords(node_coordinates, equations, dg,
790
i_index, j_index, element_index)
791
792
# Call pointwise numerical flux functions for the conservative and nonconservative part
793
# in the normal direction on the boundary
794
flux, noncons_flux = boundary_condition(u_inner, normal_direction, x, t,
795
surface_integral.surface_flux, equations)
796
797
# Copy flux to element storage in the correct orientation
798
for v in eachvariable(equations)
799
# Note the factor 0.5 necessary for the nonconservative fluxes based on
800
# the interpretation of global SBP operators coupled discontinuously via
801
# central fluxes/SATs
802
surface_flux_values[v, node_index, direction_index, element_index] = flux[v] +
803
0.5f0 *
804
noncons_flux[v]
805
end
806
807
return nothing
808
end
809
810
@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,
811
mesh::Union{P4estMesh{2}, T8codeMesh{2}},
812
have_nonconservative_terms::True,
813
combine_conservative_and_nonconservative_fluxes::True,
814
equations,
815
surface_integral, dg::DG, cache,
816
i_index, j_index,
817
node_index, direction_index, element_index,
818
boundary_index)
819
@unpack boundaries = cache
820
@unpack node_coordinates, contravariant_vectors = cache.elements
821
822
# Extract solution data from boundary container
823
u_inner = get_node_vars(boundaries.u, equations, dg, node_index, boundary_index)
824
825
# Outward-pointing normal direction (not normalized)
826
normal_direction = get_normal_direction(direction_index, contravariant_vectors,
827
i_index, j_index, element_index)
828
829
# Coordinates at boundary node
830
x = get_node_coords(node_coordinates, equations, dg,
831
i_index, j_index, element_index)
832
833
# Call pointwise numerical flux functions for the conservative and nonconservative part
834
# in the normal direction on the boundary
835
flux = boundary_condition(u_inner, normal_direction, x, t,
836
surface_integral.surface_flux, equations)
837
838
# Copy flux to element storage in the correct orientation
839
for v in eachvariable(equations)
840
surface_flux_values[v, node_index, direction_index, element_index] = flux[v]
841
end
842
843
return nothing
844
end
845
846
# Function barrier for type stability
847
function calc_boundary_flux!(cache, t, boundary_conditions,
848
mesh::P4estMeshView,
849
equations, surface_integral, dg::DG, u_parent)
850
@unpack boundary_condition_types, boundary_indices = boundary_conditions
851
852
calc_boundary_flux_by_type!(cache, t, boundary_condition_types, boundary_indices,
853
mesh, equations, surface_integral, dg, u_parent)
854
return nothing
855
end
856
857
function prolong2mortars!(cache, u,
858
mesh::Union{P4estMesh{2}, P4estMeshView{2}, T8codeMesh{2}},
859
equations,
860
mortar_l2::LobattoLegendreMortarL2,
861
dg::DGSEM)
862
@unpack neighbor_ids, node_indices = cache.mortars
863
index_range = eachnode(dg)
864
865
@threaded for mortar in eachmortar(dg, cache)
866
# Copy solution data from the small elements using "delayed indexing" with
867
# a start value and a step size to get the correct face and orientation.
868
small_indices = node_indices[1, mortar]
869
870
i_small_start, i_small_step = index_to_start_step_2d(small_indices[1],
871
index_range)
872
j_small_start, j_small_step = index_to_start_step_2d(small_indices[2],
873
index_range)
874
875
for position in 1:2
876
i_small = i_small_start
877
j_small = j_small_start
878
element = neighbor_ids[position, mortar]
879
for i in eachnode(dg)
880
for v in eachvariable(equations)
881
cache.mortars.u[1, v, position, i, mortar] = u[v, i_small, j_small,
882
element]
883
end
884
i_small += i_small_step
885
j_small += j_small_step
886
end
887
end
888
889
# Buffer to copy solution values of the large element in the correct orientation
890
# before interpolating
891
u_buffer = cache.u_threaded[Threads.threadid()]
892
893
# Copy solution of large element face to buffer in the
894
# correct orientation
895
large_indices = node_indices[2, mortar]
896
897
i_large_start, i_large_step = index_to_start_step_2d(large_indices[1],
898
index_range)
899
j_large_start, j_large_step = index_to_start_step_2d(large_indices[2],
900
index_range)
901
902
i_large = i_large_start
903
j_large = j_large_start
904
element = neighbor_ids[3, mortar]
905
for i in eachnode(dg)
906
for v in eachvariable(equations)
907
u_buffer[v, i] = u[v, i_large, j_large, element]
908
end
909
i_large += i_large_step
910
j_large += j_large_step
911
end
912
913
# Interpolate large element face data from buffer to small face locations
914
multiply_dimensionwise!(view(cache.mortars.u, 2, :, 1, :, mortar),
915
mortar_l2.forward_lower,
916
u_buffer)
917
multiply_dimensionwise!(view(cache.mortars.u, 2, :, 2, :, mortar),
918
mortar_l2.forward_upper,
919
u_buffer)
920
end
921
922
return nothing
923
end
924
925
function calc_mortar_flux!(surface_flux_values,
926
mesh::Union{P4estMesh{2}, P4estMeshView{2}, T8codeMesh{2}},
927
have_nonconservative_terms, equations,
928
mortar_l2::LobattoLegendreMortarL2,
929
surface_integral, dg::DG, cache)
930
@unpack neighbor_ids, node_indices = cache.mortars
931
@unpack contravariant_vectors = cache.elements
932
@unpack (fstar_primary_upper_threaded, fstar_primary_lower_threaded,
933
fstar_secondary_upper_threaded, fstar_secondary_lower_threaded) = cache
934
index_range = eachnode(dg)
935
936
@threaded for mortar in eachmortar(dg, cache)
937
# Choose thread-specific pre-allocated container
938
fstar_primary = (fstar_primary_lower_threaded[Threads.threadid()],
939
fstar_primary_upper_threaded[Threads.threadid()])
940
941
fstar_secondary = (fstar_secondary_lower_threaded[Threads.threadid()],
942
fstar_secondary_upper_threaded[Threads.threadid()])
943
944
# Get index information on the small elements
945
small_indices = node_indices[1, mortar]
946
small_direction = indices2direction(small_indices)
947
948
i_small_start, i_small_step = index_to_start_step_2d(small_indices[1],
949
index_range)
950
j_small_start, j_small_step = index_to_start_step_2d(small_indices[2],
951
index_range)
952
953
for position in 1:2
954
i_small = i_small_start
955
j_small = j_small_start
956
element = neighbor_ids[position, mortar]
957
for node in eachnode(dg)
958
# Get the normal direction on the small element.
959
# Note, contravariant vectors at interfaces in negative coordinate direction
960
# are pointing inwards. This is handled by `get_normal_direction`.
961
normal_direction = get_normal_direction(small_direction,
962
contravariant_vectors,
963
i_small, j_small, element)
964
965
calc_mortar_flux!(fstar_primary, fstar_secondary,
966
mesh, have_nonconservative_terms, equations,
967
surface_integral, dg, cache,
968
mortar, position, normal_direction,
969
node)
970
971
i_small += i_small_step
972
j_small += j_small_step
973
end
974
end
975
976
# Buffer to interpolate flux values of the large element to before
977
# copying in the correct orientation
978
u_buffer = cache.u_threaded[Threads.threadid()]
979
980
# in calc_interface_flux!, the interface flux is computed once over each
981
# interface using the normal from the "primary" element. The result is then
982
# passed back to the "secondary" element, flipping the sign to account for the
983
# change in the normal direction. For mortars, this sign flip occurs in
984
# "mortar_fluxes_to_elements!" instead.
985
mortar_fluxes_to_elements!(surface_flux_values,
986
mesh, equations, mortar_l2, dg, cache,
987
mortar, fstar_primary, fstar_secondary,
988
u_buffer)
989
end
990
991
return nothing
992
end
993
994
# Inlined version of the mortar flux computation on small elements for conservation laws
995
@inline function calc_mortar_flux!(fstar_primary, fstar_secondary,
996
mesh::Union{P4estMesh{2}, T8codeMesh{2}},
997
have_nonconservative_terms::False, equations,
998
surface_integral, dg::DG, cache,
999
mortar_index, position_index, normal_direction,
1000
node_index)
1001
@unpack u = cache.mortars
1002
@unpack surface_flux = surface_integral
1003
1004
u_ll, u_rr = get_surface_node_vars(u, equations, dg, position_index,
1005
node_index, mortar_index)
1006
1007
flux = surface_flux(u_ll, u_rr, normal_direction, equations)
1008
1009
# Copy flux to buffer
1010
set_node_vars!(fstar_primary[position_index], flux, equations, dg, node_index)
1011
set_node_vars!(fstar_secondary[position_index], flux, equations, dg, node_index)
1012
1013
return nothing
1014
end
1015
1016
# Inlined version of the mortar flux computation on small elements for equations with conservative and
1017
# nonconservative terms
1018
@inline function calc_mortar_flux!(fstar_primary, fstar_secondary,
1019
mesh::Union{P4estMesh{2}, T8codeMesh{2}},
1020
have_nonconservative_terms::True, equations,
1021
surface_integral, dg::DG, cache,
1022
mortar_index, position_index, normal_direction,
1023
node_index)
1024
@unpack u = cache.mortars
1025
surface_flux, nonconservative_flux = surface_integral.surface_flux
1026
1027
u_ll, u_rr = get_surface_node_vars(u, equations, dg, position_index,
1028
node_index, mortar_index)
1029
1030
# Compute conservative flux
1031
flux = surface_flux(u_ll, u_rr, normal_direction, equations)
1032
1033
# Compute nonconservative flux and add it to the conservative flux.
1034
# The nonconservative flux is scaled by a factor of 0.5 based on
1035
# the interpretation of global SBP operators coupled discontinuously via
1036
# central fluxes/SATs
1037
noncons_primary = nonconservative_flux(u_ll, u_rr, normal_direction, equations)
1038
noncons_secondary = nonconservative_flux(u_rr, u_ll, normal_direction, equations)
1039
1040
flux_plus_noncons_primary = flux + 0.5f0 * noncons_primary
1041
flux_plus_noncons_secondary = flux + 0.5f0 * noncons_secondary
1042
1043
# Copy to buffer
1044
set_node_vars!(fstar_primary[position_index], flux_plus_noncons_primary, equations,
1045
dg, node_index)
1046
set_node_vars!(fstar_secondary[position_index], flux_plus_noncons_secondary,
1047
equations, dg, node_index)
1048
1049
return nothing
1050
end
1051
1052
@inline function mortar_fluxes_to_elements!(surface_flux_values,
1053
mesh::Union{P4estMesh{2}, T8codeMesh{2}},
1054
equations,
1055
mortar_l2::LobattoLegendreMortarL2,
1056
dg::DGSEM, cache, mortar, fstar_primary,
1057
fstar_secondary, u_buffer)
1058
@unpack neighbor_ids, node_indices = cache.mortars
1059
1060
# Copy solution small to small
1061
small_indices = node_indices[1, mortar]
1062
small_direction = indices2direction(small_indices)
1063
1064
for position in 1:2
1065
element = neighbor_ids[position, mortar]
1066
for i in eachnode(dg)
1067
for v in eachvariable(equations)
1068
surface_flux_values[v, i, small_direction, element] = fstar_primary[position][v,
1069
i]
1070
end
1071
end
1072
end
1073
1074
# Project small fluxes to large element.
1075
multiply_dimensionwise!(u_buffer,
1076
mortar_l2.reverse_upper, fstar_secondary[2],
1077
mortar_l2.reverse_lower, fstar_secondary[1])
1078
1079
# The flux is calculated in the outward direction of the small elements,
1080
# so the sign must be switched to get the flux in outward direction
1081
# of the large element.
1082
# The contravariant vectors of the large element (and therefore the normal
1083
# vectors of the large element as well) are twice as large as the
1084
# contravariant vectors of the small elements. Therefore, the flux needs
1085
# to be scaled by a factor of 2 to obtain the flux of the large element.
1086
u_buffer .*= -2
1087
1088
# Copy interpolated flux values from buffer to large element face in the
1089
# correct orientation.
1090
# Note that the index of the small sides will always run forward but
1091
# the index of the large side might need to run backwards for flipped sides.
1092
large_element = neighbor_ids[3, mortar]
1093
large_indices = node_indices[2, mortar]
1094
large_direction = indices2direction(large_indices)
1095
1096
if :i_backward in large_indices
1097
for i in eachnode(dg)
1098
for v in eachvariable(equations)
1099
surface_flux_values[v, end + 1 - i, large_direction, large_element] = u_buffer[v,
1100
i]
1101
end
1102
end
1103
else
1104
for i in eachnode(dg)
1105
for v in eachvariable(equations)
1106
surface_flux_values[v, i, large_direction, large_element] = u_buffer[v,
1107
i]
1108
end
1109
end
1110
end
1111
1112
return nothing
1113
end
1114
1115
function calc_surface_integral!(backend::Nothing, du, u,
1116
mesh::Union{P4estMesh{2}, P4estMeshView{2},
1117
T8codeMesh{2}},
1118
equations, surface_integral::SurfaceIntegralWeakForm,
1119
dg::DGSEM{<:LobattoLegendreBasis}, cache)
1120
@unpack inverse_weights = dg.basis
1121
@unpack surface_flux_values = cache.elements
1122
1123
@threaded for element in eachelement(dg, cache)
1124
calc_surface_integral_per_element!(du, typeof(mesh), equations,
1125
surface_integral, dg, inverse_weights[1],
1126
surface_flux_values, element)
1127
end
1128
end
1129
1130
function calc_surface_integral!(backend::Nothing, du, u,
1131
mesh::Union{P4estMesh{2}, P4estMeshView{2}},
1132
equations, surface_integral::SurfaceIntegralWeakForm,
1133
dg::DGSEM{<:GaussLegendreBasis}, cache)
1134
@unpack boundary_interpolation_inverse_weights = dg.basis
1135
@unpack surface_flux_values = cache.elements
1136
1137
@threaded for element in eachelement(dg, cache)
1138
calc_surface_integral_per_element!(du, typeof(mesh), equations,
1139
surface_integral, dg,
1140
boundary_interpolation_inverse_weights,
1141
surface_flux_values, element)
1142
end
1143
end
1144
1145
function calc_surface_integral!(backend::Backend, du, u,
1146
mesh::Union{P4estMesh{2}, P4estMeshView{2},
1147
T8codeMesh{2}},
1148
equations,
1149
surface_integral::SurfaceIntegralWeakForm,
1150
dg::DGSEM{<:LobattoLegendreBasis}, cache)
1151
nelements(dg, cache) == 0 && return nothing
1152
@unpack inverse_weights = dg.basis
1153
@unpack surface_flux_values = cache.elements
1154
1155
kernel! = calc_surface_integral_KAkernel!(backend)
1156
kernel!(du, typeof(mesh), equations, surface_integral, dg, inverse_weights[1],
1157
surface_flux_values, ndrange = nelements(dg, cache))
1158
return nothing
1159
end
1160
1161
@kernel function calc_surface_integral_KAkernel!(du,
1162
MeshT::Type{<:Union{P4estMesh{2},
1163
P4estMeshView{2},
1164
T8codeMesh{2}}},
1165
equations,
1166
surface_integral::SurfaceIntegralWeakForm,
1167
dg::DGSEM{<:LobattoLegendreBasis},
1168
factor,
1169
surface_flux_values)
1170
element = @index(Global)
1171
calc_surface_integral_per_element!(du, MeshT, equations, surface_integral,
1172
dg, factor, surface_flux_values, element)
1173
end
1174
1175
@inline function calc_surface_integral_per_element!(du,
1176
::Type{<:Union{P4estMesh{2},
1177
P4estMeshView{2},
1178
T8codeMesh{2}}},
1179
equations,
1180
surface_integral::SurfaceIntegralWeakForm,
1181
dg::DGSEM{<:LobattoLegendreBasis},
1182
factor,
1183
surface_flux_values, element)
1184
# Note that all fluxes have been computed with outward-pointing normal vectors.
1185
# This computes the **negative** surface integral contribution,
1186
# i.e., M^{-1} * boundary_interpolation^T (which is for Gauss-Lobatto DGSEM just M^{-1} * B)
1187
# and the missing "-" is taken care of by `apply_jacobian!`.
1188
#
1189
# We also use explicit assignments instead of `+=` to let `@muladd` turn these
1190
# into FMAs (see comment at the top of the file).
1191
#
1192
# factor = inverse_weights[1]
1193
# For LGL basis: Identical to weighted boundary interpolation at x = ±1
1194
for l in eachnode(dg)
1195
for v in eachvariable(equations)
1196
# surface at -x
1197
du[v, 1, l, element] = (du[v, 1, l, element] +
1198
surface_flux_values[v, l, 1, element] *
1199
factor)
1200
1201
# surface at +x
1202
du[v, nnodes(dg), l, element] = (du[v, nnodes(dg), l, element] +
1203
surface_flux_values[v, l, 2, element] *
1204
factor)
1205
1206
# surface at -y
1207
du[v, l, 1, element] = (du[v, l, 1, element] +
1208
surface_flux_values[v, l, 3, element] *
1209
factor)
1210
1211
# surface at +y
1212
du[v, l, nnodes(dg), element] = (du[v, l, nnodes(dg), element] +
1213
surface_flux_values[v, l, 4, element] *
1214
factor)
1215
end
1216
end
1217
return nothing
1218
end
1219
1220
function calc_surface_integral_per_element!(du,
1221
::Type{<:Union{P4estMesh{2},
1222
P4estMeshView{2}}},
1223
equations,
1224
surface_integral::SurfaceIntegralWeakForm,
1225
dg::DGSEM{<:GaussLegendreBasis},
1226
boundary_interpolation_inverse_weights,
1227
surface_flux_values, element)
1228
# Note that all fluxes have been computed with outward-pointing normal vectors.
1229
# This computes the **negative** surface integral contribution,
1230
# i.e., M^{-1} * boundary_interpolation^T
1231
# and the missing "-" is taken care of by `apply_jacobian!`.
1232
#
1233
# We also use explicit assignments instead of `+=` to let `@muladd` turn these
1234
# into FMAs (see comment at the top of the file).
1235
for l in eachnode(dg)
1236
for v in eachvariable(equations)
1237
# Aliases for repeatedly accessed variables
1238
surface_flux_minus_x = surface_flux_values[v, l, 1, element]
1239
surface_flux_plus_x = surface_flux_values[v, l, 2, element]
1240
for ii in eachnode(dg)
1241
# surface at -x
1242
du[v, ii, l, element] = (du[v, ii, l, element] +
1243
surface_flux_minus_x *
1244
boundary_interpolation_inverse_weights[ii,
1245
1])
1246
# surface at +x
1247
du[v, ii, l, element] = (du[v, ii, l, element] +
1248
surface_flux_plus_x *
1249
boundary_interpolation_inverse_weights[ii,
1250
2])
1251
end
1252
1253
surface_flux_minus_y = surface_flux_values[v, l, 3, element]
1254
surface_flux_plus_y = surface_flux_values[v, l, 4, element]
1255
for jj in eachnode(dg)
1256
# surface at -y
1257
du[v, l, jj, element] = (du[v, l, jj, element] +
1258
surface_flux_minus_y *
1259
boundary_interpolation_inverse_weights[jj,
1260
1])
1261
# surface at +y
1262
du[v, l, jj, element] = (du[v, l, jj, element] +
1263
surface_flux_plus_y *
1264
boundary_interpolation_inverse_weights[jj,
1265
2])
1266
end
1267
end
1268
end
1269
1270
return nothing
1271
end
1272
1273
# Call this for coupled P4estMeshView simulations.
1274
# The coupling calculations (especially boundary conditions) require data from the parent mesh, which is why
1275
# the additional variable u_parent is needed, compared to non-coupled systems.
1276
function rhs!(du, u, t, u_parent, semis,
1277
mesh::P4estMeshView{2},
1278
equations,
1279
boundary_conditions, source_terms::Source,
1280
dg::DG, cache) where {Source}
1281
backend = nothing
1282
# Reset du
1283
@trixi_timeit timer() "reset ∂u/∂t" set_zero!(du, dg, cache)
1284
1285
# Calculate volume integral
1286
@trixi_timeit timer() "volume integral" begin
1287
calc_volume_integral!(backend, du, u, mesh,
1288
have_nonconservative_terms(equations), equations,
1289
dg.volume_integral, dg, cache)
1290
end
1291
1292
# Prolong solution to interfaces
1293
@trixi_timeit timer() "prolong2interfaces" begin
1294
prolong2interfaces!(backend, cache, u, mesh, equations, dg)
1295
end
1296
1297
# Calculate interface fluxes
1298
@trixi_timeit timer() "interface flux" begin
1299
calc_interface_flux!(backend, cache.elements.surface_flux_values, mesh,
1300
have_nonconservative_terms(equations), equations,
1301
dg.surface_integral, dg, cache)
1302
end
1303
1304
# Prolong solution to boundaries
1305
@trixi_timeit timer() "prolong2boundaries" begin
1306
prolong2boundaries!(cache, u, u_parent, semis, mesh, equations,
1307
dg.surface_integral, dg)
1308
end
1309
1310
# Calculate boundary fluxes
1311
@trixi_timeit timer() "boundary flux" begin
1312
calc_boundary_flux!(cache, t, boundary_conditions, mesh, equations,
1313
dg.surface_integral, dg, u_parent)
1314
end
1315
1316
# Prolong solution to mortars
1317
@trixi_timeit timer() "prolong2mortars" begin
1318
prolong2mortars!(cache, u, mesh, equations,
1319
dg.mortar, dg)
1320
end
1321
1322
# Calculate mortar fluxes
1323
@trixi_timeit timer() "mortar flux" begin
1324
calc_mortar_flux!(cache.elements.surface_flux_values, mesh,
1325
have_nonconservative_terms(equations), equations,
1326
dg.mortar, dg.surface_integral, dg, cache)
1327
end
1328
1329
# Calculate surface integrals
1330
@trixi_timeit timer() "surface integral" begin
1331
calc_surface_integral!(backend, du, u, mesh, equations,
1332
dg.surface_integral, dg, cache)
1333
end
1334
1335
# Apply Jacobian from mapping to reference element
1336
@trixi_timeit timer() "Jacobian" apply_jacobian!(backend, du, mesh, equations, dg,
1337
cache)
1338
1339
# Calculate source terms
1340
@trixi_timeit timer() "source terms" begin
1341
calc_sources!(du, u, t, source_terms, equations, dg, cache)
1342
end
1343
1344
return nothing
1345
end
1346
end # @muladd
1347
1348