Path: blob/main/src/solvers/dgsem_tree/containers_3d.jl
5591 views
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).1# Since these FMAs can increase the performance of many numerical algorithms,2# we need to opt-in explicitly.3# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.4@muladd begin5#! format: noindent67# Container data structure (structure-of-arrays style) for DG elements8mutable struct TreeElementContainer3D{RealT <: Real, uEltype <: Real} <:9AbstractTreeElementContainer10inverse_jacobian::Vector{RealT} # [elements]11node_coordinates::Array{RealT, 5} # [orientation, i, j, k, elements]12surface_flux_values::Array{uEltype, 5} # [variables, i, j, direction, elements]13cell_ids::Vector{Int} # [elements]14# internal `resize!`able storage15_node_coordinates::Vector{RealT}16_surface_flux_values::Vector{uEltype}17end1819# Only one-dimensional `Array`s are `resize!`able in Julia.20# Hence, we use `Vector`s as internal storage and `resize!`21# them whenever needed. Then, we reuse the same memory by22# `unsafe_wrap`ping multi-dimensional `Array`s around the23# internal storage.24function Base.resize!(elements::TreeElementContainer3D, capacity)25n_nodes = nnodes(elements)26n_variables = nvariables(elements)27@unpack _node_coordinates, _surface_flux_values,28inverse_jacobian, cell_ids = elements2930resize!(inverse_jacobian, capacity)3132resize!(_node_coordinates, 3 * n_nodes * n_nodes * n_nodes * capacity)33elements.node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates),34(3, n_nodes, n_nodes, n_nodes, capacity))3536resize!(_surface_flux_values, n_variables * n_nodes * n_nodes * 2 * 3 * capacity)37elements.surface_flux_values = unsafe_wrap(Array, pointer(_surface_flux_values),38(n_variables, n_nodes, n_nodes, 2 * 3,39capacity))4041resize!(cell_ids, capacity)4243return nothing44end4546function TreeElementContainer3D{RealT, uEltype}(capacity::Integer, n_variables,47n_nodes) where {RealT <: Real,48uEltype <: Real}49nan_RealT = convert(RealT, NaN)50nan_uEltype = convert(uEltype, NaN)5152# Initialize fields with defaults53inverse_jacobian = fill(nan_RealT, capacity)5455_node_coordinates = fill(nan_RealT, 3 * n_nodes * n_nodes * n_nodes * capacity)56node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates),57(3, n_nodes, n_nodes, n_nodes, capacity))5859_surface_flux_values = fill(nan_uEltype,60n_variables * n_nodes * n_nodes * 2 * 3 * capacity)61surface_flux_values = unsafe_wrap(Array, pointer(_surface_flux_values),62(n_variables, n_nodes, n_nodes, 2 * 3, capacity))6364cell_ids = fill(typemin(Int), capacity)6566return TreeElementContainer3D{RealT, uEltype}(inverse_jacobian, node_coordinates,67surface_flux_values, cell_ids,68_node_coordinates,69_surface_flux_values)70end7172# Create element container and initialize element data73function init_elements(cell_ids, mesh::TreeMesh3D,74equations::AbstractEquations{3},75basis, ::Type{RealT},76::Type{uEltype}) where {RealT <: Real, uEltype <: Real}77# Initialize container78n_elements = length(cell_ids)79elements = TreeElementContainer3D{RealT, uEltype}(n_elements, nvariables(equations),80nnodes(basis))8182init_elements!(elements, cell_ids, mesh, basis)83return elements84end8586function init_elements!(elements, cell_ids, mesh::TreeMesh3D, basis)87nodes = get_nodes(basis)88# Compute the length of the 1D reference interval by integrating89# the function with constant value unity on the corresponding90# element data type (using \circ)91reference_length = integrate(one ∘ eltype, nodes, basis)92# Compute the offset of the midpoint of the 1D reference interval93# (its difference from zero)94reference_offset = (first(nodes) + last(nodes)) / 29596# Store cell ids97elements.cell_ids .= cell_ids9899# Calculate inverse Jacobian and node coordinates100for element in eachelement(elements)101# Get cell id102cell_id = cell_ids[element]103104# Get cell length105dx = length_at_cell(mesh.tree, cell_id)106107# Calculate inverse Jacobian108jacobian = dx / reference_length109elements.inverse_jacobian[element] = inv(jacobian)110111# Calculate node coordinates112# Note that the `tree_coordinates` are the midpoints of the cells.113# Hence, we need to add an offset for `nodes` with a midpoint114# different from zero.115for k in eachnode(basis), j in eachnode(basis), i in eachnode(basis)116elements.node_coordinates[1, i, j, k, element] = (mesh.tree.coordinates[1,117cell_id] +118jacobian * (nodes[i] -119reference_offset))120elements.node_coordinates[2, i, j, k, element] = (mesh.tree.coordinates[2,121cell_id] +122jacobian * (nodes[j] -123reference_offset))124elements.node_coordinates[3, i, j, k, element] = (mesh.tree.coordinates[3,125cell_id] +126jacobian * (nodes[k] -127reference_offset))128end129end130131return elements132end133134# Container data structure (structure-of-arrays style) for DG interfaces135mutable struct TreeInterfaceContainer3D{uEltype <: Real} <:136AbstractTreeInterfaceContainer137u::Array{uEltype, 5} # [leftright, variables, i, j, interfaces]138neighbor_ids::Matrix{Int} # [leftright, interfaces]139orientations::Vector{Int} # [interfaces]140# internal `resize!`able storage141_u::Vector{uEltype}142_neighbor_ids::Vector{Int}143end144145# See explanation of Base.resize! for the element container146function Base.resize!(interfaces::TreeInterfaceContainer3D, capacity)147n_nodes = nnodes(interfaces)148n_variables = nvariables(interfaces)149@unpack _u, _neighbor_ids, orientations = interfaces150151resize!(_u, 2 * n_variables * n_nodes * n_nodes * capacity)152interfaces.u = unsafe_wrap(Array, pointer(_u),153(2, n_variables, n_nodes, n_nodes, capacity))154155resize!(_neighbor_ids, 2 * capacity)156interfaces.neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids),157(2, capacity))158159resize!(orientations, capacity)160161return nothing162end163164function TreeInterfaceContainer3D{uEltype}(capacity::Integer, n_variables,165n_nodes) where {uEltype <: Real}166nan = convert(uEltype, NaN)167168# Initialize fields with defaults169_u = fill(nan, 2 * n_variables * n_nodes * n_nodes * capacity)170u = unsafe_wrap(Array, pointer(_u),171(2, n_variables, n_nodes, n_nodes, capacity))172173_neighbor_ids = fill(typemin(Int), 2 * capacity)174neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids),175(2, capacity))176177orientations = fill(typemin(Int), capacity)178179return TreeInterfaceContainer3D{uEltype}(u, neighbor_ids, orientations,180_u, _neighbor_ids)181end182183# Create interface container and initialize interface data in `elements`.184function init_interfaces(cell_ids, mesh::TreeMesh3D,185elements::TreeElementContainer3D)186# Initialize container187n_interfaces = count_required_interfaces(mesh, cell_ids)188interfaces = TreeInterfaceContainer3D{eltype(elements)}(n_interfaces,189nvariables(elements),190nnodes(elements))191192# Connect elements with interfaces193init_interfaces!(interfaces, elements, mesh)194return interfaces195end196197# Count the number of interfaces that need to be created198function count_required_interfaces(mesh::TreeMesh3D, cell_ids)199count = 0200201# Iterate over all cells202for cell_id in cell_ids203for direction in eachdirection(mesh.tree)204# Only count interfaces in positive direction to avoid double counting205if direction % 2 == 1206continue207end208209# If no neighbor exists, current cell is small or at boundary and thus we need a mortar210if !has_neighbor(mesh.tree, cell_id, direction)211continue212end213214# Skip if neighbor has children215neighbor_id = mesh.tree.neighbor_ids[direction, cell_id]216if has_children(mesh.tree, neighbor_id)217continue218end219220count += 1221end222end223224return count225end226227# Initialize connectivity between elements and interfaces228function init_interfaces!(interfaces, elements, mesh::TreeMesh3D)229# Construct cell -> element mapping for easier algorithm implementation230tree = mesh.tree231c2e = zeros(Int, length(tree))232for element in eachelement(elements)233c2e[elements.cell_ids[element]] = element234end235236# Reset interface count237count = 0238239# Iterate over all elements to find neighbors and to connect via interfaces240for element in eachelement(elements)241# Get cell id242cell_id = elements.cell_ids[element]243244# Loop over directions245for direction in eachdirection(mesh.tree)246# Only create interfaces in positive direction247if direction % 2 == 1248continue249end250251# If no neighbor exists, current cell is small and thus we need a mortar252if !has_neighbor(mesh.tree, cell_id, direction)253continue254end255256# Skip if neighbor has children257neighbor_cell_id = mesh.tree.neighbor_ids[direction, cell_id]258if has_children(mesh.tree, neighbor_cell_id)259continue260end261262# Create interface between elements (1 -> "left" of interface, 2 -> "right" of interface)263count += 1264interfaces.neighbor_ids[2, count] = c2e[neighbor_cell_id]265interfaces.neighbor_ids[1, count] = element266267# Set orientation (x -> 1, y -> 2, z -> 3)268if direction in (1, 2)269interfaces.orientations[count] = 1270elseif direction in (3, 4)271interfaces.orientations[count] = 2272else273interfaces.orientations[count] = 3274end275end276end277278@assert count==ninterfaces(interfaces) ("Actual interface count ($count) does not match "*279"expectations $(ninterfaces(interfaces))")280end281282# Container data structure (structure-of-arrays style) for DG boundaries283mutable struct TreeBoundaryContainer3D{RealT <: Real, uEltype <: Real} <:284AbstractTreeBoundaryContainer285u::Array{uEltype, 5} # [leftright, variables, i, j, boundaries]286neighbor_ids::Vector{Int} # [boundaries]287orientations::Vector{Int} # [boundaries]288neighbor_sides::Vector{Int} # [boundaries]289node_coordinates::Array{RealT, 4} # [orientation, i, j, elements]290n_boundaries_per_direction::SVector{6, Int} # [direction]291# internal `resize!`able storage292_u::Vector{uEltype}293_node_coordinates::Vector{RealT}294end295296# See explanation of Base.resize! for the element container297function Base.resize!(boundaries::TreeBoundaryContainer3D, capacity)298n_nodes = nnodes(boundaries)299n_variables = nvariables(boundaries)300@unpack _u, _node_coordinates,301neighbor_ids, orientations, neighbor_sides = boundaries302303resize!(_u, 2 * n_variables * n_nodes * n_nodes * capacity)304boundaries.u = unsafe_wrap(Array, pointer(_u),305(2, n_variables, n_nodes, n_nodes, capacity))306307resize!(_node_coordinates, 3 * n_nodes * n_nodes * capacity)308boundaries.node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates),309(3, n_nodes, n_nodes, capacity))310311resize!(neighbor_ids, capacity)312313resize!(orientations, capacity)314315resize!(neighbor_sides, capacity)316317return nothing318end319320function TreeBoundaryContainer3D{RealT, uEltype}(capacity::Integer, n_variables,321n_nodes) where {RealT <: Real,322uEltype <: Real}323nan_RealT = convert(RealT, NaN)324nan_uEltype = convert(uEltype, NaN)325326# Initialize fields with defaults327_u = fill(nan_uEltype, 2 * n_variables * n_nodes * n_nodes * capacity)328u = unsafe_wrap(Array, pointer(_u),329(2, n_variables, n_nodes, n_nodes, capacity))330331neighbor_ids = fill(typemin(Int), capacity)332333orientations = fill(typemin(Int), capacity)334335neighbor_sides = fill(typemin(Int), capacity)336337_node_coordinates = fill(nan_RealT, 3 * n_nodes * n_nodes * capacity)338node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates),339(3, n_nodes, n_nodes, capacity))340341n_boundaries_per_direction = SVector(0, 0, 0, 0, 0, 0)342343return TreeBoundaryContainer3D{RealT, uEltype}(u, neighbor_ids, orientations,344neighbor_sides,345node_coordinates,346n_boundaries_per_direction,347_u, _node_coordinates)348end349350# Create boundaries container and initialize boundary data in `elements`.351function init_boundaries(cell_ids, mesh::TreeMesh3D,352elements::TreeElementContainer3D, basis)353# Initialize container354n_boundaries = count_required_boundaries(mesh, cell_ids)355boundaries = TreeBoundaryContainer3D{real(elements), eltype(elements)}(n_boundaries,356nvariables(elements),357nnodes(elements))358359# Connect elements with boundaries360init_boundaries!(boundaries, elements, mesh, basis)361return boundaries362end363364# Count the number of boundaries that need to be created365function count_required_boundaries(mesh::TreeMesh3D, cell_ids)366count = 0367368# Iterate over all cells369for cell_id in cell_ids370for direction in eachdirection(mesh.tree)371# If neighbor exists, current cell is not at a boundary372if has_neighbor(mesh.tree, cell_id, direction)373continue374end375376# If coarse neighbor exists, current cell is not at a boundary377if has_coarse_neighbor(mesh.tree, cell_id, direction)378continue379end380381# No neighbor exists in this direction -> must be a boundary382count += 1383end384end385386return count387end388389# Initialize connectivity between elements and boundaries390function init_boundaries!(boundaries, elements, mesh::TreeMesh3D, basis)391# Reset boundaries count392count = 0393394# Initialize boundary counts395counts_per_direction = MVector(0, 0, 0, 0, 0, 0)396397# OBS! Iterate over directions first, then over elements, and count boundaries in each direction398# Rationale: This way the boundaries are internally sorted by the directions -x, +x, -y etc.,399# obviating the need to store the boundary condition to be applied explicitly.400# Loop over directions401for direction in eachdirection(mesh.tree)402# Iterate over all elements to find missing neighbors and to connect to boundaries403for element in eachelement(elements)404# Get cell id405cell_id = elements.cell_ids[element]406407# If neighbor exists, current cell is not at a boundary408if has_neighbor(mesh.tree, cell_id, direction)409continue410end411412# If coarse neighbor exists, current cell is not at a boundary413if has_coarse_neighbor(mesh.tree, cell_id, direction)414continue415end416417# Create boundary418count += 1419counts_per_direction[direction] += 1420421# Set neighbor element id422boundaries.neighbor_ids[count] = element423424# Set neighbor side, which denotes the direction (1 -> negative, 2 -> positive) of the element425if iseven(direction)426boundaries.neighbor_sides[count] = 1427else428boundaries.neighbor_sides[count] = 2429end430431# Set orientation (x -> 1, y -> 2)432if direction in (1, 2)433boundaries.orientations[count] = 1434elseif direction in (3, 4)435boundaries.orientations[count] = 2436else437boundaries.orientations[count] = 3438end439440# Store node coordinates441enc = elements.node_coordinates442if direction == 1 # -x direction443boundaries.node_coordinates[:, :, :, count] .= enc[:, 1, :, :, element]444elseif direction == 2 # +x direction445boundaries.node_coordinates[:, :, :, count] .= enc[:, end, :, :,446element]447elseif direction == 3 # -y direction448boundaries.node_coordinates[:, :, :, count] .= enc[:, :, 1, :, element]449elseif direction == 4 # +y direction450boundaries.node_coordinates[:, :, :, count] .= enc[:, :, end, :,451element]452elseif direction == 5 # -z direction453boundaries.node_coordinates[:, :, :, count] .= enc[:, :, :, 1, element]454elseif direction == 6 # +z direction455boundaries.node_coordinates[:, :, :, count] .= enc[:, :, :, end,456element]457else458error("should not happen")459end460end461end462463@assert count==nboundaries(boundaries) ("Actual boundaries count ($count) does not match "*464"expectations $(nboundaries(boundaries))")465@assert sum(counts_per_direction) == count466467boundaries.n_boundaries_per_direction = SVector(counts_per_direction)468469return SVector(counts_per_direction)470end471472# Container data structure (structure-of-arrays style) for DG L2 mortars473# Positions/directions for orientations = 1, large_sides = 2:474# mortar is orthogonal to x-axis, large side is in positive coordinate direction wrt mortar475# /----------------------------\ /----------------------------\476# | | | | |477# | upper, left | upper, right | | |478# | 3 | 4 | | |479# | | | | large |480# |-------------|--------------| | 5 |481# z | | | | |482# | lower, left | lower, right | | |483# ^ | 1 | 2 | | |484# | | | | | |485# | \----------------------------/ \----------------------------/486# |487# ⋅----> y488# Left and right are always wrt to a coordinate direction:489# * left is always the negative direction490# * right is always the positive direction491#492# Left and right are used *both* for the numbering of the mortar faces *and* for the position of the493# elements with respect to the axis orthogonal to the mortar.494mutable struct TreeL2MortarContainer3D{uEltype <: Real} <: AbstractTreeL2MortarContainer495u_upper_left::Array{uEltype, 5} # [leftright, variables, i, j, mortars]496u_upper_right::Array{uEltype, 5} # [leftright, variables, i, j, mortars]497u_lower_left::Array{uEltype, 5} # [leftright, variables, i, j, mortars]498u_lower_right::Array{uEltype, 5} # [leftright, variables, i, j, mortars]499neighbor_ids::Array{Int, 2} # [position, mortars]500# Large sides: left -> 1, right -> 2501large_sides::Vector{Int} # [mortars]502orientations::Vector{Int} # [mortars]503# internal `resize!`able storage504_u_upper_left::Vector{uEltype}505_u_upper_right::Vector{uEltype}506_u_lower_left::Vector{uEltype}507_u_lower_right::Vector{uEltype}508_neighbor_ids::Vector{Int}509end510511# Return number of equation variables512nvariables(mortars::TreeL2MortarContainer3D) = size(mortars.u_upper_left, 2)513# Return number of mortar nodes (L2 mortars are only h-adaptive, not p-adaptive)514nnodes(mortars::TreeL2MortarContainer3D) = size(mortars.u_upper_left, 3)515516# See explanation of Base.resize! for the element container517function Base.resize!(mortars::TreeL2MortarContainer3D, capacity)518n_nodes = nnodes(mortars)519n_variables = nvariables(mortars)520@unpack _u_upper_left, _u_upper_right, _u_lower_left, _u_lower_right,521_neighbor_ids, large_sides, orientations = mortars522523resize!(_u_upper_left, 2 * n_variables * n_nodes * n_nodes * capacity)524mortars.u_upper_left = unsafe_wrap(Array, pointer(_u_upper_left),525(2, n_variables, n_nodes, n_nodes, capacity))526527resize!(_u_upper_right, 2 * n_variables * n_nodes * n_nodes * capacity)528mortars.u_upper_right = unsafe_wrap(Array, pointer(_u_upper_right),529(2, n_variables, n_nodes, n_nodes, capacity))530531resize!(_u_lower_left, 2 * n_variables * n_nodes * n_nodes * capacity)532mortars.u_lower_left = unsafe_wrap(Array, pointer(_u_lower_left),533(2, n_variables, n_nodes, n_nodes, capacity))534535resize!(_u_lower_right, 2 * n_variables * n_nodes * n_nodes * capacity)536mortars.u_lower_right = unsafe_wrap(Array, pointer(_u_lower_right),537(2, n_variables, n_nodes, n_nodes, capacity))538539resize!(_neighbor_ids, 5 * capacity)540mortars.neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids),541(5, capacity))542543resize!(large_sides, capacity)544545resize!(orientations, capacity)546547return nothing548end549550function TreeL2MortarContainer3D{uEltype}(capacity::Integer, n_variables,551n_nodes) where {uEltype <: Real}552nan = convert(uEltype, NaN)553554# Initialize fields with defaults555_u_upper_left = fill(nan, 2 * n_variables * n_nodes * n_nodes * capacity)556u_upper_left = unsafe_wrap(Array, pointer(_u_upper_left),557(2, n_variables, n_nodes, n_nodes, capacity))558559_u_upper_right = fill(nan, 2 * n_variables * n_nodes * n_nodes * capacity)560u_upper_right = unsafe_wrap(Array, pointer(_u_upper_right),561(2, n_variables, n_nodes, n_nodes, capacity))562563_u_lower_left = fill(nan, 2 * n_variables * n_nodes * n_nodes * capacity)564u_lower_left = unsafe_wrap(Array, pointer(_u_lower_left),565(2, n_variables, n_nodes, n_nodes, capacity))566567_u_lower_right = fill(nan, 2 * n_variables * n_nodes * n_nodes * capacity)568u_lower_right = unsafe_wrap(Array, pointer(_u_lower_right),569(2, n_variables, n_nodes, n_nodes, capacity))570571_neighbor_ids = fill(typemin(Int), 5 * capacity)572neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids),573(5, capacity))574575large_sides = fill(typemin(Int), capacity)576577orientations = fill(typemin(Int), capacity)578579return TreeL2MortarContainer3D{uEltype}(u_upper_left, u_upper_right,580u_lower_left, u_lower_right,581neighbor_ids, large_sides, orientations,582_u_upper_left, _u_upper_right,583_u_lower_left, _u_lower_right,584_neighbor_ids)585end586587# Allow printing container contents588function Base.show(io::IO, ::MIME"text/plain", c::TreeL2MortarContainer3D)589@nospecialize c # reduce precompilation time590591println(io, '*'^20)592for idx in CartesianIndices(c.u_upper_left)593println(io, "c.u_upper_left[$idx] = $(c.u_upper_left[idx])")594end595for idx in CartesianIndices(c.u_upper_right)596println(io, "c.u_upper_right[$idx] = $(c.u_upper_right[idx])")597end598for idx in CartesianIndices(c.u_lower_left)599println(io, "c.u_lower_left[$idx] = $(c.u_lower_left[idx])")600end601for idx in CartesianIndices(c.u_lower_right)602println(io, "c.u_lower_right[$idx] = $(c.u_lower_right[idx])")603end604println(io, "transpose(c.neighbor_ids) = $(transpose(c.neighbor_ids))")605println(io, "c.large_sides = $(c.large_sides)")606println(io, "c.orientations = $(c.orientations)")607print(io, '*'^20)608return nothing609end610611# Create mortar container and initialize mortar data in `elements`.612function init_mortars(cell_ids, mesh::TreeMesh3D,613elements::TreeElementContainer3D,614mortar::LobattoLegendreMortarL2)615# Initialize containers616n_mortars = count_required_mortars(mesh, cell_ids)617mortars = TreeL2MortarContainer3D{eltype(elements)}(n_mortars, nvariables(elements),618nnodes(elements))619620# Connect elements with mortars621init_mortars!(mortars, elements, mesh)622return mortars623end624625# Count the number of mortars that need to be created626function count_required_mortars(mesh::TreeMesh3D, cell_ids)627count = 0628629# Iterate over all cells and count mortars from perspective of coarse cells630for cell_id in cell_ids631for direction in eachdirection(mesh.tree)632# If no neighbor exists, cell is small with large neighbor or at boundary -> do nothing633if !has_neighbor(mesh.tree, cell_id, direction)634continue635end636637# If neighbor has no children, this is a conforming interface -> do nothing638neighbor_id = mesh.tree.neighbor_ids[direction, cell_id]639if !has_children(mesh.tree, neighbor_id)640continue641end642643count += 1644end645end646647return count648end649650# Initialize connectivity between elements and mortars651function init_mortars!(mortars, elements, mesh::TreeMesh3D)652# Construct cell -> element mapping for easier algorithm implementation653tree = mesh.tree654c2e = zeros(Int, length(tree))655for element in eachelement(elements)656c2e[elements.cell_ids[element]] = element657end658659# Reset interface count660count = 0661662# Iterate over all elements to find neighbors and to connect via interfaces663for element in eachelement(elements)664# Get cell id665cell_id = elements.cell_ids[element]666667for direction in eachdirection(mesh.tree)668# If no neighbor exists, cell is small with large neighbor -> do nothing669if !has_neighbor(mesh.tree, cell_id, direction)670continue671end672673# If neighbor has no children, this is a conforming interface -> do nothing674neighbor_cell_id = mesh.tree.neighbor_ids[direction, cell_id]675if !has_children(mesh.tree, neighbor_cell_id)676continue677end678679# Create mortar between elements (3 possible orientations):680#681# mortar in x-direction:682# 1 -> small element in lower, left position (-y, -z)683# 2 -> small element in lower, right position (+y, -z)684# 3 -> small element in upper, left position (-y, +z)685# 4 -> small element in upper, right position (+y, +z)686#687# mortar in y-direction:688# 1 -> small element in lower, left position (-x, -z)689# 2 -> small element in lower, right position (+x, -z)690# 3 -> small element in upper, left position (-x, +z)691# 4 -> small element in upper, right position (+x, +z)692#693# mortar in z-direction:694# 1 -> small element in lower, left position (-x, -y)695# 2 -> small element in lower, right position (+x, -y)696# 3 -> small element in upper, left position (-x, +y)697# 4 -> small element in upper, right position (+x, +y)698#699# Always the case:700# 5 -> large element701#702count += 1703mortars.neighbor_ids[5, count] = element704705# Directions are from the perspective of the large element706# ("Where are the small elements? Ah, in the ... direction!")707if direction == 1 # -x708mortars.neighbor_ids[1, count] = c2e[mesh.tree.child_ids[2,709neighbor_cell_id]]710mortars.neighbor_ids[2, count] = c2e[mesh.tree.child_ids[4,711neighbor_cell_id]]712mortars.neighbor_ids[3, count] = c2e[mesh.tree.child_ids[6,713neighbor_cell_id]]714mortars.neighbor_ids[4, count] = c2e[mesh.tree.child_ids[8,715neighbor_cell_id]]716elseif direction == 2 # +x717mortars.neighbor_ids[1, count] = c2e[mesh.tree.child_ids[1,718neighbor_cell_id]]719mortars.neighbor_ids[2, count] = c2e[mesh.tree.child_ids[3,720neighbor_cell_id]]721mortars.neighbor_ids[3, count] = c2e[mesh.tree.child_ids[5,722neighbor_cell_id]]723mortars.neighbor_ids[4, count] = c2e[mesh.tree.child_ids[7,724neighbor_cell_id]]725elseif direction == 3 # -y726mortars.neighbor_ids[1, count] = c2e[mesh.tree.child_ids[3,727neighbor_cell_id]]728mortars.neighbor_ids[2, count] = c2e[mesh.tree.child_ids[4,729neighbor_cell_id]]730mortars.neighbor_ids[3, count] = c2e[mesh.tree.child_ids[7,731neighbor_cell_id]]732mortars.neighbor_ids[4, count] = c2e[mesh.tree.child_ids[8,733neighbor_cell_id]]734elseif direction == 4 # +y735mortars.neighbor_ids[1, count] = c2e[mesh.tree.child_ids[1,736neighbor_cell_id]]737mortars.neighbor_ids[2, count] = c2e[mesh.tree.child_ids[2,738neighbor_cell_id]]739mortars.neighbor_ids[3, count] = c2e[mesh.tree.child_ids[5,740neighbor_cell_id]]741mortars.neighbor_ids[4, count] = c2e[mesh.tree.child_ids[6,742neighbor_cell_id]]743elseif direction == 5 # -z744mortars.neighbor_ids[1, count] = c2e[mesh.tree.child_ids[5,745neighbor_cell_id]]746mortars.neighbor_ids[2, count] = c2e[mesh.tree.child_ids[6,747neighbor_cell_id]]748mortars.neighbor_ids[3, count] = c2e[mesh.tree.child_ids[7,749neighbor_cell_id]]750mortars.neighbor_ids[4, count] = c2e[mesh.tree.child_ids[8,751neighbor_cell_id]]752elseif direction == 6 # +z753mortars.neighbor_ids[1, count] = c2e[mesh.tree.child_ids[1,754neighbor_cell_id]]755mortars.neighbor_ids[2, count] = c2e[mesh.tree.child_ids[2,756neighbor_cell_id]]757mortars.neighbor_ids[3, count] = c2e[mesh.tree.child_ids[3,758neighbor_cell_id]]759mortars.neighbor_ids[4, count] = c2e[mesh.tree.child_ids[4,760neighbor_cell_id]]761else762error("should not happen")763end764765# Set large side, which denotes the direction (1 -> negative, 2 -> positive) of the large side766if iseven(direction)767mortars.large_sides[count] = 1768else769mortars.large_sides[count] = 2770end771772# Set orientation (x -> 1, y -> 2, z -> 3)773if direction in (1, 2)774mortars.orientations[count] = 1775elseif direction in (3, 4)776mortars.orientations[count] = 2777else778mortars.orientations[count] = 3779end780end781end782783@assert count==nmortars(mortars) ("Actual mortar count ($count) does not match "*784"expectations $(nmortars(mortars))")785end786787mutable struct ContainerAntidiffusiveFlux3D{uEltype <: Real} <:788AbstractContainerAntidiffusiveFlux789antidiffusive_flux1_L::Array{uEltype, 5} # [variables, i, j, k, elements]790antidiffusive_flux1_R::Array{uEltype, 5} # [variables, i, j, k, elements]791antidiffusive_flux2_L::Array{uEltype, 5} # [variables, i, j, k, elements]792antidiffusive_flux2_R::Array{uEltype, 5} # [variables, i, j, k, elements]793antidiffusive_flux3_L::Array{uEltype, 5} # [variables, i, j, k, elements]794antidiffusive_flux3_R::Array{uEltype, 5} # [variables, i, j, k, elements]795# internal `resize!`able storage796_antidiffusive_flux1_L::Vector{uEltype}797_antidiffusive_flux1_R::Vector{uEltype}798_antidiffusive_flux2_L::Vector{uEltype}799_antidiffusive_flux2_R::Vector{uEltype}800_antidiffusive_flux3_L::Vector{uEltype}801_antidiffusive_flux3_R::Vector{uEltype}802end803804function ContainerAntidiffusiveFlux3D{uEltype}(capacity::Integer, n_variables,805n_nodes) where {uEltype <: Real}806nan_uEltype = convert(uEltype, NaN)807808# Initialize fields with defaults809_antidiffusive_flux1_L = fill(nan_uEltype,810n_variables * (n_nodes + 1) * n_nodes * n_nodes *811capacity)812antidiffusive_flux1_L = unsafe_wrap(Array, pointer(_antidiffusive_flux1_L),813(n_variables, n_nodes + 1, n_nodes, n_nodes,814capacity))815_antidiffusive_flux1_R = fill(nan_uEltype,816n_variables * (n_nodes + 1) * n_nodes * n_nodes *817capacity)818antidiffusive_flux1_R = unsafe_wrap(Array, pointer(_antidiffusive_flux1_R),819(n_variables, n_nodes + 1, n_nodes, n_nodes,820capacity))821822_antidiffusive_flux2_L = fill(nan_uEltype,823n_variables * n_nodes * (n_nodes + 1) * n_nodes *824capacity)825antidiffusive_flux2_L = unsafe_wrap(Array, pointer(_antidiffusive_flux2_L),826(n_variables, n_nodes, n_nodes + 1, n_nodes,827capacity))828_antidiffusive_flux2_R = fill(nan_uEltype,829n_variables * n_nodes * (n_nodes + 1) * n_nodes *830capacity)831antidiffusive_flux2_R = unsafe_wrap(Array, pointer(_antidiffusive_flux2_R),832(n_variables, n_nodes, n_nodes + 1, n_nodes,833capacity))834835_antidiffusive_flux3_L = fill(nan_uEltype,836n_variables * n_nodes * n_nodes * (n_nodes + 1) *837capacity)838antidiffusive_flux3_L = unsafe_wrap(Array, pointer(_antidiffusive_flux3_L),839(n_variables, n_nodes, n_nodes, n_nodes + 1,840capacity))841_antidiffusive_flux3_R = fill(nan_uEltype,842n_variables * n_nodes * n_nodes * (n_nodes + 1) *843capacity)844antidiffusive_flux3_R = unsafe_wrap(Array, pointer(_antidiffusive_flux3_R),845(n_variables, n_nodes, n_nodes, n_nodes + 1,846capacity))847848reset_antidiffusive_fluxes!(antidiffusive_flux1_L, antidiffusive_flux1_R,849antidiffusive_flux2_L, antidiffusive_flux2_R,850antidiffusive_flux3_L, antidiffusive_flux3_R)851852return ContainerAntidiffusiveFlux3D{uEltype}(antidiffusive_flux1_L,853antidiffusive_flux1_R,854antidiffusive_flux2_L,855antidiffusive_flux2_R,856antidiffusive_flux3_L,857antidiffusive_flux3_R,858_antidiffusive_flux1_L,859_antidiffusive_flux1_R,860_antidiffusive_flux2_L,861_antidiffusive_flux2_R,862_antidiffusive_flux3_L,863_antidiffusive_flux3_R)864end865866# Only one-dimensional `Array`s are `resize!`able in Julia.867# Hence, we use `Vector`s as internal storage and `resize!`868# them whenever needed. Then, we reuse the same memory by869# `unsafe_wrap`ping multi-dimensional `Array`s around the870# internal storage.871function Base.resize!(fluxes::ContainerAntidiffusiveFlux3D, capacity)872n_nodes = nnodes(fluxes)873n_variables = nvariables(fluxes)874875@unpack _antidiffusive_flux1_L, _antidiffusive_flux1_R, _antidiffusive_flux2_L, _antidiffusive_flux2_R, _antidiffusive_flux3_L, _antidiffusive_flux3_R = fluxes876877resize!(_antidiffusive_flux1_L,878n_variables * (n_nodes + 1) * n_nodes * n_nodes * capacity)879fluxes.antidiffusive_flux1_L = unsafe_wrap(Array, pointer(_antidiffusive_flux1_L),880(n_variables,881n_nodes + 1, n_nodes, n_nodes,882capacity))883resize!(_antidiffusive_flux1_R,884n_variables * (n_nodes + 1) * n_nodes * n_nodes * capacity)885fluxes.antidiffusive_flux1_R = unsafe_wrap(Array, pointer(_antidiffusive_flux1_R),886(n_variables,887n_nodes + 1, n_nodes, n_nodes,888capacity))889resize!(_antidiffusive_flux2_L,890n_variables * n_nodes * (n_nodes + 1) * n_nodes * capacity)891fluxes.antidiffusive_flux2_L = unsafe_wrap(Array, pointer(_antidiffusive_flux2_L),892(n_variables,893n_nodes, n_nodes + 1, n_nodes,894capacity))895resize!(_antidiffusive_flux2_R,896n_variables * n_nodes * (n_nodes + 1) * n_nodes * capacity)897fluxes.antidiffusive_flux2_R = unsafe_wrap(Array, pointer(_antidiffusive_flux2_R),898(n_variables,899n_nodes, n_nodes + 1, n_nodes,900capacity))901902resize!(_antidiffusive_flux3_L,903n_variables * n_nodes * n_nodes * (n_nodes + 1) * capacity)904fluxes.antidiffusive_flux3_L = unsafe_wrap(Array, pointer(_antidiffusive_flux3_L),905(n_variables,906n_nodes, n_nodes, n_nodes + 1,907capacity))908resize!(_antidiffusive_flux3_R,909n_variables * n_nodes * n_nodes * (n_nodes + 1) * capacity)910fluxes.antidiffusive_flux3_R = unsafe_wrap(Array, pointer(_antidiffusive_flux3_R),911(n_variables,912n_nodes, n_nodes, n_nodes + 1,913capacity))914915return nothing916end917918function reset_antidiffusive_fluxes!(antidiffusive_flux1_L, antidiffusive_flux1_R,919antidiffusive_flux2_L, antidiffusive_flux2_R,920antidiffusive_flux3_L, antidiffusive_flux3_R)921uEltype = eltype(antidiffusive_flux1_L)922@threaded for element in axes(antidiffusive_flux1_L, 5)923antidiffusive_flux1_L[:, 1, :, :, element] .= zero(uEltype)924antidiffusive_flux1_L[:, end, :, :, element] .= zero(uEltype)925antidiffusive_flux1_R[:, 1, :, :, element] .= zero(uEltype)926antidiffusive_flux1_R[:, end, :, :, element] .= zero(uEltype)927928antidiffusive_flux2_L[:, :, 1, :, element] .= zero(uEltype)929antidiffusive_flux2_L[:, :, end, :, element] .= zero(uEltype)930antidiffusive_flux2_R[:, :, 1, :, element] .= zero(uEltype)931antidiffusive_flux2_R[:, :, end, :, element] .= zero(uEltype)932933antidiffusive_flux3_L[:, :, :, 1, element] .= zero(uEltype)934antidiffusive_flux3_L[:, :, :, end, element] .= zero(uEltype)935antidiffusive_flux3_R[:, :, :, 1, element] .= zero(uEltype)936antidiffusive_flux3_R[:, :, :, end, element] .= zero(uEltype)937end938939return nothing940end941end # @muladd942943944