Path: blob/main/src/solvers/dgsem_p4est/containers.jl
5616 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: noindent67mutable struct P4estElementContainer{NDIMS, RealT <: Real, uEltype <: Real,8NDIMSP1, NDIMSP2, NDIMSP3,9ArrayRealTNDIMSP1 <: DenseArray{RealT, NDIMSP1},10ArrayRealTNDIMSP2 <: DenseArray{RealT, NDIMSP2},11ArrayRealTNDIMSP3 <: DenseArray{RealT, NDIMSP3},12VectorRealT <: DenseVector{RealT},13ArrayuEltypeNDIMSP2 <:14DenseArray{uEltype, NDIMSP2},15VectoruEltype <: DenseVector{uEltype}} <:16AbstractElementContainer17# Physical coordinates at each node18node_coordinates::ArrayRealTNDIMSP2 # [orientation, node_i, node_j, node_k, element]1920# Jacobian matrix of the transformation21# [jacobian_i, jacobian_j, node_i, node_j, node_k, element] where jacobian_i is the first index of the Jacobian matrix22jacobian_matrix::ArrayRealTNDIMSP32324# Contravariant vectors, scaled by J, in Kopriva's blue book called Ja^i_n (i index, n dimension)25contravariant_vectors::ArrayRealTNDIMSP3 # [dimension, index, node_i, node_j, node_k, element]2627# 1/J where J is the Jacobian determinant (determinant of Jacobian matrix)28inverse_jacobian::ArrayRealTNDIMSP1 # [node_i, node_j, node_k, element]2930# Buffer for calculated surface flux31surface_flux_values::ArrayuEltypeNDIMSP2 # [variable, i, j, direction, element]3233# internal `resize!`able storage34_node_coordinates::VectorRealT35_jacobian_matrix::VectorRealT36_contravariant_vectors::VectorRealT37_inverse_jacobian::VectorRealT38_surface_flux_values::VectoruEltype39end4041@inline function nelements(elements::P4estElementContainer)42return size(elements.node_coordinates, ndims(elements) + 2)43end44@inline Base.ndims(::P4estElementContainer{NDIMS}) where {NDIMS} = NDIMS45@inline function Base.eltype(::P4estElementContainer{NDIMS, RealT, uEltype}) where {46NDIMS,47RealT,48uEltype49}50return uEltype51end5253# Only one-dimensional `Array`s are `resize!`able in Julia.54# Hence, we use `Vector`s as internal storage and `resize!`55# them whenever needed. Then, we reuse the same memory by56# `unsafe_wrap`ping multi-dimensional `Array`s around the57# internal storage.58function Base.resize!(elements::P4estElementContainer, capacity)59@unpack _node_coordinates, _jacobian_matrix, _contravariant_vectors,60_inverse_jacobian, _surface_flux_values = elements6162n_dims = ndims(elements)63n_nodes = size(elements.node_coordinates, 2)64n_variables = size(elements.surface_flux_values, 1)65ArrayType = storage_type(elements)6667resize!(_node_coordinates, n_dims * n_nodes^n_dims * capacity)68elements.node_coordinates = unsafe_wrap_or_alloc(ArrayType,69_node_coordinates,70(n_dims,71ntuple(_ -> n_nodes, n_dims)...,72capacity))7374resize!(_jacobian_matrix, n_dims^2 * n_nodes^n_dims * capacity)75elements.jacobian_matrix = unsafe_wrap_or_alloc(ArrayType,76_jacobian_matrix,77(n_dims, n_dims,78ntuple(_ -> n_nodes, n_dims)...,79capacity))8081resize!(_contravariant_vectors, length(_jacobian_matrix))82elements.contravariant_vectors = unsafe_wrap_or_alloc(ArrayType,83_contravariant_vectors,84size(elements.jacobian_matrix))8586resize!(_inverse_jacobian, n_nodes^n_dims * capacity)87elements.inverse_jacobian = unsafe_wrap_or_alloc(ArrayType,88_inverse_jacobian,89(ntuple(_ -> n_nodes, n_dims)...,90capacity))9192resize!(_surface_flux_values,93n_variables * n_nodes^(n_dims - 1) * (n_dims * 2) * capacity)94elements.surface_flux_values = unsafe_wrap_or_alloc(ArrayType,95_surface_flux_values,96(n_variables,97ntuple(_ -> n_nodes,98n_dims - 1)...,99n_dims * 2, capacity))100101return nothing102end103104# Create element container and initialize element data105function init_elements(mesh::Union{P4estMesh{NDIMS, NDIMS, RealT},106P4estMeshView{NDIMS, NDIMS, RealT},107T8codeMesh{NDIMS, RealT}},108equations,109basis,110::Type{uEltype}) where {NDIMS, RealT <: Real, uEltype <: Real}111nelements = ncells(mesh)112113_node_coordinates = Vector{RealT}(undef, NDIMS * nnodes(basis)^NDIMS * nelements)114node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates),115(NDIMS, ntuple(_ -> nnodes(basis), NDIMS)...,116nelements))117118_jacobian_matrix = Vector{RealT}(undef, NDIMS^2 * nnodes(basis)^NDIMS * nelements)119jacobian_matrix = unsafe_wrap(Array, pointer(_jacobian_matrix),120(NDIMS, NDIMS, ntuple(_ -> nnodes(basis), NDIMS)...,121nelements))122123_contravariant_vectors = similar(_jacobian_matrix)124contravariant_vectors = unsafe_wrap(Array, pointer(_contravariant_vectors),125size(jacobian_matrix))126127_inverse_jacobian = Vector{RealT}(undef, nnodes(basis)^NDIMS * nelements)128inverse_jacobian = unsafe_wrap(Array, pointer(_inverse_jacobian),129(ntuple(_ -> nnodes(basis), NDIMS)..., nelements))130131_surface_flux_values = Vector{uEltype}(undef,132nvariables(equations) *133nnodes(basis)^(NDIMS - 1) * (NDIMS * 2) *134nelements)135surface_flux_values = unsafe_wrap(Array, pointer(_surface_flux_values),136(nvariables(equations),137ntuple(_ -> nnodes(basis), NDIMS - 1)...,138NDIMS * 2, nelements))139140elements = P4estElementContainer{NDIMS, RealT, uEltype,141NDIMS + 1, NDIMS + 2, NDIMS + 3,142Array{RealT, NDIMS + 1},143Array{RealT, NDIMS + 2},144Array{RealT, NDIMS + 3},145Vector{RealT},146Array{uEltype, NDIMS + 2}, Vector{uEltype}}(node_coordinates,147jacobian_matrix,148contravariant_vectors,149inverse_jacobian,150surface_flux_values,151_node_coordinates,152_jacobian_matrix,153_contravariant_vectors,154_inverse_jacobian,155_surface_flux_values)156157init_elements!(elements, mesh, basis)158return elements159end160161function Adapt.parent_type(::Type{<:P4estElementContainer{<:Any, <:Any, <:Any, <:Any,162<:Any, <:Any, ArrayT}}) where {ArrayT}163return ArrayT164end165166# Manual adapt_structure since we have aliasing memory167function Adapt.adapt_structure(to,168elements::P4estElementContainer{NDIMS}) where {NDIMS}169# Adapt underlying storage170_node_coordinates = adapt(to, elements._node_coordinates)171_jacobian_matrix = adapt(to, elements._jacobian_matrix)172_contravariant_vectors = adapt(to, elements._contravariant_vectors)173_inverse_jacobian = adapt(to, elements._inverse_jacobian)174_surface_flux_values = adapt(to, elements._surface_flux_values)175176RealT = eltype(_inverse_jacobian)177uEltype = eltype(_surface_flux_values)178179# Wrap arrays again180node_coordinates = unsafe_wrap_or_alloc(to, _node_coordinates,181size(elements.node_coordinates))182jacobian_matrix = unsafe_wrap_or_alloc(to, _jacobian_matrix,183size(elements.jacobian_matrix))184contravariant_vectors = unsafe_wrap_or_alloc(to, _contravariant_vectors,185size(jacobian_matrix))186inverse_jacobian = unsafe_wrap_or_alloc(to, _inverse_jacobian,187size(elements.inverse_jacobian))188surface_flux_values = unsafe_wrap_or_alloc(to, _surface_flux_values,189size(elements.surface_flux_values))190191new_type_params = (NDIMS,192RealT, uEltype,193NDIMS + 1, NDIMS + 2, NDIMS + 3,194typeof(inverse_jacobian), # ArrayRealTNDIMSP1195typeof(node_coordinates), # ArrayRealTNDIMSP2196typeof(jacobian_matrix), # ArrayRealTNDIMSP3197typeof(_node_coordinates), # VectorRealT198typeof(surface_flux_values), # ArrayuEltypeNDIMSP2199typeof(_surface_flux_values)) # VectoruEltype200return P4estElementContainer{new_type_params...}(node_coordinates,201jacobian_matrix,202contravariant_vectors,203inverse_jacobian,204surface_flux_values,205_node_coordinates,206_jacobian_matrix,207_contravariant_vectors,208_inverse_jacobian,209_surface_flux_values)210end211212mutable struct P4estInterfaceContainer{NDIMS, RealT <: Real, uEltype <: Real,213NDIMSP1, NDIMSP2,214uArray <: DenseArray{uEltype, NDIMSP2},215NormalArray <:216Union{DenseArray{RealT, NDIMSP1}, Nothing},217IdsMatrix <: DenseMatrix{Int},218IndicesMatrix <:219DenseMatrix{NTuple{NDIMS, Symbol}},220uVector <: DenseVector{uEltype},221NormalVector <:222Union{DenseVector{RealT}, Nothing},223IdsVector <: DenseVector{Int},224IndicesVector <:225DenseVector{NTuple{NDIMS, Symbol}}} <:226AbstractInterfaceContainer227u::uArray # [primary/secondary, variable, i, j, interface]228normal_directions::NormalArray # [dimension, i, j, interface]229neighbor_ids::IdsMatrix # [primary/secondary, interface]230node_indices::IndicesMatrix # [primary/secondary, interface]231232# internal `resize!`able storage233_u::uVector234_normal_directions::NormalVector235_neighbor_ids::IdsVector236_node_indices::IndicesVector237end238239# `trivial` means that the interface normals can be taken from240# the outer/surface element nodes241@inline trivial_interface_normals(::LobattoLegendreBasis) = true242# For Gauss-Legendre basis, the interface normals need to be interpolated,243# analogous to the surface values.244@inline trivial_interface_normals(::GaussLegendreBasis) = false245246@inline function ninterfaces(interfaces::P4estInterfaceContainer)247return size(interfaces.neighbor_ids, 2)248end249@inline Base.ndims(::P4estInterfaceContainer{NDIMS}) where {NDIMS} = NDIMS250251# See explanation of Base.resize! for the element container252function Base.resize!(interfaces::P4estInterfaceContainer, capacity)253@unpack _u, _normal_directions, _neighbor_ids, _node_indices = interfaces254255n_dims = ndims(interfaces)256n_nodes = size(interfaces.u, 3)257n_variables = size(interfaces.u, 2)258ArrayType = storage_type(interfaces)259260resize!(_u, 2 * n_variables * n_nodes^(n_dims - 1) * capacity)261interfaces.u = unsafe_wrap(ArrayType, pointer(_u),262(2, n_variables, ntuple(_ -> n_nodes, n_dims - 1)...,263capacity))264265if _normal_directions === nothing # trivial interface normals, e.g. for Lobatto-Legendre basis266interfaces.normal_directions = nothing267else # non-trivial interface normals, e.g. for Gauss-Legendre basis268resize!(_normal_directions, n_dims * n_nodes^(n_dims - 1) * capacity)269interfaces.normal_directions = unsafe_wrap(ArrayType,270pointer(_normal_directions),271(n_dims,272ntuple(_ -> n_nodes,273n_dims - 1)...,274capacity))275end276277resize!(_neighbor_ids, 2 * capacity)278interfaces.neighbor_ids = unsafe_wrap(ArrayType, pointer(_neighbor_ids),279(2, capacity))280281resize!(_node_indices, 2 * capacity)282interfaces.node_indices = unsafe_wrap(ArrayType, pointer(_node_indices),283(2, capacity))284285return nothing286end287288# Create interface container and initialize interface data.289function init_interfaces(mesh::Union{P4estMesh, P4estMeshView, T8codeMesh}, equations,290basis, elements)291NDIMS = ndims(elements)292RealT = real(mesh)293uEltype = eltype(elements)294295# Initialize container296n_interfaces = count_required_surfaces(mesh).interfaces297298_u = Vector{uEltype}(undef,2992 * nvariables(equations) * nnodes(basis)^(NDIMS - 1) *300n_interfaces)301u = unsafe_wrap(Array, pointer(_u),302(2, nvariables(equations), ntuple(_ -> nnodes(basis), NDIMS - 1)...,303n_interfaces))304305if !trivial_interface_normals(basis)306_normal_directions = Vector{RealT}(undef,307NDIMS * nnodes(basis)^(NDIMS - 1) *308n_interfaces)309normal_directions = unsafe_wrap(Array, pointer(_normal_directions),310(NDIMS,311ntuple(_ -> nnodes(basis), NDIMS - 1)...,312n_interfaces))313else314_normal_directions = nothing315normal_directions = nothing316end317318_neighbor_ids = Vector{Int}(undef, 2 * n_interfaces)319neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids), (2, n_interfaces))320321_node_indices = Vector{NTuple{NDIMS, Symbol}}(undef, 2 * n_interfaces)322node_indices = unsafe_wrap(Array, pointer(_node_indices), (2, n_interfaces))323324interfaces = P4estInterfaceContainer{NDIMS, RealT, uEltype,325NDIMS + 1, NDIMS + 2,326typeof(u), typeof(normal_directions),327typeof(neighbor_ids), typeof(node_indices),328typeof(_u), typeof(_normal_directions),329typeof(_neighbor_ids), typeof(_node_indices)}(u,330normal_directions,331neighbor_ids,332node_indices,333_u,334_normal_directions,335_neighbor_ids,336_node_indices)337338init_interfaces!(interfaces, elements, mesh, basis)339340return interfaces341end342343function init_interfaces!(interfaces, elements,344mesh::Union{P4estMesh, P4estMeshView}, basis)345init_surfaces!(interfaces, nothing, nothing, mesh)346init_normal_directions!(interfaces, basis, elements)347348return interfaces349end350351function Adapt.parent_type(::Type{<:P4estInterfaceContainer{<:Any, <:Any, <:Any,352<:Any, <:Any,353ArrayT}}) where {ArrayT}354return ArrayT355end356357# Manual adapt_structure since we have aliasing memory358function Adapt.adapt_structure(to,359interfaces::P4estInterfaceContainer{NDIMS,360RealT,361uEltype}) where {362NDIMS,363uEltype,364RealT365}366# Adapt underlying storage367_u = adapt(to, interfaces._u)368_normal_directions = interfaces._normal_directions === nothing ? nothing :369adapt(to, interfaces._normal_directions)370_neighbor_ids = adapt(to, interfaces._neighbor_ids)371_node_indices = adapt(to, interfaces._node_indices)372# Wrap arrays again373u = unsafe_wrap_or_alloc(to, _u, size(interfaces.u))374normal_directions = _normal_directions === nothing ? nothing : # Check if interface normals are trivial375unsafe_wrap_or_alloc(to, _normal_directions,376size(interfaces.normal_directions))377neighbor_ids = unsafe_wrap_or_alloc(to, _neighbor_ids,378size(interfaces.neighbor_ids))379node_indices = unsafe_wrap_or_alloc(to, _node_indices,380size(interfaces.node_indices))381382new_type_params = (NDIMS,383RealT, eltype(_u),384NDIMS + 1, NDIMS + 2,385typeof(u), typeof(normal_directions),386typeof(neighbor_ids), typeof(node_indices),387typeof(_u), typeof(_normal_directions),388typeof(_neighbor_ids), typeof(_node_indices))389return P4estInterfaceContainer{new_type_params...}(u, normal_directions,390neighbor_ids, node_indices,391_u, _normal_directions,392_neighbor_ids, _node_indices)393end394395mutable struct P4estBoundaryContainer{NDIMS, uEltype <: Real, NDIMSP1,396uArray <: DenseArray{uEltype, NDIMSP1},397IdsVector <: DenseVector{Int},398IndicesVector <:399DenseVector{NTuple{NDIMS, Symbol}},400uVector <: DenseVector{uEltype}} <:401AbstractBoundaryContainer402u::uArray # [variables, i, j, boundary]403neighbor_ids::IdsVector # [boundary]404node_indices::IndicesVector # [boundary]405name::Vector{Symbol} # [boundary]406407# internal `resize!`able storage408_u::uVector409end410411@inline function nboundaries(boundaries::P4estBoundaryContainer)412return length(boundaries.neighbor_ids)413end414@inline Base.ndims(::P4estBoundaryContainer{NDIMS}) where {NDIMS} = NDIMS415@inline function Base.eltype(::P4estBoundaryContainer{NDIMS, uEltype}) where {NDIMS,416uEltype}417return uEltype418end419420# See explanation of Base.resize! for the element container421function Base.resize!(boundaries::P4estBoundaryContainer, capacity)422@unpack _u, neighbor_ids, node_indices, name = boundaries423424n_dims = ndims(boundaries)425n_nodes = size(boundaries.u, 2)426n_variables = size(boundaries.u, 1)427ArrayType = storage_type(boundaries)428429resize!(_u, n_variables * n_nodes^(n_dims - 1) * capacity)430boundaries.u = unsafe_wrap(ArrayType, pointer(_u),431(n_variables, ntuple(_ -> n_nodes, n_dims - 1)...,432capacity))433434resize!(neighbor_ids, capacity)435436resize!(node_indices, capacity)437438resize!(name, capacity)439440return nothing441end442443# Create interface container and initialize interface data in `elements`.444function init_boundaries(mesh::Union{P4estMesh, P4estMeshView, T8codeMesh}, equations,445basis, elements)446NDIMS = ndims(elements)447uEltype = eltype(elements)448449# Initialize container450n_boundaries = count_required_surfaces(mesh).boundaries451452_u = Vector{uEltype}(undef,453nvariables(equations) * nnodes(basis)^(NDIMS - 1) *454n_boundaries)455u = unsafe_wrap(Array, pointer(_u),456(nvariables(equations), ntuple(_ -> nnodes(basis), NDIMS - 1)...,457n_boundaries))458459neighbor_ids = Vector{Int}(undef, n_boundaries)460node_indices = Vector{NTuple{NDIMS, Symbol}}(undef, n_boundaries)461names = Vector{Symbol}(undef, n_boundaries)462463boundaries = P4estBoundaryContainer{NDIMS, uEltype, NDIMS + 1, typeof(u),464typeof(neighbor_ids), typeof(node_indices),465typeof(_u)}(u, neighbor_ids,466node_indices, names,467_u)468469if n_boundaries > 0470init_boundaries!(boundaries, mesh)471end472473return boundaries474end475476function init_boundaries!(boundaries, mesh::Union{P4estMesh, P4estMeshView})477init_surfaces!(nothing, nothing, boundaries, mesh)478479return boundaries480end481482# Function barrier for type stability483function init_boundaries_iter_face_inner(info_pw, boundaries, boundary_id, mesh)484# Extract boundary data485side_pw = load_pointerwrapper_side(info_pw)486# Get local tree, one-based indexing487tree_pw = load_pointerwrapper_tree(mesh.p4est, side_pw.treeid[] + 1)488# Quadrant numbering offset of this quadrant489offset = tree_pw.quadrants_offset[]490491# Verify before accessing is.full, but this should never happen492@assert side_pw.is_hanging[] == false493494local_quad_id = side_pw.is.full.quadid[]495# Global ID of this quad496quad_id = offset + local_quad_id497498# Write data to boundaries container499# `p4est` uses zero-based indexing; convert to one-based indexing500boundaries.neighbor_ids[boundary_id] = quad_id + 1501502# Face at which the boundary lies503face = side_pw.face[]504505# Save boundaries.node_indices dimension specific in containers_[23]d.jl506init_boundary_node_indices!(boundaries, face, boundary_id)507508# One-based indexing509boundaries.name[boundary_id] = mesh.boundary_names[face + 1, side_pw.treeid[] + 1]510511return nothing512end513514function Adapt.parent_type(::Type{<:P4estBoundaryContainer{<:Any, <:Any, <:Any, ArrayT}}) where {ArrayT}515return ArrayT516end517518# Manual adapt_structure since we have aliasing memory519function Adapt.adapt_structure(to, boundaries::P4estBoundaryContainer)520_u = adapt(to, boundaries._u)521u = unsafe_wrap_or_alloc(to, _u, size(boundaries.u))522neighbor_ids = adapt(to, boundaries.neighbor_ids)523node_indices = adapt(to, boundaries.node_indices)524name = boundaries.name525526NDIMS = ndims(boundaries)527return P4estBoundaryContainer{NDIMS, eltype(_u), NDIMS + 1, typeof(u),528typeof(neighbor_ids), typeof(node_indices),529typeof(_u)}(u, neighbor_ids, node_indices,530name, _u)531end532533# Container data structure (structure-of-arrays style) for DG L2 mortars534#535# The positions used in `neighbor_ids` are 1:3 (in 2D) or 1:5 (in 3D), where 1:2 (in 2D)536# or 1:4 (in 3D) are the small elements numbered in z-order and 3 or 5 is the large element.537# The solution values on the mortar element are saved in `u`, where `position` is the number538# of the small element that corresponds to the respective part of the mortar element.539# The first dimension `small/large side` takes 1 for small side and 2 for large side.540#541# Illustration of the positions in `neighbor_ids` in 3D, where ξ and η are the local coordinates542# of the mortar element, which are precisely the local coordinates that span543# the surface of the smaller side.544# Note that the orientation in the physical space is completely irrelevant here.545# ┌─────────────┬─────────────┐ ┌───────────────────────────┐546# │ │ │ │ │547# │ small │ small │ │ │548# │ 3 │ 4 │ │ │549# │ │ │ │ large │550# ├─────────────┼─────────────┤ │ 5 │551# η │ │ │ │ │552# │ small │ small │ │ │553# ↑ │ 1 │ 2 │ │ │554# │ │ │ │ │ │555# │ └─────────────┴─────────────┘ └───────────────────────────┘556# │557# ⋅────> ξ558mutable struct P4estMortarContainer{NDIMS, uEltype <: Real, NDIMSP1, NDIMSP3,559uArray <: DenseArray{uEltype, NDIMSP3},560IdsMatrix <: DenseMatrix{Int},561IndicesMatrix <:562DenseMatrix{NTuple{NDIMS, Symbol}},563uVector <: DenseVector{uEltype},564IdsVector <: DenseVector{Int},565IndicesVector <:566DenseVector{NTuple{NDIMS, Symbol}}} <:567AbstractMortarContainer568u::uArray # [small/large side, variable, position, i, j, mortar]569neighbor_ids::IdsMatrix # [position, mortar]570node_indices::IndicesMatrix # [small/large, mortar]571572# internal `resize!`able storage573_u::uVector574_neighbor_ids::IdsVector575_node_indices::IndicesVector576end577578@inline nmortars(mortars::P4estMortarContainer) = size(mortars.neighbor_ids, 2)579@inline Base.ndims(::P4estMortarContainer{NDIMS}) where {NDIMS} = NDIMS580@inline function Base.eltype(::P4estMortarContainer{NDIMS, uEltype}) where {NDIMS,581uEltype}582return uEltype583end584585# See explanation of Base.resize! for the element container586function Base.resize!(mortars::P4estMortarContainer, capacity)587@unpack _u, _neighbor_ids, _node_indices = mortars588589n_dims = ndims(mortars)590n_nodes = size(mortars.u, 4)591n_variables = size(mortars.u, 2)592ArrayType = storage_type(mortars)593594resize!(_u, 2 * n_variables * 2^(n_dims - 1) * n_nodes^(n_dims - 1) * capacity)595mortars.u = unsafe_wrap(ArrayType, pointer(_u),596(2, n_variables, 2^(n_dims - 1),597ntuple(_ -> n_nodes, n_dims - 1)..., capacity))598599resize!(_neighbor_ids, (2^(n_dims - 1) + 1) * capacity)600mortars.neighbor_ids = unsafe_wrap(ArrayType, pointer(_neighbor_ids),601(2^(n_dims - 1) + 1, capacity))602603resize!(_node_indices, 2 * capacity)604mortars.node_indices = unsafe_wrap(ArrayType, pointer(_node_indices), (2, capacity))605606return nothing607end608609# Create mortar container and initialize mortar data.610function init_mortars(mesh::Union{P4estMesh, P4estMeshView, T8codeMesh}, equations,611basis, elements)612NDIMS = ndims(elements)613uEltype = eltype(elements)614615# Initialize container616n_mortars = count_required_surfaces(mesh).mortars617618_u = Vector{uEltype}(undef,6192 * nvariables(equations) * 2^(NDIMS - 1) *620nnodes(basis)^(NDIMS - 1) * n_mortars)621u = unsafe_wrap(Array, pointer(_u),622(2, nvariables(equations), 2^(NDIMS - 1),623ntuple(_ -> nnodes(basis), NDIMS - 1)..., n_mortars))624625_neighbor_ids = Vector{Int}(undef, (2^(NDIMS - 1) + 1) * n_mortars)626neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids),627(2^(NDIMS - 1) + 1, n_mortars))628629_node_indices = Vector{NTuple{NDIMS, Symbol}}(undef, 2 * n_mortars)630node_indices = unsafe_wrap(Array, pointer(_node_indices), (2, n_mortars))631632mortars = P4estMortarContainer{NDIMS, uEltype, NDIMS + 1, NDIMS + 3, typeof(u),633typeof(neighbor_ids), typeof(node_indices),634typeof(_u), typeof(_neighbor_ids),635typeof(_node_indices)}(u,636neighbor_ids,637node_indices,638_u,639_neighbor_ids,640_node_indices)641642if n_mortars > 0643init_mortars!(mortars, mesh)644end645646return mortars647end648649function init_mortars!(mortars, mesh::Union{P4estMesh, P4estMeshView})650init_surfaces!(nothing, mortars, nothing, mesh)651652return mortars653end654655function Adapt.parent_type(::Type{<:P4estMortarContainer{<:Any, <:Any, <:Any, <:Any,656ArrayT}}) where {ArrayT}657return ArrayT658end659660# Manual adapt_structure since we have aliasing memory661function Adapt.adapt_structure(to, mortars::P4estMortarContainer)662# Adapt underlying storage663_u = adapt(to, mortars._u)664_neighbor_ids = adapt(to, mortars._neighbor_ids)665_node_indices = adapt(to, mortars._node_indices)666667# Wrap arrays again668u = unsafe_wrap_or_alloc(to, _u, size(mortars.u))669neighbor_ids = unsafe_wrap_or_alloc(to, _neighbor_ids, size(mortars.neighbor_ids))670node_indices = unsafe_wrap_or_alloc(to, _node_indices, size(mortars.node_indices))671672NDIMS = ndims(mortars)673new_type_params = (NDIMS,674eltype(_u),675NDIMS + 1, NDIMS + 3,676typeof(u), typeof(neighbor_ids), typeof(node_indices),677typeof(_u), typeof(_neighbor_ids), typeof(_node_indices))678return P4estMortarContainer{new_type_params...}(u, neighbor_ids, node_indices,679_u, _neighbor_ids, _node_indices)680end681682function reinitialize_containers!(mesh::P4estMesh, equations, dg::DGSEM, cache)683n_cells = ncells(mesh)684685# Re-initialize elements container686@unpack elements = cache687resize!(elements, n_cells)688init_elements!(elements, mesh, dg.basis)689690# Resize volume integral and related datastructures691@unpack volume_integral = dg692resize_volume_integral_cache!(cache, mesh, volume_integral, n_cells)693reinit_volume_integral_cache!(cache, mesh, dg, volume_integral, n_cells)694695required = count_required_surfaces(mesh)696697# resize interfaces container698@unpack interfaces = cache699resize!(interfaces, required.interfaces)700701# resize boundaries container702@unpack boundaries = cache703resize!(boundaries, required.boundaries)704705# re-initialize mortars container706@unpack mortars = cache707resize!(mortars, required.mortars)708709# re-initialize containers together to reduce710# the number of iterations over the mesh in `p4est`711init_surfaces!(interfaces, mortars, boundaries, mesh)712713# init_normal_directions! requires that `node_indices` have been initialized714init_normal_directions!(interfaces, dg.basis, elements)715716return nothing717end718719# A helper struct used in initialization methods below720mutable struct InitSurfacesIterFaceUserData{Interfaces, Mortars, Boundaries, Mesh}721interfaces::Interfaces722interface_id::Int723mortars::Mortars724mortar_id::Int725boundaries::Boundaries726boundary_id::Int727mesh::Mesh728end729730function InitSurfacesIterFaceUserData(interfaces, mortars, boundaries, mesh)731return InitSurfacesIterFaceUserData{typeof(interfaces), typeof(mortars),732typeof(boundaries), typeof(mesh)}(interfaces, 1,733mortars, 1,734boundaries, 1,735mesh)736end737738function init_surfaces_iter_face(info, user_data)739# Unpack user_data740data = unsafe_pointer_to_objref(Ptr{InitSurfacesIterFaceUserData}(user_data))741742# Function barrier because the unpacked user_data above is type-unstable743return init_surfaces_iter_face_inner(info, data)744end745746# 2D747function cfunction(::typeof(init_surfaces_iter_face), ::Val{2})748@cfunction(init_surfaces_iter_face, Cvoid,749(Ptr{p4est_iter_face_info_t}, Ptr{Cvoid}))750end751# 3D752function cfunction(::typeof(init_surfaces_iter_face), ::Val{3})753@cfunction(init_surfaces_iter_face, Cvoid,754(Ptr{p8est_iter_face_info_t}, Ptr{Cvoid}))755end756757# Function barrier for type stability758function init_surfaces_iter_face_inner(info, user_data)759@unpack interfaces, mortars, boundaries = user_data760info_pw = PointerWrapper(info)761elem_count = info_pw.sides.elem_count[]762763if elem_count == 2764# Two neighboring elements => Interface or mortar765766# Extract surface data767sides_pw = (load_pointerwrapper_side(info_pw, 1),768load_pointerwrapper_side(info_pw, 2))769770if sides_pw[1].is_hanging[] == false && sides_pw[2].is_hanging[] == false771# No hanging nodes => normal interface772if interfaces !== nothing773init_interfaces_iter_face_inner(info_pw, sides_pw, user_data)774end775else776# Hanging nodes => mortar777if mortars !== nothing778init_mortars_iter_face_inner(info_pw, sides_pw, user_data)779end780end781elseif elem_count == 1782# One neighboring elements => boundary783if boundaries !== nothing784init_boundaries_iter_face_inner(info_pw, user_data)785end786end787return nothing788end789790function init_surfaces!(interfaces, mortars, boundaries, mesh::P4estMesh)791# Let `p4est` iterate over all interfaces and call init_surfaces_iter_face792iter_face_c = cfunction(init_surfaces_iter_face, Val(ndims(mesh)))793user_data = InitSurfacesIterFaceUserData(interfaces, mortars, boundaries, mesh)794795iterate_p4est(mesh.p4est, user_data; iter_face_c = iter_face_c)796797return interfaces798end799800# Initialization of interfaces after the function barrier801function init_interfaces_iter_face_inner(info_pw, sides_pw, user_data)802@unpack interfaces, interface_id, mesh = user_data803user_data.interface_id += 1804805# Get Tuple of local trees, one-based indexing806trees_pw = (load_pointerwrapper_tree(mesh.p4est, sides_pw[1].treeid[] + 1),807load_pointerwrapper_tree(mesh.p4est, sides_pw[2].treeid[] + 1))808# Quadrant numbering offsets of the quadrants at this interface809offsets = SVector(trees_pw[1].quadrants_offset[],810trees_pw[2].quadrants_offset[])811812local_quad_ids = SVector(sides_pw[1].is.full.quadid[], sides_pw[2].is.full.quadid[])813# Global IDs of the neighboring quads814quad_ids = offsets + local_quad_ids815816# Write data to interfaces container817# `p4est` uses zero-based indexing; convert to one-based indexing818interfaces.neighbor_ids[1, interface_id] = quad_ids[1] + 1819interfaces.neighbor_ids[2, interface_id] = quad_ids[2] + 1820821# Face at which the interface lies822faces = (sides_pw[1].face[], sides_pw[2].face[])823824# Save interfaces.node_indices dimension specific in containers_[23]d.jl825init_interface_node_indices!(interfaces, faces, info_pw.orientation[], interface_id)826827return nothing828end829830# Initialization of boundaries after the function barrier831function init_boundaries_iter_face_inner(info_pw, user_data)832@unpack boundaries, boundary_id, mesh = user_data833user_data.boundary_id += 1834835# Extract boundary data836side_pw = load_pointerwrapper_side(info_pw)837# Get local tree, one-based indexing838tree_pw = load_pointerwrapper_tree(mesh.p4est, side_pw.treeid[] + 1)839# Quadrant numbering offset of this quadrant840offset = tree_pw.quadrants_offset[]841842# Verify before accessing is.full, but this should never happen843@assert side_pw.is_hanging[] == false844845local_quad_id = side_pw.is.full.quadid[]846# Global ID of this quad847quad_id = offset + local_quad_id848849# Write data to boundaries container850# `p4est` uses zero-based indexing; convert to one-based indexing851boundaries.neighbor_ids[boundary_id] = quad_id + 1852853# Face at which the boundary lies854face = side_pw.face[]855856# Save boundaries.node_indices dimension specific in containers_[23]d.jl857init_boundary_node_indices!(boundaries, face, boundary_id)858859# One-based indexing860boundaries.name[boundary_id] = mesh.boundary_names[face + 1, side_pw.treeid[] + 1]861862return nothing863end864865# Initialization of mortars after the function barrier866function init_mortars_iter_face_inner(info_pw, sides_pw, user_data)867@unpack mortars, mortar_id, mesh = user_data868user_data.mortar_id += 1869870# Get Tuple of local trees, one-based indexing871trees_pw = (load_pointerwrapper_tree(mesh.p4est, sides_pw[1].treeid[] + 1),872load_pointerwrapper_tree(mesh.p4est, sides_pw[2].treeid[] + 1))873# Quadrant numbering offsets of the quadrants at this interface874offsets = SVector(trees_pw[1].quadrants_offset[],875trees_pw[2].quadrants_offset[])876877if sides_pw[1].is_hanging[] == true878# Left is small, right is large879faces = (sides_pw[1].face[], sides_pw[2].face[])880881local_small_quad_ids = sides_pw[1].is.hanging.quadid[]882# Global IDs of the two small quads883small_quad_ids = offsets[1] .+ local_small_quad_ids884885# Just be sure before accessing is.full886@assert sides_pw[2].is_hanging[] == false887large_quad_id = offsets[2] + sides_pw[2].is.full.quadid[]888else # sides_pw[2].is_hanging[] == true889# Right is small, left is large.890# init_mortar_node_indices! below expects side 1 to contain the small elements.891faces = (sides_pw[2].face[], sides_pw[1].face[])892893local_small_quad_ids = sides_pw[2].is.hanging.quadid[]894# Global IDs of the two small quads895small_quad_ids = offsets[2] .+ local_small_quad_ids896897# Just be sure before accessing is.full898@assert sides_pw[1].is_hanging[] == false899large_quad_id = offsets[1] + sides_pw[1].is.full.quadid[]900end901902# Write data to mortar container, 1 and 2 are the small elements903# `p4est` uses zero-based indexing; convert to one-based indexing904mortars.neighbor_ids[1:(end - 1), mortar_id] .= small_quad_ids[:] .+ 1905# Last entry is the large element906mortars.neighbor_ids[end, mortar_id] = large_quad_id + 1907908init_mortar_node_indices!(mortars, faces, info_pw.orientation[], mortar_id)909910return nothing911end912913# Iterate over all interfaces and count914# - (inner) interfaces915# - mortars916# - boundaries917# and collect the numbers in `user_data` in this order.918function count_surfaces_iter_face(info, user_data)919info_pw = PointerWrapper(info)920elem_count = info_pw.sides.elem_count[]921922if elem_count == 2923# Two neighboring elements => Interface or mortar924925# Extract surface data926sides_pw = (load_pointerwrapper_side(info_pw, 1),927load_pointerwrapper_side(info_pw, 2))928929if sides_pw[1].is_hanging[] == false && sides_pw[2].is_hanging[] == false930# No hanging nodes => normal interface931# Unpack user_data = [interface_count] and increment interface_count932pw = PointerWrapper(Int, user_data)933id = pw[1]934pw[1] = id + 1935else936# Hanging nodes => mortar937# Unpack user_data = [mortar_count] and increment mortar_count938pw = PointerWrapper(Int, user_data)939id = pw[2]940pw[2] = id + 1941end942elseif elem_count == 1943# One neighboring elements => boundary944945# Unpack user_data = [boundary_count] and increment boundary_count946pw = PointerWrapper(Int, user_data)947id = pw[3]948pw[3] = id + 1949end950951return nothing952end953954# 2D955function cfunction(::typeof(count_surfaces_iter_face), ::Val{2})956@cfunction(count_surfaces_iter_face, Cvoid,957(Ptr{p4est_iter_face_info_t}, Ptr{Cvoid}))958end959# 3D960function cfunction(::typeof(count_surfaces_iter_face), ::Val{3})961@cfunction(count_surfaces_iter_face, Cvoid,962(Ptr{p8est_iter_face_info_t}, Ptr{Cvoid}))963end964965function count_required_surfaces(mesh::P4estMesh)966# Let `p4est` iterate over all interfaces and call count_surfaces_iter_face967iter_face_c = cfunction(count_surfaces_iter_face, Val(ndims(mesh)))968969# interfaces, mortars, boundaries970user_data = [0, 0, 0]971972iterate_p4est(mesh.p4est, user_data; iter_face_c = iter_face_c)973974# Return counters975return (interfaces = user_data[1],976mortars = user_data[2],977boundaries = user_data[3])978end979980# Return direction of the face, which is indexed by node_indices981@inline function indices2direction(indices::NTuple{3, Symbol})982if indices[1] === :begin983return 1984elseif indices[1] === :end985return 2986elseif indices[2] === :begin987return 3988elseif indices[2] === :end989return 4990elseif indices[3] === :begin991return 5992else # if indices[3] === :end993return 6994end995end996997@inline function indices2direction(indices::NTuple{2, Symbol})998if indices[1] === :begin999return 11000elseif indices[1] === :end1001return 21002elseif indices[2] === :begin1003return 31004else # if indices[2] === :end1005return 41006end1007end10081009# Build a reduced cache which can be passed to GPU kernels1010@inline function kernel_filter_cache(cache)1011return (;1012elements = (; contravariant_vectors = cache.elements.contravariant_vectors))1013end10141015include("containers_2d.jl")1016include("containers_3d.jl")1017include("containers_parallel.jl")1018include("containers_parallel_2d.jl")1019include("containers_parallel_3d.jl")1020end # @muladd102110221023