Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/solvers/dgsem_tree/containers_3d.jl
5591 views
1
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
2
# Since these FMAs can increase the performance of many numerical algorithms,
3
# we need to opt-in explicitly.
4
# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
5
@muladd begin
6
#! format: noindent
7
8
# Container data structure (structure-of-arrays style) for DG elements
9
mutable struct TreeElementContainer3D{RealT <: Real, uEltype <: Real} <:
10
AbstractTreeElementContainer
11
inverse_jacobian::Vector{RealT} # [elements]
12
node_coordinates::Array{RealT, 5} # [orientation, i, j, k, elements]
13
surface_flux_values::Array{uEltype, 5} # [variables, i, j, direction, elements]
14
cell_ids::Vector{Int} # [elements]
15
# internal `resize!`able storage
16
_node_coordinates::Vector{RealT}
17
_surface_flux_values::Vector{uEltype}
18
end
19
20
# Only one-dimensional `Array`s are `resize!`able in Julia.
21
# Hence, we use `Vector`s as internal storage and `resize!`
22
# them whenever needed. Then, we reuse the same memory by
23
# `unsafe_wrap`ping multi-dimensional `Array`s around the
24
# internal storage.
25
function Base.resize!(elements::TreeElementContainer3D, capacity)
26
n_nodes = nnodes(elements)
27
n_variables = nvariables(elements)
28
@unpack _node_coordinates, _surface_flux_values,
29
inverse_jacobian, cell_ids = elements
30
31
resize!(inverse_jacobian, capacity)
32
33
resize!(_node_coordinates, 3 * n_nodes * n_nodes * n_nodes * capacity)
34
elements.node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates),
35
(3, n_nodes, n_nodes, n_nodes, capacity))
36
37
resize!(_surface_flux_values, n_variables * n_nodes * n_nodes * 2 * 3 * capacity)
38
elements.surface_flux_values = unsafe_wrap(Array, pointer(_surface_flux_values),
39
(n_variables, n_nodes, n_nodes, 2 * 3,
40
capacity))
41
42
resize!(cell_ids, capacity)
43
44
return nothing
45
end
46
47
function TreeElementContainer3D{RealT, uEltype}(capacity::Integer, n_variables,
48
n_nodes) where {RealT <: Real,
49
uEltype <: Real}
50
nan_RealT = convert(RealT, NaN)
51
nan_uEltype = convert(uEltype, NaN)
52
53
# Initialize fields with defaults
54
inverse_jacobian = fill(nan_RealT, capacity)
55
56
_node_coordinates = fill(nan_RealT, 3 * n_nodes * n_nodes * n_nodes * capacity)
57
node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates),
58
(3, n_nodes, n_nodes, n_nodes, capacity))
59
60
_surface_flux_values = fill(nan_uEltype,
61
n_variables * n_nodes * n_nodes * 2 * 3 * capacity)
62
surface_flux_values = unsafe_wrap(Array, pointer(_surface_flux_values),
63
(n_variables, n_nodes, n_nodes, 2 * 3, capacity))
64
65
cell_ids = fill(typemin(Int), capacity)
66
67
return TreeElementContainer3D{RealT, uEltype}(inverse_jacobian, node_coordinates,
68
surface_flux_values, cell_ids,
69
_node_coordinates,
70
_surface_flux_values)
71
end
72
73
# Create element container and initialize element data
74
function init_elements(cell_ids, mesh::TreeMesh3D,
75
equations::AbstractEquations{3},
76
basis, ::Type{RealT},
77
::Type{uEltype}) where {RealT <: Real, uEltype <: Real}
78
# Initialize container
79
n_elements = length(cell_ids)
80
elements = TreeElementContainer3D{RealT, uEltype}(n_elements, nvariables(equations),
81
nnodes(basis))
82
83
init_elements!(elements, cell_ids, mesh, basis)
84
return elements
85
end
86
87
function init_elements!(elements, cell_ids, mesh::TreeMesh3D, basis)
88
nodes = get_nodes(basis)
89
# Compute the length of the 1D reference interval by integrating
90
# the function with constant value unity on the corresponding
91
# element data type (using \circ)
92
reference_length = integrate(one eltype, nodes, basis)
93
# Compute the offset of the midpoint of the 1D reference interval
94
# (its difference from zero)
95
reference_offset = (first(nodes) + last(nodes)) / 2
96
97
# Store cell ids
98
elements.cell_ids .= cell_ids
99
100
# Calculate inverse Jacobian and node coordinates
101
for element in eachelement(elements)
102
# Get cell id
103
cell_id = cell_ids[element]
104
105
# Get cell length
106
dx = length_at_cell(mesh.tree, cell_id)
107
108
# Calculate inverse Jacobian
109
jacobian = dx / reference_length
110
elements.inverse_jacobian[element] = inv(jacobian)
111
112
# Calculate node coordinates
113
# Note that the `tree_coordinates` are the midpoints of the cells.
114
# Hence, we need to add an offset for `nodes` with a midpoint
115
# different from zero.
116
for k in eachnode(basis), j in eachnode(basis), i in eachnode(basis)
117
elements.node_coordinates[1, i, j, k, element] = (mesh.tree.coordinates[1,
118
cell_id] +
119
jacobian * (nodes[i] -
120
reference_offset))
121
elements.node_coordinates[2, i, j, k, element] = (mesh.tree.coordinates[2,
122
cell_id] +
123
jacobian * (nodes[j] -
124
reference_offset))
125
elements.node_coordinates[3, i, j, k, element] = (mesh.tree.coordinates[3,
126
cell_id] +
127
jacobian * (nodes[k] -
128
reference_offset))
129
end
130
end
131
132
return elements
133
end
134
135
# Container data structure (structure-of-arrays style) for DG interfaces
136
mutable struct TreeInterfaceContainer3D{uEltype <: Real} <:
137
AbstractTreeInterfaceContainer
138
u::Array{uEltype, 5} # [leftright, variables, i, j, interfaces]
139
neighbor_ids::Matrix{Int} # [leftright, interfaces]
140
orientations::Vector{Int} # [interfaces]
141
# internal `resize!`able storage
142
_u::Vector{uEltype}
143
_neighbor_ids::Vector{Int}
144
end
145
146
# See explanation of Base.resize! for the element container
147
function Base.resize!(interfaces::TreeInterfaceContainer3D, capacity)
148
n_nodes = nnodes(interfaces)
149
n_variables = nvariables(interfaces)
150
@unpack _u, _neighbor_ids, orientations = interfaces
151
152
resize!(_u, 2 * n_variables * n_nodes * n_nodes * capacity)
153
interfaces.u = unsafe_wrap(Array, pointer(_u),
154
(2, n_variables, n_nodes, n_nodes, capacity))
155
156
resize!(_neighbor_ids, 2 * capacity)
157
interfaces.neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids),
158
(2, capacity))
159
160
resize!(orientations, capacity)
161
162
return nothing
163
end
164
165
function TreeInterfaceContainer3D{uEltype}(capacity::Integer, n_variables,
166
n_nodes) where {uEltype <: Real}
167
nan = convert(uEltype, NaN)
168
169
# Initialize fields with defaults
170
_u = fill(nan, 2 * n_variables * n_nodes * n_nodes * capacity)
171
u = unsafe_wrap(Array, pointer(_u),
172
(2, n_variables, n_nodes, n_nodes, capacity))
173
174
_neighbor_ids = fill(typemin(Int), 2 * capacity)
175
neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids),
176
(2, capacity))
177
178
orientations = fill(typemin(Int), capacity)
179
180
return TreeInterfaceContainer3D{uEltype}(u, neighbor_ids, orientations,
181
_u, _neighbor_ids)
182
end
183
184
# Create interface container and initialize interface data in `elements`.
185
function init_interfaces(cell_ids, mesh::TreeMesh3D,
186
elements::TreeElementContainer3D)
187
# Initialize container
188
n_interfaces = count_required_interfaces(mesh, cell_ids)
189
interfaces = TreeInterfaceContainer3D{eltype(elements)}(n_interfaces,
190
nvariables(elements),
191
nnodes(elements))
192
193
# Connect elements with interfaces
194
init_interfaces!(interfaces, elements, mesh)
195
return interfaces
196
end
197
198
# Count the number of interfaces that need to be created
199
function count_required_interfaces(mesh::TreeMesh3D, cell_ids)
200
count = 0
201
202
# Iterate over all cells
203
for cell_id in cell_ids
204
for direction in eachdirection(mesh.tree)
205
# Only count interfaces in positive direction to avoid double counting
206
if direction % 2 == 1
207
continue
208
end
209
210
# If no neighbor exists, current cell is small or at boundary and thus we need a mortar
211
if !has_neighbor(mesh.tree, cell_id, direction)
212
continue
213
end
214
215
# Skip if neighbor has children
216
neighbor_id = mesh.tree.neighbor_ids[direction, cell_id]
217
if has_children(mesh.tree, neighbor_id)
218
continue
219
end
220
221
count += 1
222
end
223
end
224
225
return count
226
end
227
228
# Initialize connectivity between elements and interfaces
229
function init_interfaces!(interfaces, elements, mesh::TreeMesh3D)
230
# Construct cell -> element mapping for easier algorithm implementation
231
tree = mesh.tree
232
c2e = zeros(Int, length(tree))
233
for element in eachelement(elements)
234
c2e[elements.cell_ids[element]] = element
235
end
236
237
# Reset interface count
238
count = 0
239
240
# Iterate over all elements to find neighbors and to connect via interfaces
241
for element in eachelement(elements)
242
# Get cell id
243
cell_id = elements.cell_ids[element]
244
245
# Loop over directions
246
for direction in eachdirection(mesh.tree)
247
# Only create interfaces in positive direction
248
if direction % 2 == 1
249
continue
250
end
251
252
# If no neighbor exists, current cell is small and thus we need a mortar
253
if !has_neighbor(mesh.tree, cell_id, direction)
254
continue
255
end
256
257
# Skip if neighbor has children
258
neighbor_cell_id = mesh.tree.neighbor_ids[direction, cell_id]
259
if has_children(mesh.tree, neighbor_cell_id)
260
continue
261
end
262
263
# Create interface between elements (1 -> "left" of interface, 2 -> "right" of interface)
264
count += 1
265
interfaces.neighbor_ids[2, count] = c2e[neighbor_cell_id]
266
interfaces.neighbor_ids[1, count] = element
267
268
# Set orientation (x -> 1, y -> 2, z -> 3)
269
if direction in (1, 2)
270
interfaces.orientations[count] = 1
271
elseif direction in (3, 4)
272
interfaces.orientations[count] = 2
273
else
274
interfaces.orientations[count] = 3
275
end
276
end
277
end
278
279
@assert count==ninterfaces(interfaces) ("Actual interface count ($count) does not match "*
280
"expectations $(ninterfaces(interfaces))")
281
end
282
283
# Container data structure (structure-of-arrays style) for DG boundaries
284
mutable struct TreeBoundaryContainer3D{RealT <: Real, uEltype <: Real} <:
285
AbstractTreeBoundaryContainer
286
u::Array{uEltype, 5} # [leftright, variables, i, j, boundaries]
287
neighbor_ids::Vector{Int} # [boundaries]
288
orientations::Vector{Int} # [boundaries]
289
neighbor_sides::Vector{Int} # [boundaries]
290
node_coordinates::Array{RealT, 4} # [orientation, i, j, elements]
291
n_boundaries_per_direction::SVector{6, Int} # [direction]
292
# internal `resize!`able storage
293
_u::Vector{uEltype}
294
_node_coordinates::Vector{RealT}
295
end
296
297
# See explanation of Base.resize! for the element container
298
function Base.resize!(boundaries::TreeBoundaryContainer3D, capacity)
299
n_nodes = nnodes(boundaries)
300
n_variables = nvariables(boundaries)
301
@unpack _u, _node_coordinates,
302
neighbor_ids, orientations, neighbor_sides = boundaries
303
304
resize!(_u, 2 * n_variables * n_nodes * n_nodes * capacity)
305
boundaries.u = unsafe_wrap(Array, pointer(_u),
306
(2, n_variables, n_nodes, n_nodes, capacity))
307
308
resize!(_node_coordinates, 3 * n_nodes * n_nodes * capacity)
309
boundaries.node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates),
310
(3, n_nodes, n_nodes, capacity))
311
312
resize!(neighbor_ids, capacity)
313
314
resize!(orientations, capacity)
315
316
resize!(neighbor_sides, capacity)
317
318
return nothing
319
end
320
321
function TreeBoundaryContainer3D{RealT, uEltype}(capacity::Integer, n_variables,
322
n_nodes) where {RealT <: Real,
323
uEltype <: Real}
324
nan_RealT = convert(RealT, NaN)
325
nan_uEltype = convert(uEltype, NaN)
326
327
# Initialize fields with defaults
328
_u = fill(nan_uEltype, 2 * n_variables * n_nodes * n_nodes * capacity)
329
u = unsafe_wrap(Array, pointer(_u),
330
(2, n_variables, n_nodes, n_nodes, capacity))
331
332
neighbor_ids = fill(typemin(Int), capacity)
333
334
orientations = fill(typemin(Int), capacity)
335
336
neighbor_sides = fill(typemin(Int), capacity)
337
338
_node_coordinates = fill(nan_RealT, 3 * n_nodes * n_nodes * capacity)
339
node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates),
340
(3, n_nodes, n_nodes, capacity))
341
342
n_boundaries_per_direction = SVector(0, 0, 0, 0, 0, 0)
343
344
return TreeBoundaryContainer3D{RealT, uEltype}(u, neighbor_ids, orientations,
345
neighbor_sides,
346
node_coordinates,
347
n_boundaries_per_direction,
348
_u, _node_coordinates)
349
end
350
351
# Create boundaries container and initialize boundary data in `elements`.
352
function init_boundaries(cell_ids, mesh::TreeMesh3D,
353
elements::TreeElementContainer3D, basis)
354
# Initialize container
355
n_boundaries = count_required_boundaries(mesh, cell_ids)
356
boundaries = TreeBoundaryContainer3D{real(elements), eltype(elements)}(n_boundaries,
357
nvariables(elements),
358
nnodes(elements))
359
360
# Connect elements with boundaries
361
init_boundaries!(boundaries, elements, mesh, basis)
362
return boundaries
363
end
364
365
# Count the number of boundaries that need to be created
366
function count_required_boundaries(mesh::TreeMesh3D, cell_ids)
367
count = 0
368
369
# Iterate over all cells
370
for cell_id in cell_ids
371
for direction in eachdirection(mesh.tree)
372
# If neighbor exists, current cell is not at a boundary
373
if has_neighbor(mesh.tree, cell_id, direction)
374
continue
375
end
376
377
# If coarse neighbor exists, current cell is not at a boundary
378
if has_coarse_neighbor(mesh.tree, cell_id, direction)
379
continue
380
end
381
382
# No neighbor exists in this direction -> must be a boundary
383
count += 1
384
end
385
end
386
387
return count
388
end
389
390
# Initialize connectivity between elements and boundaries
391
function init_boundaries!(boundaries, elements, mesh::TreeMesh3D, basis)
392
# Reset boundaries count
393
count = 0
394
395
# Initialize boundary counts
396
counts_per_direction = MVector(0, 0, 0, 0, 0, 0)
397
398
# OBS! Iterate over directions first, then over elements, and count boundaries in each direction
399
# Rationale: This way the boundaries are internally sorted by the directions -x, +x, -y etc.,
400
# obviating the need to store the boundary condition to be applied explicitly.
401
# Loop over directions
402
for direction in eachdirection(mesh.tree)
403
# Iterate over all elements to find missing neighbors and to connect to boundaries
404
for element in eachelement(elements)
405
# Get cell id
406
cell_id = elements.cell_ids[element]
407
408
# If neighbor exists, current cell is not at a boundary
409
if has_neighbor(mesh.tree, cell_id, direction)
410
continue
411
end
412
413
# If coarse neighbor exists, current cell is not at a boundary
414
if has_coarse_neighbor(mesh.tree, cell_id, direction)
415
continue
416
end
417
418
# Create boundary
419
count += 1
420
counts_per_direction[direction] += 1
421
422
# Set neighbor element id
423
boundaries.neighbor_ids[count] = element
424
425
# Set neighbor side, which denotes the direction (1 -> negative, 2 -> positive) of the element
426
if iseven(direction)
427
boundaries.neighbor_sides[count] = 1
428
else
429
boundaries.neighbor_sides[count] = 2
430
end
431
432
# Set orientation (x -> 1, y -> 2)
433
if direction in (1, 2)
434
boundaries.orientations[count] = 1
435
elseif direction in (3, 4)
436
boundaries.orientations[count] = 2
437
else
438
boundaries.orientations[count] = 3
439
end
440
441
# Store node coordinates
442
enc = elements.node_coordinates
443
if direction == 1 # -x direction
444
boundaries.node_coordinates[:, :, :, count] .= enc[:, 1, :, :, element]
445
elseif direction == 2 # +x direction
446
boundaries.node_coordinates[:, :, :, count] .= enc[:, end, :, :,
447
element]
448
elseif direction == 3 # -y direction
449
boundaries.node_coordinates[:, :, :, count] .= enc[:, :, 1, :, element]
450
elseif direction == 4 # +y direction
451
boundaries.node_coordinates[:, :, :, count] .= enc[:, :, end, :,
452
element]
453
elseif direction == 5 # -z direction
454
boundaries.node_coordinates[:, :, :, count] .= enc[:, :, :, 1, element]
455
elseif direction == 6 # +z direction
456
boundaries.node_coordinates[:, :, :, count] .= enc[:, :, :, end,
457
element]
458
else
459
error("should not happen")
460
end
461
end
462
end
463
464
@assert count==nboundaries(boundaries) ("Actual boundaries count ($count) does not match "*
465
"expectations $(nboundaries(boundaries))")
466
@assert sum(counts_per_direction) == count
467
468
boundaries.n_boundaries_per_direction = SVector(counts_per_direction)
469
470
return SVector(counts_per_direction)
471
end
472
473
# Container data structure (structure-of-arrays style) for DG L2 mortars
474
# Positions/directions for orientations = 1, large_sides = 2:
475
# mortar is orthogonal to x-axis, large side is in positive coordinate direction wrt mortar
476
# /----------------------------\ /----------------------------\
477
# | | | | |
478
# | upper, left | upper, right | | |
479
# | 3 | 4 | | |
480
# | | | | large |
481
# |-------------|--------------| | 5 |
482
# z | | | | |
483
# | lower, left | lower, right | | |
484
# ^ | 1 | 2 | | |
485
# | | | | | |
486
# | \----------------------------/ \----------------------------/
487
# |
488
# ⋅----> y
489
# Left and right are always wrt to a coordinate direction:
490
# * left is always the negative direction
491
# * right is always the positive direction
492
#
493
# Left and right are used *both* for the numbering of the mortar faces *and* for the position of the
494
# elements with respect to the axis orthogonal to the mortar.
495
mutable struct TreeL2MortarContainer3D{uEltype <: Real} <: AbstractTreeL2MortarContainer
496
u_upper_left::Array{uEltype, 5} # [leftright, variables, i, j, mortars]
497
u_upper_right::Array{uEltype, 5} # [leftright, variables, i, j, mortars]
498
u_lower_left::Array{uEltype, 5} # [leftright, variables, i, j, mortars]
499
u_lower_right::Array{uEltype, 5} # [leftright, variables, i, j, mortars]
500
neighbor_ids::Array{Int, 2} # [position, mortars]
501
# Large sides: left -> 1, right -> 2
502
large_sides::Vector{Int} # [mortars]
503
orientations::Vector{Int} # [mortars]
504
# internal `resize!`able storage
505
_u_upper_left::Vector{uEltype}
506
_u_upper_right::Vector{uEltype}
507
_u_lower_left::Vector{uEltype}
508
_u_lower_right::Vector{uEltype}
509
_neighbor_ids::Vector{Int}
510
end
511
512
# Return number of equation variables
513
nvariables(mortars::TreeL2MortarContainer3D) = size(mortars.u_upper_left, 2)
514
# Return number of mortar nodes (L2 mortars are only h-adaptive, not p-adaptive)
515
nnodes(mortars::TreeL2MortarContainer3D) = size(mortars.u_upper_left, 3)
516
517
# See explanation of Base.resize! for the element container
518
function Base.resize!(mortars::TreeL2MortarContainer3D, capacity)
519
n_nodes = nnodes(mortars)
520
n_variables = nvariables(mortars)
521
@unpack _u_upper_left, _u_upper_right, _u_lower_left, _u_lower_right,
522
_neighbor_ids, large_sides, orientations = mortars
523
524
resize!(_u_upper_left, 2 * n_variables * n_nodes * n_nodes * capacity)
525
mortars.u_upper_left = unsafe_wrap(Array, pointer(_u_upper_left),
526
(2, n_variables, n_nodes, n_nodes, capacity))
527
528
resize!(_u_upper_right, 2 * n_variables * n_nodes * n_nodes * capacity)
529
mortars.u_upper_right = unsafe_wrap(Array, pointer(_u_upper_right),
530
(2, n_variables, n_nodes, n_nodes, capacity))
531
532
resize!(_u_lower_left, 2 * n_variables * n_nodes * n_nodes * capacity)
533
mortars.u_lower_left = unsafe_wrap(Array, pointer(_u_lower_left),
534
(2, n_variables, n_nodes, n_nodes, capacity))
535
536
resize!(_u_lower_right, 2 * n_variables * n_nodes * n_nodes * capacity)
537
mortars.u_lower_right = unsafe_wrap(Array, pointer(_u_lower_right),
538
(2, n_variables, n_nodes, n_nodes, capacity))
539
540
resize!(_neighbor_ids, 5 * capacity)
541
mortars.neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids),
542
(5, capacity))
543
544
resize!(large_sides, capacity)
545
546
resize!(orientations, capacity)
547
548
return nothing
549
end
550
551
function TreeL2MortarContainer3D{uEltype}(capacity::Integer, n_variables,
552
n_nodes) where {uEltype <: Real}
553
nan = convert(uEltype, NaN)
554
555
# Initialize fields with defaults
556
_u_upper_left = fill(nan, 2 * n_variables * n_nodes * n_nodes * capacity)
557
u_upper_left = unsafe_wrap(Array, pointer(_u_upper_left),
558
(2, n_variables, n_nodes, n_nodes, capacity))
559
560
_u_upper_right = fill(nan, 2 * n_variables * n_nodes * n_nodes * capacity)
561
u_upper_right = unsafe_wrap(Array, pointer(_u_upper_right),
562
(2, n_variables, n_nodes, n_nodes, capacity))
563
564
_u_lower_left = fill(nan, 2 * n_variables * n_nodes * n_nodes * capacity)
565
u_lower_left = unsafe_wrap(Array, pointer(_u_lower_left),
566
(2, n_variables, n_nodes, n_nodes, capacity))
567
568
_u_lower_right = fill(nan, 2 * n_variables * n_nodes * n_nodes * capacity)
569
u_lower_right = unsafe_wrap(Array, pointer(_u_lower_right),
570
(2, n_variables, n_nodes, n_nodes, capacity))
571
572
_neighbor_ids = fill(typemin(Int), 5 * capacity)
573
neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids),
574
(5, capacity))
575
576
large_sides = fill(typemin(Int), capacity)
577
578
orientations = fill(typemin(Int), capacity)
579
580
return TreeL2MortarContainer3D{uEltype}(u_upper_left, u_upper_right,
581
u_lower_left, u_lower_right,
582
neighbor_ids, large_sides, orientations,
583
_u_upper_left, _u_upper_right,
584
_u_lower_left, _u_lower_right,
585
_neighbor_ids)
586
end
587
588
# Allow printing container contents
589
function Base.show(io::IO, ::MIME"text/plain", c::TreeL2MortarContainer3D)
590
@nospecialize c # reduce precompilation time
591
592
println(io, '*'^20)
593
for idx in CartesianIndices(c.u_upper_left)
594
println(io, "c.u_upper_left[$idx] = $(c.u_upper_left[idx])")
595
end
596
for idx in CartesianIndices(c.u_upper_right)
597
println(io, "c.u_upper_right[$idx] = $(c.u_upper_right[idx])")
598
end
599
for idx in CartesianIndices(c.u_lower_left)
600
println(io, "c.u_lower_left[$idx] = $(c.u_lower_left[idx])")
601
end
602
for idx in CartesianIndices(c.u_lower_right)
603
println(io, "c.u_lower_right[$idx] = $(c.u_lower_right[idx])")
604
end
605
println(io, "transpose(c.neighbor_ids) = $(transpose(c.neighbor_ids))")
606
println(io, "c.large_sides = $(c.large_sides)")
607
println(io, "c.orientations = $(c.orientations)")
608
print(io, '*'^20)
609
return nothing
610
end
611
612
# Create mortar container and initialize mortar data in `elements`.
613
function init_mortars(cell_ids, mesh::TreeMesh3D,
614
elements::TreeElementContainer3D,
615
mortar::LobattoLegendreMortarL2)
616
# Initialize containers
617
n_mortars = count_required_mortars(mesh, cell_ids)
618
mortars = TreeL2MortarContainer3D{eltype(elements)}(n_mortars, nvariables(elements),
619
nnodes(elements))
620
621
# Connect elements with mortars
622
init_mortars!(mortars, elements, mesh)
623
return mortars
624
end
625
626
# Count the number of mortars that need to be created
627
function count_required_mortars(mesh::TreeMesh3D, cell_ids)
628
count = 0
629
630
# Iterate over all cells and count mortars from perspective of coarse cells
631
for cell_id in cell_ids
632
for direction in eachdirection(mesh.tree)
633
# If no neighbor exists, cell is small with large neighbor or at boundary -> do nothing
634
if !has_neighbor(mesh.tree, cell_id, direction)
635
continue
636
end
637
638
# If neighbor has no children, this is a conforming interface -> do nothing
639
neighbor_id = mesh.tree.neighbor_ids[direction, cell_id]
640
if !has_children(mesh.tree, neighbor_id)
641
continue
642
end
643
644
count += 1
645
end
646
end
647
648
return count
649
end
650
651
# Initialize connectivity between elements and mortars
652
function init_mortars!(mortars, elements, mesh::TreeMesh3D)
653
# Construct cell -> element mapping for easier algorithm implementation
654
tree = mesh.tree
655
c2e = zeros(Int, length(tree))
656
for element in eachelement(elements)
657
c2e[elements.cell_ids[element]] = element
658
end
659
660
# Reset interface count
661
count = 0
662
663
# Iterate over all elements to find neighbors and to connect via interfaces
664
for element in eachelement(elements)
665
# Get cell id
666
cell_id = elements.cell_ids[element]
667
668
for direction in eachdirection(mesh.tree)
669
# If no neighbor exists, cell is small with large neighbor -> do nothing
670
if !has_neighbor(mesh.tree, cell_id, direction)
671
continue
672
end
673
674
# If neighbor has no children, this is a conforming interface -> do nothing
675
neighbor_cell_id = mesh.tree.neighbor_ids[direction, cell_id]
676
if !has_children(mesh.tree, neighbor_cell_id)
677
continue
678
end
679
680
# Create mortar between elements (3 possible orientations):
681
#
682
# mortar in x-direction:
683
# 1 -> small element in lower, left position (-y, -z)
684
# 2 -> small element in lower, right position (+y, -z)
685
# 3 -> small element in upper, left position (-y, +z)
686
# 4 -> small element in upper, right position (+y, +z)
687
#
688
# mortar in y-direction:
689
# 1 -> small element in lower, left position (-x, -z)
690
# 2 -> small element in lower, right position (+x, -z)
691
# 3 -> small element in upper, left position (-x, +z)
692
# 4 -> small element in upper, right position (+x, +z)
693
#
694
# mortar in z-direction:
695
# 1 -> small element in lower, left position (-x, -y)
696
# 2 -> small element in lower, right position (+x, -y)
697
# 3 -> small element in upper, left position (-x, +y)
698
# 4 -> small element in upper, right position (+x, +y)
699
#
700
# Always the case:
701
# 5 -> large element
702
#
703
count += 1
704
mortars.neighbor_ids[5, count] = element
705
706
# Directions are from the perspective of the large element
707
# ("Where are the small elements? Ah, in the ... direction!")
708
if direction == 1 # -x
709
mortars.neighbor_ids[1, count] = c2e[mesh.tree.child_ids[2,
710
neighbor_cell_id]]
711
mortars.neighbor_ids[2, count] = c2e[mesh.tree.child_ids[4,
712
neighbor_cell_id]]
713
mortars.neighbor_ids[3, count] = c2e[mesh.tree.child_ids[6,
714
neighbor_cell_id]]
715
mortars.neighbor_ids[4, count] = c2e[mesh.tree.child_ids[8,
716
neighbor_cell_id]]
717
elseif direction == 2 # +x
718
mortars.neighbor_ids[1, count] = c2e[mesh.tree.child_ids[1,
719
neighbor_cell_id]]
720
mortars.neighbor_ids[2, count] = c2e[mesh.tree.child_ids[3,
721
neighbor_cell_id]]
722
mortars.neighbor_ids[3, count] = c2e[mesh.tree.child_ids[5,
723
neighbor_cell_id]]
724
mortars.neighbor_ids[4, count] = c2e[mesh.tree.child_ids[7,
725
neighbor_cell_id]]
726
elseif direction == 3 # -y
727
mortars.neighbor_ids[1, count] = c2e[mesh.tree.child_ids[3,
728
neighbor_cell_id]]
729
mortars.neighbor_ids[2, count] = c2e[mesh.tree.child_ids[4,
730
neighbor_cell_id]]
731
mortars.neighbor_ids[3, count] = c2e[mesh.tree.child_ids[7,
732
neighbor_cell_id]]
733
mortars.neighbor_ids[4, count] = c2e[mesh.tree.child_ids[8,
734
neighbor_cell_id]]
735
elseif direction == 4 # +y
736
mortars.neighbor_ids[1, count] = c2e[mesh.tree.child_ids[1,
737
neighbor_cell_id]]
738
mortars.neighbor_ids[2, count] = c2e[mesh.tree.child_ids[2,
739
neighbor_cell_id]]
740
mortars.neighbor_ids[3, count] = c2e[mesh.tree.child_ids[5,
741
neighbor_cell_id]]
742
mortars.neighbor_ids[4, count] = c2e[mesh.tree.child_ids[6,
743
neighbor_cell_id]]
744
elseif direction == 5 # -z
745
mortars.neighbor_ids[1, count] = c2e[mesh.tree.child_ids[5,
746
neighbor_cell_id]]
747
mortars.neighbor_ids[2, count] = c2e[mesh.tree.child_ids[6,
748
neighbor_cell_id]]
749
mortars.neighbor_ids[3, count] = c2e[mesh.tree.child_ids[7,
750
neighbor_cell_id]]
751
mortars.neighbor_ids[4, count] = c2e[mesh.tree.child_ids[8,
752
neighbor_cell_id]]
753
elseif direction == 6 # +z
754
mortars.neighbor_ids[1, count] = c2e[mesh.tree.child_ids[1,
755
neighbor_cell_id]]
756
mortars.neighbor_ids[2, count] = c2e[mesh.tree.child_ids[2,
757
neighbor_cell_id]]
758
mortars.neighbor_ids[3, count] = c2e[mesh.tree.child_ids[3,
759
neighbor_cell_id]]
760
mortars.neighbor_ids[4, count] = c2e[mesh.tree.child_ids[4,
761
neighbor_cell_id]]
762
else
763
error("should not happen")
764
end
765
766
# Set large side, which denotes the direction (1 -> negative, 2 -> positive) of the large side
767
if iseven(direction)
768
mortars.large_sides[count] = 1
769
else
770
mortars.large_sides[count] = 2
771
end
772
773
# Set orientation (x -> 1, y -> 2, z -> 3)
774
if direction in (1, 2)
775
mortars.orientations[count] = 1
776
elseif direction in (3, 4)
777
mortars.orientations[count] = 2
778
else
779
mortars.orientations[count] = 3
780
end
781
end
782
end
783
784
@assert count==nmortars(mortars) ("Actual mortar count ($count) does not match "*
785
"expectations $(nmortars(mortars))")
786
end
787
788
mutable struct ContainerAntidiffusiveFlux3D{uEltype <: Real} <:
789
AbstractContainerAntidiffusiveFlux
790
antidiffusive_flux1_L::Array{uEltype, 5} # [variables, i, j, k, elements]
791
antidiffusive_flux1_R::Array{uEltype, 5} # [variables, i, j, k, elements]
792
antidiffusive_flux2_L::Array{uEltype, 5} # [variables, i, j, k, elements]
793
antidiffusive_flux2_R::Array{uEltype, 5} # [variables, i, j, k, elements]
794
antidiffusive_flux3_L::Array{uEltype, 5} # [variables, i, j, k, elements]
795
antidiffusive_flux3_R::Array{uEltype, 5} # [variables, i, j, k, elements]
796
# internal `resize!`able storage
797
_antidiffusive_flux1_L::Vector{uEltype}
798
_antidiffusive_flux1_R::Vector{uEltype}
799
_antidiffusive_flux2_L::Vector{uEltype}
800
_antidiffusive_flux2_R::Vector{uEltype}
801
_antidiffusive_flux3_L::Vector{uEltype}
802
_antidiffusive_flux3_R::Vector{uEltype}
803
end
804
805
function ContainerAntidiffusiveFlux3D{uEltype}(capacity::Integer, n_variables,
806
n_nodes) where {uEltype <: Real}
807
nan_uEltype = convert(uEltype, NaN)
808
809
# Initialize fields with defaults
810
_antidiffusive_flux1_L = fill(nan_uEltype,
811
n_variables * (n_nodes + 1) * n_nodes * n_nodes *
812
capacity)
813
antidiffusive_flux1_L = unsafe_wrap(Array, pointer(_antidiffusive_flux1_L),
814
(n_variables, n_nodes + 1, n_nodes, n_nodes,
815
capacity))
816
_antidiffusive_flux1_R = fill(nan_uEltype,
817
n_variables * (n_nodes + 1) * n_nodes * n_nodes *
818
capacity)
819
antidiffusive_flux1_R = unsafe_wrap(Array, pointer(_antidiffusive_flux1_R),
820
(n_variables, n_nodes + 1, n_nodes, n_nodes,
821
capacity))
822
823
_antidiffusive_flux2_L = fill(nan_uEltype,
824
n_variables * n_nodes * (n_nodes + 1) * n_nodes *
825
capacity)
826
antidiffusive_flux2_L = unsafe_wrap(Array, pointer(_antidiffusive_flux2_L),
827
(n_variables, n_nodes, n_nodes + 1, n_nodes,
828
capacity))
829
_antidiffusive_flux2_R = fill(nan_uEltype,
830
n_variables * n_nodes * (n_nodes + 1) * n_nodes *
831
capacity)
832
antidiffusive_flux2_R = unsafe_wrap(Array, pointer(_antidiffusive_flux2_R),
833
(n_variables, n_nodes, n_nodes + 1, n_nodes,
834
capacity))
835
836
_antidiffusive_flux3_L = fill(nan_uEltype,
837
n_variables * n_nodes * n_nodes * (n_nodes + 1) *
838
capacity)
839
antidiffusive_flux3_L = unsafe_wrap(Array, pointer(_antidiffusive_flux3_L),
840
(n_variables, n_nodes, n_nodes, n_nodes + 1,
841
capacity))
842
_antidiffusive_flux3_R = fill(nan_uEltype,
843
n_variables * n_nodes * n_nodes * (n_nodes + 1) *
844
capacity)
845
antidiffusive_flux3_R = unsafe_wrap(Array, pointer(_antidiffusive_flux3_R),
846
(n_variables, n_nodes, n_nodes, n_nodes + 1,
847
capacity))
848
849
reset_antidiffusive_fluxes!(antidiffusive_flux1_L, antidiffusive_flux1_R,
850
antidiffusive_flux2_L, antidiffusive_flux2_R,
851
antidiffusive_flux3_L, antidiffusive_flux3_R)
852
853
return ContainerAntidiffusiveFlux3D{uEltype}(antidiffusive_flux1_L,
854
antidiffusive_flux1_R,
855
antidiffusive_flux2_L,
856
antidiffusive_flux2_R,
857
antidiffusive_flux3_L,
858
antidiffusive_flux3_R,
859
_antidiffusive_flux1_L,
860
_antidiffusive_flux1_R,
861
_antidiffusive_flux2_L,
862
_antidiffusive_flux2_R,
863
_antidiffusive_flux3_L,
864
_antidiffusive_flux3_R)
865
end
866
867
# Only one-dimensional `Array`s are `resize!`able in Julia.
868
# Hence, we use `Vector`s as internal storage and `resize!`
869
# them whenever needed. Then, we reuse the same memory by
870
# `unsafe_wrap`ping multi-dimensional `Array`s around the
871
# internal storage.
872
function Base.resize!(fluxes::ContainerAntidiffusiveFlux3D, capacity)
873
n_nodes = nnodes(fluxes)
874
n_variables = nvariables(fluxes)
875
876
@unpack _antidiffusive_flux1_L, _antidiffusive_flux1_R, _antidiffusive_flux2_L, _antidiffusive_flux2_R, _antidiffusive_flux3_L, _antidiffusive_flux3_R = fluxes
877
878
resize!(_antidiffusive_flux1_L,
879
n_variables * (n_nodes + 1) * n_nodes * n_nodes * capacity)
880
fluxes.antidiffusive_flux1_L = unsafe_wrap(Array, pointer(_antidiffusive_flux1_L),
881
(n_variables,
882
n_nodes + 1, n_nodes, n_nodes,
883
capacity))
884
resize!(_antidiffusive_flux1_R,
885
n_variables * (n_nodes + 1) * n_nodes * n_nodes * capacity)
886
fluxes.antidiffusive_flux1_R = unsafe_wrap(Array, pointer(_antidiffusive_flux1_R),
887
(n_variables,
888
n_nodes + 1, n_nodes, n_nodes,
889
capacity))
890
resize!(_antidiffusive_flux2_L,
891
n_variables * n_nodes * (n_nodes + 1) * n_nodes * capacity)
892
fluxes.antidiffusive_flux2_L = unsafe_wrap(Array, pointer(_antidiffusive_flux2_L),
893
(n_variables,
894
n_nodes, n_nodes + 1, n_nodes,
895
capacity))
896
resize!(_antidiffusive_flux2_R,
897
n_variables * n_nodes * (n_nodes + 1) * n_nodes * capacity)
898
fluxes.antidiffusive_flux2_R = unsafe_wrap(Array, pointer(_antidiffusive_flux2_R),
899
(n_variables,
900
n_nodes, n_nodes + 1, n_nodes,
901
capacity))
902
903
resize!(_antidiffusive_flux3_L,
904
n_variables * n_nodes * n_nodes * (n_nodes + 1) * capacity)
905
fluxes.antidiffusive_flux3_L = unsafe_wrap(Array, pointer(_antidiffusive_flux3_L),
906
(n_variables,
907
n_nodes, n_nodes, n_nodes + 1,
908
capacity))
909
resize!(_antidiffusive_flux3_R,
910
n_variables * n_nodes * n_nodes * (n_nodes + 1) * capacity)
911
fluxes.antidiffusive_flux3_R = unsafe_wrap(Array, pointer(_antidiffusive_flux3_R),
912
(n_variables,
913
n_nodes, n_nodes, n_nodes + 1,
914
capacity))
915
916
return nothing
917
end
918
919
function reset_antidiffusive_fluxes!(antidiffusive_flux1_L, antidiffusive_flux1_R,
920
antidiffusive_flux2_L, antidiffusive_flux2_R,
921
antidiffusive_flux3_L, antidiffusive_flux3_R)
922
uEltype = eltype(antidiffusive_flux1_L)
923
@threaded for element in axes(antidiffusive_flux1_L, 5)
924
antidiffusive_flux1_L[:, 1, :, :, element] .= zero(uEltype)
925
antidiffusive_flux1_L[:, end, :, :, element] .= zero(uEltype)
926
antidiffusive_flux1_R[:, 1, :, :, element] .= zero(uEltype)
927
antidiffusive_flux1_R[:, end, :, :, element] .= zero(uEltype)
928
929
antidiffusive_flux2_L[:, :, 1, :, element] .= zero(uEltype)
930
antidiffusive_flux2_L[:, :, end, :, element] .= zero(uEltype)
931
antidiffusive_flux2_R[:, :, 1, :, element] .= zero(uEltype)
932
antidiffusive_flux2_R[:, :, end, :, element] .= zero(uEltype)
933
934
antidiffusive_flux3_L[:, :, :, 1, element] .= zero(uEltype)
935
antidiffusive_flux3_L[:, :, :, end, element] .= zero(uEltype)
936
antidiffusive_flux3_R[:, :, :, 1, element] .= zero(uEltype)
937
antidiffusive_flux3_R[:, :, :, end, element] .= zero(uEltype)
938
end
939
940
return nothing
941
end
942
end # @muladd
943
944