Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/solvers/dgsem_structured/containers_3d.jl
5590 views
1
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
2
# Since these FMAs can increase the performance of many numerical algorithms,
3
# we need to opt-in explicitly.
4
# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
5
@muladd begin
6
#! format: noindent
7
8
# Initialize data structures in element container
9
function init_elements!(elements, mesh::StructuredMesh{3}, basis::LobattoLegendreBasis)
10
@unpack node_coordinates, left_neighbors,
11
jacobian_matrix, contravariant_vectors, inverse_jacobian = elements
12
13
linear_indices = LinearIndices(size(mesh))
14
15
# Calculate node coordinates, Jacobian matrix, and inverse Jacobian determinant
16
for cell_z in 1:size(mesh, 3), cell_y in 1:size(mesh, 2), cell_x in 1:size(mesh, 1)
17
element = linear_indices[cell_x, cell_y, cell_z]
18
19
calc_node_coordinates!(node_coordinates, element, cell_x, cell_y, cell_z,
20
mesh.mapping, mesh, basis)
21
22
calc_jacobian_matrix!(jacobian_matrix, element, node_coordinates, basis)
23
24
calc_contravariant_vectors!(contravariant_vectors, element, jacobian_matrix,
25
node_coordinates, basis)
26
27
calc_inverse_jacobian!(inverse_jacobian, element, jacobian_matrix, basis)
28
end
29
30
initialize_left_neighbor_connectivity!(left_neighbors, mesh, linear_indices)
31
32
return nothing
33
end
34
35
# Calculate physical coordinates to which every node of the reference element is mapped
36
# `mesh.mapping` is passed as an additional argument for type stability (function barrier)
37
function calc_node_coordinates!(node_coordinates, element,
38
cell_x, cell_y, cell_z,
39
mapping, mesh::StructuredMesh{3},
40
basis::LobattoLegendreBasis)
41
@unpack nodes = basis
42
43
# Get cell length in reference mesh
44
dx = 2 / size(mesh, 1)
45
dy = 2 / size(mesh, 2)
46
dz = 2 / size(mesh, 3)
47
48
# Calculate node coordinates of reference mesh
49
cell_x_offset = -1 + (cell_x - 1) * dx + dx / 2
50
cell_y_offset = -1 + (cell_y - 1) * dy + dy / 2
51
cell_z_offset = -1 + (cell_z - 1) * dz + dz / 2
52
53
for k in eachnode(basis), j in eachnode(basis), i in eachnode(basis)
54
# node_coordinates are the mapped reference node_coordinates
55
node_coordinates[:, i, j, k, element] .= mapping(cell_x_offset +
56
dx / 2 * nodes[i],
57
cell_y_offset +
58
dy / 2 * nodes[j],
59
cell_z_offset +
60
dz / 2 * nodes[k])
61
end
62
63
return nothing
64
end
65
66
# Calculate Jacobian matrix of the mapping from the reference element to the element in the physical domain
67
function calc_jacobian_matrix!(jacobian_matrix::AbstractArray{<:Any, 6}, element,
68
node_coordinates, basis)
69
# The code below is equivalent to the following matrix multiplications but much faster.
70
#
71
# for dim in 1:3, j in eachnode(basis), i in eachnode(basis)
72
# # ∂/∂ξ
73
# jacobian_matrix[dim, 1, :, i, j, element] = basis.derivative_matrix * node_coordinates[dim, :, i, j, element]
74
# # ∂/∂η
75
# jacobian_matrix[dim, 2, i, :, j, element] = basis.derivative_matrix * node_coordinates[dim, i, :, j, element]
76
# # ∂/∂ζ
77
# jacobian_matrix[dim, 3, i, j, :, element] = basis.derivative_matrix * node_coordinates[dim, i, j, :, element]
78
# end
79
80
@turbo for dim in 1:3, k in eachnode(basis), j in eachnode(basis),
81
i in eachnode(basis)
82
83
result = zero(eltype(jacobian_matrix))
84
85
for ii in eachnode(basis)
86
result += basis.derivative_matrix[i, ii] *
87
node_coordinates[dim, ii, j, k, element]
88
end
89
90
jacobian_matrix[dim, 1, i, j, k, element] = result
91
end
92
93
@turbo for dim in 1:3, k in eachnode(basis), j in eachnode(basis),
94
i in eachnode(basis)
95
96
result = zero(eltype(jacobian_matrix))
97
98
for ii in eachnode(basis)
99
result += basis.derivative_matrix[j, ii] *
100
node_coordinates[dim, i, ii, k, element]
101
end
102
103
jacobian_matrix[dim, 2, i, j, k, element] = result
104
end
105
106
@turbo for dim in 1:3, k in eachnode(basis), j in eachnode(basis),
107
i in eachnode(basis)
108
109
result = zero(eltype(jacobian_matrix))
110
111
for ii in eachnode(basis)
112
result += basis.derivative_matrix[k, ii] *
113
node_coordinates[dim, i, j, ii, element]
114
end
115
116
jacobian_matrix[dim, 3, i, j, k, element] = result
117
end
118
119
return jacobian_matrix
120
end
121
122
# Calculate contravariant vectors, multiplied by the Jacobian determinant J of the transformation mapping,
123
# using the invariant curl form.
124
# These are called Ja^i in Kopriva's blue book.
125
function calc_contravariant_vectors!(contravariant_vectors::AbstractArray{<:Any, 6},
126
element,
127
jacobian_matrix, node_coordinates,
128
basis::LobattoLegendreBasis)
129
@unpack derivative_matrix = basis
130
131
# The general form is
132
# Jaⁱₙ = 0.5 * ( ∇ × (Xₘ ∇ Xₗ - Xₗ ∇ Xₘ) )ᵢ where (n, m, l) cyclic and ∇ = (∂/∂ξ, ∂/∂η, ∂/∂ζ)ᵀ
133
134
for n in 1:3
135
# (n, m, l) cyclic
136
m = (n % 3) + 1
137
l = ((n + 1) % 3) + 1
138
139
# Calculate Ja¹ₙ = 0.5 * [ (Xₘ Xₗ_ζ - Xₗ Xₘ_ζ)_η - (Xₘ Xₗ_η - Xₗ Xₘ_η)_ζ ]
140
# For each of these, the first and second summand are computed in separate loops
141
# for performance reasons.
142
143
# First summand 0.5 * (Xₘ Xₗ_ζ - Xₗ Xₘ_ζ)_η
144
@turbo for k in eachnode(basis), j in eachnode(basis), i in eachnode(basis)
145
result = zero(eltype(contravariant_vectors))
146
147
for ii in eachnode(basis)
148
# Multiply derivative_matrix to j-dimension to differentiate wrt η
149
result += 0.5f0 * derivative_matrix[j, ii] *
150
(node_coordinates[m, i, ii, k, element] *
151
jacobian_matrix[l, 3, i, ii, k, element] -
152
node_coordinates[l, i, ii, k, element] *
153
jacobian_matrix[m, 3, i, ii, k, element])
154
end
155
156
contravariant_vectors[n, 1, i, j, k, element] = result
157
end
158
159
# Second summand -0.5 * (Xₘ Xₗ_η - Xₗ Xₘ_η)_ζ
160
@turbo for k in eachnode(basis), j in eachnode(basis), i in eachnode(basis)
161
result = zero(eltype(contravariant_vectors))
162
163
for ii in eachnode(basis)
164
# Multiply derivative_matrix to k-dimension to differentiate wrt ζ
165
result += 0.5f0 * derivative_matrix[k, ii] *
166
(node_coordinates[m, i, j, ii, element] *
167
jacobian_matrix[l, 2, i, j, ii, element] -
168
node_coordinates[l, i, j, ii, element] *
169
jacobian_matrix[m, 2, i, j, ii, element])
170
end
171
172
contravariant_vectors[n, 1, i, j, k, element] -= result
173
end
174
175
# Calculate Ja²ₙ = 0.5 * [ (Xₘ Xₗ_ξ - Xₗ Xₘ_ξ)_ζ - (Xₘ Xₗ_ζ - Xₗ Xₘ_ζ)_ξ ]
176
177
# First summand 0.5 * (Xₘ Xₗ_ξ - Xₗ Xₘ_ξ)_ζ
178
@turbo for k in eachnode(basis), j in eachnode(basis), i in eachnode(basis)
179
result = zero(eltype(contravariant_vectors))
180
181
for ii in eachnode(basis)
182
# Multiply derivative_matrix to k-dimension to differentiate wrt ζ
183
result += 0.5f0 * derivative_matrix[k, ii] *
184
(node_coordinates[m, i, j, ii, element] *
185
jacobian_matrix[l, 1, i, j, ii, element] -
186
node_coordinates[l, i, j, ii, element] *
187
jacobian_matrix[m, 1, i, j, ii, element])
188
end
189
190
contravariant_vectors[n, 2, i, j, k, element] = result
191
end
192
193
# Second summand -0.5 * (Xₘ Xₗ_ζ - Xₗ Xₘ_ζ)_ξ
194
@turbo for k in eachnode(basis), j in eachnode(basis), i in eachnode(basis)
195
result = zero(eltype(contravariant_vectors))
196
197
for ii in eachnode(basis)
198
# Multiply derivative_matrix to i-dimension to differentiate wrt ξ
199
result += 0.5f0 * derivative_matrix[i, ii] *
200
(node_coordinates[m, ii, j, k, element] *
201
jacobian_matrix[l, 3, ii, j, k, element] -
202
node_coordinates[l, ii, j, k, element] *
203
jacobian_matrix[m, 3, ii, j, k, element])
204
end
205
206
contravariant_vectors[n, 2, i, j, k, element] -= result
207
end
208
209
# Calculate Ja³ₙ = 0.5 * [ (Xₘ Xₗ_η - Xₗ Xₘ_η)_ξ - (Xₘ Xₗ_ξ - Xₗ Xₘ_ξ)_η ]
210
211
# First summand 0.5 * (Xₘ Xₗ_η - Xₗ Xₘ_η)_ξ
212
@turbo for k in eachnode(basis), j in eachnode(basis), i in eachnode(basis)
213
result = zero(eltype(contravariant_vectors))
214
215
for ii in eachnode(basis)
216
# Multiply derivative_matrix to i-dimension to differentiate wrt ξ
217
result += 0.5f0 * derivative_matrix[i, ii] *
218
(node_coordinates[m, ii, j, k, element] *
219
jacobian_matrix[l, 2, ii, j, k, element] -
220
node_coordinates[l, ii, j, k, element] *
221
jacobian_matrix[m, 2, ii, j, k, element])
222
end
223
224
contravariant_vectors[n, 3, i, j, k, element] = result
225
end
226
227
# Second summand -0.5 * (Xₘ Xₗ_ξ - Xₗ Xₘ_ξ)_η
228
@turbo for k in eachnode(basis), j in eachnode(basis), i in eachnode(basis)
229
result = zero(eltype(contravariant_vectors))
230
231
for ii in eachnode(basis)
232
# Multiply derivative_matrix to j-dimension to differentiate wrt η
233
result += 0.5f0 * derivative_matrix[j, ii] *
234
(node_coordinates[m, i, ii, k, element] *
235
jacobian_matrix[l, 1, i, ii, k, element] -
236
node_coordinates[l, i, ii, k, element] *
237
jacobian_matrix[m, 1, i, ii, k, element])
238
end
239
240
contravariant_vectors[n, 3, i, j, k, element] -= result
241
end
242
end
243
244
return contravariant_vectors
245
end
246
247
# Calculate inverse Jacobian (determinant of Jacobian matrix of the mapping) in each node
248
function calc_inverse_jacobian!(inverse_jacobian::AbstractArray{<:Any, 4}, element,
249
jacobian_matrix, basis)
250
@turbo for k in eachnode(basis), j in eachnode(basis), i in eachnode(basis)
251
# Calculate Determinant by using Sarrus formula (about 100 times faster than LinearAlgebra.det())
252
inverse_jacobian[i, j, k, element] = inv(jacobian_matrix[1, 1, i, j, k,
253
element] *
254
jacobian_matrix[2, 2, i, j, k,
255
element] *
256
jacobian_matrix[3, 3, i, j, k, element] +
257
jacobian_matrix[1, 2, i, j, k,
258
element] *
259
jacobian_matrix[2, 3, i, j, k,
260
element] *
261
jacobian_matrix[3, 1, i, j, k, element] +
262
jacobian_matrix[1, 3, i, j, k,
263
element] *
264
jacobian_matrix[2, 1, i, j, k,
265
element] *
266
jacobian_matrix[3, 2, i, j, k, element] -
267
jacobian_matrix[3, 1, i, j, k,
268
element] *
269
jacobian_matrix[2, 2, i, j, k,
270
element] *
271
jacobian_matrix[1, 3, i, j, k, element] -
272
jacobian_matrix[3, 2, i, j, k,
273
element] *
274
jacobian_matrix[2, 3, i, j, k,
275
element] *
276
jacobian_matrix[1, 1, i, j, k, element] -
277
jacobian_matrix[3, 3, i, j, k,
278
element] *
279
jacobian_matrix[2, 1, i, j, k,
280
element] *
281
jacobian_matrix[1, 2, i, j, k, element])
282
end
283
284
return inverse_jacobian
285
end
286
287
# Save id of left neighbor of every element
288
function initialize_left_neighbor_connectivity!(left_neighbors, mesh::StructuredMesh{3},
289
linear_indices)
290
# Neighbors in x-direction
291
for cell_z in 1:size(mesh, 3), cell_y in 1:size(mesh, 2)
292
# Inner elements
293
for cell_x in 2:size(mesh, 1)
294
element = linear_indices[cell_x, cell_y, cell_z]
295
left_neighbors[1, element] = linear_indices[cell_x - 1, cell_y, cell_z]
296
end
297
298
if isperiodic(mesh, 1)
299
# Periodic boundary
300
left_neighbors[1, linear_indices[1, cell_y, cell_z]] = linear_indices[end,
301
cell_y,
302
cell_z]
303
else
304
left_neighbors[1, linear_indices[1, cell_y, cell_z]] = 0
305
end
306
end
307
308
# Neighbors in y-direction
309
for cell_z in 1:size(mesh, 3), cell_x in 1:size(mesh, 1)
310
# Inner elements
311
for cell_y in 2:size(mesh, 2)
312
element = linear_indices[cell_x, cell_y, cell_z]
313
left_neighbors[2, element] = linear_indices[cell_x, cell_y - 1, cell_z]
314
end
315
316
if isperiodic(mesh, 2)
317
# Periodic boundary
318
left_neighbors[2, linear_indices[cell_x, 1, cell_z]] = linear_indices[cell_x,
319
end,
320
cell_z]
321
else
322
left_neighbors[2, linear_indices[cell_x, 1, cell_z]] = 0
323
end
324
end
325
326
# Neighbors in z-direction
327
for cell_y in 1:size(mesh, 2), cell_x in 1:size(mesh, 1)
328
# Inner elements
329
for cell_z in 2:size(mesh, 3)
330
element = linear_indices[cell_x, cell_y, cell_z]
331
left_neighbors[3, element] = linear_indices[cell_x, cell_y, cell_z - 1]
332
end
333
334
if isperiodic(mesh, 3)
335
# Periodic boundary
336
left_neighbors[3, linear_indices[cell_x, cell_y, 1]] = linear_indices[cell_x,
337
cell_y,
338
end]
339
else
340
left_neighbors[3, linear_indices[cell_x, cell_y, 1]] = 0
341
end
342
end
343
344
return left_neighbors
345
end
346
347
# Compute the normal vectors for freestream-preserving FV method on curvilinear subcells, see
348
# equations (14) and (B.53) in:
349
# - Hennemann, Rueda-Ramírez, Hindenlang, Gassner (2020)
350
# A provably entropy stable subcell shock capturing approach for high order split form DG for the compressible Euler equations
351
# [arXiv: 2008.12044v2](https://arxiv.org/pdf/2008.12044)
352
function calc_normalvectors_subcell_fv!(normal_vectors_1, normal_vectors_2,
353
normal_vectors_3,
354
mesh::Union{StructuredMesh{3},
355
P4estMesh{3}, T8codeMesh{3}},
356
dg, cache_containers)
357
@unpack contravariant_vectors = cache_containers.elements
358
@unpack weights, derivative_matrix = dg.basis
359
360
@threaded for element in eachelement(dg, cache_containers)
361
# First contravariant vector/direction
362
for k in eachnode(dg), j in eachnode(dg)
363
# We do not store i = 1, as it is never used, see `calcflux_fv!`.
364
# => Store i = 2 at position 1
365
@views normal_vectors_1[:, 1, j, k, element] .= get_contravariant_vector(1,
366
contravariant_vectors,
367
1,
368
j,
369
k,
370
element)
371
for m in eachnode(dg)
372
wD_im = weights[1] * derivative_matrix[1, m]
373
@views normal_vectors_1[:, 1, j, k, element] .+= wD_im *
374
get_contravariant_vector(1,
375
contravariant_vectors,
376
m,
377
j,
378
k,
379
element)
380
end
381
382
for i in 2:(nnodes(dg) - 1) # Actual indices: 3 to nnodes(dg)
383
@views normal_vectors_1[:, i, j, k, element] .= normal_vectors_1[:,
384
i - 1,
385
j,
386
k,
387
element]
388
for m in eachnode(dg)
389
wD_im = weights[i] * derivative_matrix[i, m]
390
@views normal_vectors_1[:, i, j, k, element] .+= wD_im *
391
get_contravariant_vector(1,
392
contravariant_vectors,
393
m,
394
j,
395
k,
396
element)
397
end
398
end
399
end
400
401
# Second contravariant vector/direction
402
for k in eachnode(dg), i in eachnode(dg)
403
# We do not store j = 1, as it is never used.
404
# => Store physical j = 2 at position 1
405
@views normal_vectors_2[:, i, 1, k, element] .= get_contravariant_vector(2,
406
contravariant_vectors,
407
i,
408
1,
409
k,
410
element)
411
for m in eachnode(dg)
412
wD_jm = weights[1] * derivative_matrix[1, m]
413
@views normal_vectors_2[:, i, 1, k, element] .+= wD_jm *
414
get_contravariant_vector(2,
415
contravariant_vectors,
416
i,
417
m,
418
k,
419
element)
420
end
421
422
for j in 2:(nnodes(dg) - 1) # Actual indices: 3 to nnodes(dg)
423
@views normal_vectors_2[:, i, j, k, element] .= normal_vectors_2[:,
424
i,
425
j - 1,
426
k,
427
element]
428
429
for m in eachnode(dg)
430
wD_jm = weights[j] * derivative_matrix[j, m]
431
@views normal_vectors_2[:, i, j, k, element] .+= wD_jm *
432
get_contravariant_vector(2,
433
contravariant_vectors,
434
i,
435
m,
436
k,
437
element)
438
end
439
end
440
end
441
442
# Third contravariant vector/direction
443
for j in eachnode(dg), i in eachnode(dg)
444
# We do not store k = 1, as it is never used.
445
# => Store physical k = 2 at position 1
446
@views normal_vectors_3[:, i, j, 1, element] .= get_contravariant_vector(3,
447
contravariant_vectors,
448
i,
449
j,
450
1,
451
element)
452
for m in eachnode(dg)
453
wD_km = weights[1] * derivative_matrix[1, m]
454
@views normal_vectors_3[:, i, j, 1, element] .+= wD_km *
455
get_contravariant_vector(3,
456
contravariant_vectors,
457
i,
458
j,
459
m,
460
element)
461
end
462
463
for k in 2:(nnodes(dg) - 1) # Actual indices: 3 to nnodes(dg)
464
@views normal_vectors_3[:, i, j, k, element] .= normal_vectors_3[:,
465
i,
466
j,
467
k - 1,
468
element]
469
for m in eachnode(dg)
470
wD_km = weights[k] * derivative_matrix[k, m]
471
@views normal_vectors_3[:, i, j, k, element] .+= wD_km *
472
get_contravariant_vector(3,
473
contravariant_vectors,
474
i,
475
j,
476
m,
477
element)
478
end
479
end
480
end
481
end
482
483
return nothing
484
end
485
486
# Used for both fixed (`StructuredMesh{3}`)
487
# and adaptive meshes (`P4estMesh{3}` or `T8codeMesh{3}`)
488
mutable struct NormalVectorContainer3D{RealT <: Real} <:
489
AbstractNormalVectorContainer
490
const n_nodes::Int
491
# For normal vectors computed from first contravariant vectors
492
normal_vectors_1::Array{RealT, 5} # [NDIMS, NNODES - 1, NNODES, NNODES, NELEMENTS]
493
# For normal vectors computed from second contravariant vectors
494
normal_vectors_2::Array{RealT, 5} # [NDIMS, NNODES, NNODES - 1, NNODES, NELEMENTS]
495
# For normal vectors computed from third contravariant vectors
496
normal_vectors_3::Array{RealT, 5} # [NDIMS, NNODES, NNODES, NNODES - 1, NELEMENTS]
497
498
# internal `resize!`able storage
499
_normal_vectors_1::Vector{RealT}
500
_normal_vectors_2::Vector{RealT}
501
_normal_vectors_3::Vector{RealT}
502
end
503
504
function NormalVectorContainer3D(mesh::Union{StructuredMesh{3},
505
P4estMesh{3}, T8codeMesh{3}},
506
dg, cache_containers)
507
@unpack contravariant_vectors = cache_containers.elements
508
RealT = eltype(contravariant_vectors)
509
n_elements = nelements(dg, cache_containers)
510
n_nodes = nnodes(dg.basis)
511
512
_normal_vectors_1 = Vector{RealT}(undef,
513
3 * (n_nodes - 1) * n_nodes * n_nodes *
514
n_elements)
515
normal_vectors_1 = unsafe_wrap(Array, pointer(_normal_vectors_1),
516
(3, n_nodes - 1, n_nodes, n_nodes,
517
n_elements))
518
519
_normal_vectors_2 = Vector{RealT}(undef,
520
3 * n_nodes * (n_nodes - 1) * n_nodes *
521
n_elements)
522
normal_vectors_2 = unsafe_wrap(Array, pointer(_normal_vectors_2),
523
(3, n_nodes, n_nodes - 1, n_nodes,
524
n_elements))
525
526
_normal_vectors_3 = Vector{RealT}(undef,
527
3 * n_nodes * n_nodes * (n_nodes - 1) *
528
n_elements)
529
normal_vectors_3 = unsafe_wrap(Array, pointer(_normal_vectors_3),
530
(3, n_nodes, n_nodes, n_nodes - 1,
531
n_elements))
532
533
calc_normalvectors_subcell_fv!(normal_vectors_1, normal_vectors_2, normal_vectors_3,
534
mesh, dg, cache_containers)
535
536
return NormalVectorContainer3D{RealT}(n_nodes,
537
normal_vectors_1, normal_vectors_2,
538
normal_vectors_3,
539
_normal_vectors_1, _normal_vectors_2,
540
_normal_vectors_3)
541
end
542
543
# Required only for adaptive meshes (`P4estMesh` or `T8codeMesh`)
544
function Base.resize!(normal_vectors::NormalVectorContainer3D, capacity)
545
@unpack n_nodes, _normal_vectors_1, _normal_vectors_2, _normal_vectors_3 = normal_vectors
546
ArrayType = storage_type(normal_vectors)
547
548
resize!(_normal_vectors_1, 3 * (n_nodes - 1) * n_nodes * n_nodes * capacity)
549
normal_vectors.normal_vectors_1 = unsafe_wrap_or_alloc(ArrayType, _normal_vectors_1,
550
(3,
551
n_nodes - 1,
552
n_nodes,
553
n_nodes,
554
capacity))
555
556
resize!(_normal_vectors_2, 3 * n_nodes * (n_nodes - 1) * n_nodes * capacity)
557
normal_vectors.normal_vectors_2 = unsafe_wrap_or_alloc(ArrayType, _normal_vectors_2,
558
(3,
559
n_nodes,
560
n_nodes - 1,
561
n_nodes,
562
capacity))
563
564
resize!(_normal_vectors_3, 3 * n_nodes * n_nodes * (n_nodes - 1) * capacity)
565
normal_vectors.normal_vectors_3 = unsafe_wrap_or_alloc(ArrayType, _normal_vectors_3,
566
(3,
567
n_nodes,
568
n_nodes,
569
n_nodes - 1,
570
capacity))
571
572
return nothing
573
end
574
575
# Required only for adaptive meshes (`P4estMesh` or `T8codeMesh`)
576
function init_normal_vectors!(normal_vectors::NormalVectorContainer3D,
577
mesh::Union{P4estMesh{3}, T8codeMesh{3}}, dg, cache)
578
@unpack normal_vectors_1, normal_vectors_2, normal_vectors_3 = normal_vectors
579
calc_normalvectors_subcell_fv!(normal_vectors_1, normal_vectors_2, normal_vectors_3,
580
mesh, dg, cache)
581
582
return nothing
583
end
584
end # @muladd
585
586