Path: blob/main/src/solvers/dgsem_tree/containers_2d.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 TreeElementContainer2D{RealT <: Real, uEltype <: Real} <:9AbstractTreeElementContainer10inverse_jacobian::Vector{RealT} # [elements]11node_coordinates::Array{RealT, 4} # [orientation, i, j, elements]12surface_flux_values::Array{uEltype, 4} # [variables, i, 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::TreeElementContainer2D, 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, 2 * n_nodes * n_nodes * capacity)33elements.node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates),34(2, n_nodes, n_nodes, capacity))3536resize!(_surface_flux_values, n_variables * n_nodes * 2 * 2 * capacity)37elements.surface_flux_values = unsafe_wrap(Array, pointer(_surface_flux_values),38(n_variables, n_nodes, 2 * 2, capacity))3940resize!(cell_ids, capacity)4142return nothing43end4445function TreeElementContainer2D{RealT, uEltype}(capacity::Integer, n_variables,46n_nodes) where {RealT <: Real,47uEltype <: Real}48nan_RealT = convert(RealT, NaN)49nan_uEltype = convert(uEltype, NaN)5051# Initialize fields with defaults52inverse_jacobian = fill(nan_RealT, capacity)5354_node_coordinates = fill(nan_RealT, 2 * n_nodes * n_nodes * capacity)55node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates),56(2, n_nodes, n_nodes, capacity))5758_surface_flux_values = fill(nan_uEltype, n_variables * n_nodes * 2 * 2 * capacity)59surface_flux_values = unsafe_wrap(Array, pointer(_surface_flux_values),60(n_variables, n_nodes, 2 * 2, capacity))6162cell_ids = fill(typemin(Int), capacity)6364return TreeElementContainer2D{RealT, uEltype}(inverse_jacobian, node_coordinates,65surface_flux_values, cell_ids,66_node_coordinates,67_surface_flux_values)68end6970# Create element container and initialize element data71function init_elements(cell_ids, mesh::TreeMesh2D,72equations::AbstractEquations{2},73basis, ::Type{RealT},74::Type{uEltype}) where {RealT <: Real, uEltype <: Real}75# Initialize container76n_elements = length(cell_ids)77elements = TreeElementContainer2D{RealT, uEltype}(n_elements, nvariables(equations),78nnodes(basis))7980init_elements!(elements, cell_ids, mesh, basis)81return elements82end8384function init_elements!(elements, cell_ids, mesh::TreeMesh2D, basis)85nodes = get_nodes(basis)86# Compute the length of the 1D reference interval by integrating87# the function with constant value unity on the corresponding88# element data type (using \circ)89reference_length = integrate(one ∘ eltype, nodes, basis)90# Compute the offset of the midpoint of the 1D reference interval91# (its difference from zero)92reference_offset = (first(nodes) + last(nodes)) / 29394# Store cell ids95elements.cell_ids .= cell_ids9697# Calculate inverse Jacobian and node coordinates98for element in eachelement(elements)99# Get cell id100cell_id = cell_ids[element]101102# Get cell length103dx = length_at_cell(mesh.tree, cell_id)104105# Calculate inverse Jacobian106jacobian = dx / reference_length107elements.inverse_jacobian[element] = inv(jacobian)108109# Calculate node coordinates110# Note that the `tree_coordinates` are the midpoints of the cells.111# Hence, we need to add an offset for `nodes` with a midpoint112# different from zero.113for j in eachnode(basis), i in eachnode(basis)114elements.node_coordinates[1, i, j, element] = (mesh.tree.coordinates[1,115cell_id] +116jacobian *117(nodes[i] - reference_offset))118elements.node_coordinates[2, i, j, element] = (mesh.tree.coordinates[2,119cell_id] +120jacobian *121(nodes[j] - reference_offset))122end123end124125return elements126end127128# Container data structure (structure-of-arrays style) for DG interfaces129mutable struct TreeInterfaceContainer2D{uEltype <: Real} <:130AbstractTreeInterfaceContainer131u::Array{uEltype, 4} # [leftright, variables, i, interfaces]132neighbor_ids::Array{Int, 2} # [leftright, interfaces]133orientations::Vector{Int} # [interfaces]134# internal `resize!`able storage135_u::Vector{uEltype}136_neighbor_ids::Vector{Int}137end138139# See explanation of Base.resize! for the element container140function Base.resize!(interfaces::TreeInterfaceContainer2D, capacity)141n_nodes = nnodes(interfaces)142n_variables = nvariables(interfaces)143@unpack _u, _neighbor_ids, orientations = interfaces144145resize!(_u, 2 * n_variables * n_nodes * capacity)146interfaces.u = unsafe_wrap(Array, pointer(_u),147(2, n_variables, n_nodes, capacity))148149resize!(_neighbor_ids, 2 * capacity)150interfaces.neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids),151(2, capacity))152153resize!(orientations, capacity)154155return nothing156end157158function TreeInterfaceContainer2D{uEltype}(capacity::Integer, n_variables,159n_nodes) where {uEltype <: Real}160nan = convert(uEltype, NaN)161162# Initialize fields with defaults163_u = fill(nan, 2 * n_variables * n_nodes * capacity)164u = unsafe_wrap(Array, pointer(_u),165(2, n_variables, n_nodes, capacity))166167_neighbor_ids = fill(typemin(Int), 2 * capacity)168neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids),169(2, capacity))170171orientations = fill(typemin(Int), capacity)172173return TreeInterfaceContainer2D{uEltype}(u, neighbor_ids, orientations,174_u, _neighbor_ids)175end176177# Create interface container and initialize interface data in `elements`.178function init_interfaces(cell_ids, mesh::TreeMesh2D,179elements::TreeElementContainer2D)180# Initialize container181n_interfaces = count_required_interfaces(mesh, cell_ids)182interfaces = TreeInterfaceContainer2D{eltype(elements)}(n_interfaces,183nvariables(elements),184nnodes(elements))185186# Connect elements with interfaces187init_interfaces!(interfaces, elements, mesh)188return interfaces189end190191# Count the number of interfaces that need to be created192function count_required_interfaces(mesh::TreeMesh2D, cell_ids)193count = 0194195# Iterate over all cells196for cell_id in cell_ids197for direction in eachdirection(mesh.tree)198# Only count interfaces in positive direction to avoid double counting199if direction % 2 == 1200continue201end202203# If no neighbor exists, current cell is small or at boundary and thus we need a mortar204if !has_neighbor(mesh.tree, cell_id, direction)205continue206end207208# Skip if neighbor has children209neighbor_cell_id = mesh.tree.neighbor_ids[direction, cell_id]210if has_children(mesh.tree, neighbor_cell_id)211continue212end213214# Skip if neighbor is on different rank -> create MPI interface instead215if mpi_isparallel() && !is_own_cell(mesh.tree, neighbor_cell_id)216continue217end218219count += 1220end221end222223return count224end225226# Initialize connectivity between elements and interfaces227function init_interfaces!(interfaces, elements, mesh::TreeMesh2D)228# Exit early if there are no interfaces to initialize229if ninterfaces(interfaces) == 0230return nothing231end232233# Construct cell -> element mapping for easier algorithm implementation234tree = mesh.tree235c2e = zeros(Int, length(tree))236for element in eachelement(elements)237c2e[elements.cell_ids[element]] = element238end239240# Reset interface count241count = 0242243# Iterate over all elements to find neighbors and to connect via interfaces244for element in eachelement(elements)245# Get cell id246cell_id = elements.cell_ids[element]247248# Loop over directions249for direction in eachdirection(mesh.tree)250# Only create interfaces in positive direction251if direction % 2 == 1252continue253end254255# If no neighbor exists, current cell is small and thus we need a mortar256if !has_neighbor(mesh.tree, cell_id, direction)257continue258end259260# Skip if neighbor has children261neighbor_cell_id = mesh.tree.neighbor_ids[direction, cell_id]262if has_children(mesh.tree, neighbor_cell_id)263continue264end265266# Skip if neighbor is on different rank -> create MPI interface instead267if mpi_isparallel() && !is_own_cell(mesh.tree, neighbor_cell_id)268continue269end270271# Create interface between elements (1 -> "left" of interface, 2 -> "right" of interface)272count += 1273interfaces.neighbor_ids[2, count] = c2e[neighbor_cell_id]274interfaces.neighbor_ids[1, count] = element275276# Set orientation (x -> 1, y -> 2)277interfaces.orientations[count] = div(direction, 2)278end279end280281@assert count==ninterfaces(interfaces) ("Actual interface count ($count) does not match "*282"expectations $(ninterfaces(interfaces))")283end284285# Container data structure (structure-of-arrays style) for DG boundaries286mutable struct TreeBoundaryContainer2D{RealT <: Real, uEltype <: Real} <:287AbstractTreeBoundaryContainer288u::Array{uEltype, 4} # [leftright, variables, i, boundaries]289neighbor_ids::Vector{Int} # [boundaries]290orientations::Vector{Int} # [boundaries]291neighbor_sides::Vector{Int} # [boundaries]292node_coordinates::Array{RealT, 3} # [orientation, i, elements]293n_boundaries_per_direction::SVector{4, Int} # [direction]294# internal `resize!`able storage295_u::Vector{uEltype}296_node_coordinates::Vector{RealT}297end298299# See explanation of Base.resize! for the element container300function Base.resize!(boundaries::TreeBoundaryContainer2D, capacity)301n_nodes = nnodes(boundaries)302n_variables = nvariables(boundaries)303@unpack _u, _node_coordinates,304neighbor_ids, orientations, neighbor_sides = boundaries305306resize!(_u, 2 * n_variables * n_nodes * capacity)307boundaries.u = unsafe_wrap(Array, pointer(_u),308(2, n_variables, n_nodes, capacity))309310resize!(_node_coordinates, 2 * n_nodes * capacity)311boundaries.node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates),312(2, n_nodes, capacity))313314resize!(neighbor_ids, capacity)315316resize!(orientations, capacity)317318resize!(neighbor_sides, capacity)319320return nothing321end322323function TreeBoundaryContainer2D{RealT, uEltype}(capacity::Integer, n_variables,324n_nodes) where {RealT <: Real,325uEltype <: Real}326nan_RealT = convert(RealT, NaN)327nan_uEltype = convert(uEltype, NaN)328329# Initialize fields with defaults330_u = fill(nan_uEltype, 2 * n_variables * n_nodes * capacity)331u = unsafe_wrap(Array, pointer(_u),332(2, n_variables, n_nodes, capacity))333334neighbor_ids = fill(typemin(Int), capacity)335336orientations = fill(typemin(Int), capacity)337338neighbor_sides = fill(typemin(Int), capacity)339340_node_coordinates = fill(nan_RealT, 2 * n_nodes * capacity)341node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates),342(2, n_nodes, capacity))343344n_boundaries_per_direction = SVector(0, 0, 0, 0)345346return TreeBoundaryContainer2D{RealT, uEltype}(u, neighbor_ids, orientations,347neighbor_sides,348node_coordinates,349n_boundaries_per_direction,350_u, _node_coordinates)351end352353# Create boundaries container and initialize boundary data in `elements`.354function init_boundaries(cell_ids, mesh::TreeMesh2D,355elements::TreeElementContainer2D, basis)356# Initialize container357n_boundaries = count_required_boundaries(mesh, cell_ids)358boundaries = TreeBoundaryContainer2D{real(elements), eltype(elements)}(n_boundaries,359nvariables(elements),360nnodes(elements))361362# Connect elements with boundaries363init_boundaries!(boundaries, elements, mesh, basis)364return boundaries365end366367# Count the number of boundaries that need to be created368function count_required_boundaries(mesh::TreeMesh2D, cell_ids)369count = 0370371# Iterate over all cells372for cell_id in cell_ids373for direction in eachdirection(mesh.tree)374# If neighbor exists, current cell is not at a boundary375if has_neighbor(mesh.tree, cell_id, direction)376continue377end378379# If coarse neighbor exists, current cell is not at a boundary380if has_coarse_neighbor(mesh.tree, cell_id, direction)381continue382end383384# No neighbor exists in this direction -> must be a boundary385count += 1386end387end388389return count390end391392# For Lobatto points, we can simply use the outer nodes of the elements as boundary nodes.393function calc_boundary_node_coordinates!(boundaries, element, count, direction,394elements, mesh::TreeMesh2D,395basis::LobattoLegendreBasis)396el_node_coords = elements.node_coordinates397bnd_node_coords = boundaries.node_coordinates398399if direction == 1 # -x direction400@views bnd_node_coords[:, :, count] .= el_node_coords[:, 1, :, element]401elseif direction == 2 # +x direction402@views bnd_node_coords[:, :, count] .= el_node_coords[:, end, :, element]403elseif direction == 3 # -y direction404@views bnd_node_coords[:, :, count] .= el_node_coords[:, :, 1, element]405elseif direction == 4 # +y direction406@views bnd_node_coords[:, :, count] .= el_node_coords[:, :, end, element]407else408error("should not happen")409end410411return nothing412end413414# For Gauss points, we need to interpolate the boundary node coordinates.415function calc_boundary_node_coordinates!(boundaries, element, count, direction,416elements, mesh::TreeMesh2D,417basis::GaussLegendreBasis)418boundary_matrix = basis.boundary_interpolation419el_node_coords = elements.node_coordinates420bnd_node_coords = boundaries.node_coordinates421422if direction == 1 # -x direction: interpolate in x for each y node j423for j in eachnode(basis)424for orientation in 1:2 # Need to set both x and y coordinate of boundary node425@views bnd_node_coords[orientation, j, count] = dot(boundary_matrix[:,4261],427el_node_coords[orientation,428:,429j,430element])431end432end433elseif direction == 2 # +x direction: interpolate in x for each y node j434for j in eachnode(basis)435for orientation in 1:2 # Need to set both x and y coordinate of boundary node436@views bnd_node_coords[orientation, j, count] = dot(boundary_matrix[:,4372],438el_node_coords[orientation,439:,440j,441element])442end443end444elseif direction == 3 # -y direction: interpolate in y for each x node i445for i in eachnode(basis)446for orientation in 1:2 # Need to set both x and y coordinate of boundary node447@views bnd_node_coords[orientation, i, count] = dot(boundary_matrix[:,4481],449el_node_coords[orientation,450i,451:,452element])453end454end455elseif direction == 4 # +y direction: interpolate in y for each x node i456for i in eachnode(basis)457for orientation in 1:2 # Need to set both x and y coordinate of boundary node458@views bnd_node_coords[orientation, i, count] = dot(boundary_matrix[:,4592],460el_node_coords[orientation,461i,462:,463element])464end465end466else467error("should not happen")468end469470return nothing471end472473# Initialize connectivity between elements and boundaries474function init_boundaries!(boundaries, elements, mesh::TreeMesh2D, basis)475# Exit early if there are no boundaries to initialize476if nboundaries(boundaries) == 0477# In this case n_boundaries_per_direction still needs to be reset!478boundaries.n_boundaries_per_direction = SVector(0, 0, 0, 0)479return nothing480end481482# Reset boundaries count483count = 0484485# Initialize boundary counts486counts_per_direction = MVector(0, 0, 0, 0)487488# OBS! Iterate over directions first, then over elements, and count boundaries in each direction489# Rationale: This way the boundaries are internally sorted by the directions -x, +x, -y etc.,490# obviating the need to store the boundary condition to be applied explicitly.491# Loop over directions492for direction in eachdirection(mesh.tree)493# Iterate over all elements to find missing neighbors and to connect to boundaries494for element in eachelement(elements)495# Get cell id496cell_id = elements.cell_ids[element]497498# If neighbor exists, current cell is not at a boundary499if has_neighbor(mesh.tree, cell_id, direction)500continue501end502503# If coarse neighbor exists, current cell is not at a boundary504if has_coarse_neighbor(mesh.tree, cell_id, direction)505continue506end507508# Create boundary509count += 1510counts_per_direction[direction] += 1511512# Set neighbor element id513boundaries.neighbor_ids[count] = element514515# Set neighbor side, which denotes the direction (1 -> negative, 2 -> positive) of the element516if iseven(direction)517boundaries.neighbor_sides[count] = 1518else519boundaries.neighbor_sides[count] = 2520end521522# Set orientation (x -> 1, y -> 2)523if direction in (1, 2)524boundaries.orientations[count] = 1 # x direction525else526boundaries.orientations[count] = 2 # y direction527end528529# Calculate node coordinates530calc_boundary_node_coordinates!(boundaries, element, count, direction,531elements, mesh, basis)532end533end534535@assert count==nboundaries(boundaries) ("Actual boundaries count ($count) does not match "*536"expectations $(nboundaries(boundaries))")537@assert sum(counts_per_direction) == count538539boundaries.n_boundaries_per_direction = SVector(counts_per_direction)540541return boundaries.n_boundaries_per_direction542end543544# Container data structure (structure-of-arrays style) for DG L2 mortars545# Positions/directions for orientations = 1, large_sides = 2:546# mortar is orthogonal to x-axis, large side is in positive coordinate direction wrt mortar547# | |548# upper = 2 | |549# | |550# | 3 = large side551# | |552# lower = 1 | |553# | |554mutable struct TreeL2MortarContainer2D{uEltype <: Real} <:555AbstractTreeL2MortarContainer556u_upper::Array{uEltype, 4} # [leftright, variables, i, mortars]557u_lower::Array{uEltype, 4} # [leftright, variables, i, mortars]558neighbor_ids::Array{Int, 2} # [position, mortars]559# Large sides: left -> 1, right -> 2560large_sides::Vector{Int} # [mortars]561orientations::Vector{Int} # [mortars]562# internal `resize!`able storage563_u_upper::Vector{uEltype}564_u_lower::Vector{uEltype}565_neighbor_ids::Vector{Int}566end567568# Return number of mortar nodes (L2 mortars are only h-adaptive, not p-adaptive)569@inline nnodes(mortars::TreeL2MortarContainer2D) = size(mortars.u_upper, 3)570# Return number of equation variables571@inline nvariables(mortars::TreeL2MortarContainer2D) = size(mortars.u_upper, 2)572573# See explanation of Base.resize! for the element container574function Base.resize!(mortars::TreeL2MortarContainer2D, capacity)575n_nodes = nnodes(mortars)576n_variables = nvariables(mortars)577@unpack _u_upper, _u_lower, _neighbor_ids,578large_sides, orientations = mortars579580resize!(_u_upper, 2 * n_variables * n_nodes * capacity)581mortars.u_upper = unsafe_wrap(Array, pointer(_u_upper),582(2, n_variables, n_nodes, capacity))583584resize!(_u_lower, 2 * n_variables * n_nodes * capacity)585mortars.u_lower = unsafe_wrap(Array, pointer(_u_lower),586(2, n_variables, n_nodes, capacity))587588resize!(_neighbor_ids, 3 * capacity)589mortars.neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids),590(3, capacity))591592resize!(large_sides, capacity)593594resize!(orientations, capacity)595596return nothing597end598599function TreeL2MortarContainer2D{uEltype}(capacity::Integer, n_variables,600n_nodes) where {uEltype <: Real}601nan = convert(uEltype, NaN)602603# Initialize fields with defaults604_u_upper = fill(nan, 2 * n_variables * n_nodes * capacity)605u_upper = unsafe_wrap(Array, pointer(_u_upper),606(2, n_variables, n_nodes, capacity))607608_u_lower = fill(nan, 2 * n_variables * n_nodes * capacity)609u_lower = unsafe_wrap(Array, pointer(_u_lower),610(2, n_variables, n_nodes, capacity))611612_neighbor_ids = fill(typemin(Int), 3 * capacity)613neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids),614(3, capacity))615616large_sides = fill(typemin(Int), capacity)617618orientations = fill(typemin(Int), capacity)619620return TreeL2MortarContainer2D{uEltype}(u_upper, u_lower,621neighbor_ids,622large_sides, orientations,623_u_upper, _u_lower, _neighbor_ids)624end625626# Allow printing container contents627function Base.show(io::IO, ::MIME"text/plain", c::TreeL2MortarContainer2D)628@nospecialize c # reduce precompilation time629630println(io, '*'^20)631for idx in CartesianIndices(c.u_upper)632println(io, "c.u_upper[$idx] = $(c.u_upper[idx])")633end634for idx in CartesianIndices(c.u_lower)635println(io, "c.u_lower[$idx] = $(c.u_lower[idx])")636end637println(io, "transpose(c.neighbor_ids) = $(transpose(c.neighbor_ids))")638println(io, "c.large_sides = $(c.large_sides)")639println(io, "c.orientations = $(c.orientations)")640print(io, '*'^20)641return nothing642end643644# Create mortar container and initialize mortar data in `elements`.645function init_mortars(cell_ids, mesh::TreeMesh2D,646elements::TreeElementContainer2D,647::LobattoLegendreMortarL2)648# Initialize containers649n_mortars = count_required_mortars(mesh, cell_ids)650mortars = TreeL2MortarContainer2D{eltype(elements)}(n_mortars, nvariables(elements),651nnodes(elements))652653# Connect elements with mortars654init_mortars!(mortars, elements, mesh)655return mortars656end657658# Count the number of mortars that need to be created659function count_required_mortars(mesh::TreeMesh2D, cell_ids)660count = 0661662# Iterate over all cells and count mortars from perspective of coarse cells663for cell_id in cell_ids664for direction in eachdirection(mesh.tree)665# If no neighbor exists, cell is small with large neighbor or at boundary -> do nothing666if !has_neighbor(mesh.tree, cell_id, direction)667continue668end669670# If neighbor has no children, this is a conforming interface -> do nothing671neighbor_id = mesh.tree.neighbor_ids[direction, cell_id]672if !has_children(mesh.tree, neighbor_id)673continue674end675676# Skip if one of the small cells is on different rank -> create mpi mortar instead677# (the coarse cell is always on the local rank)678if mpi_isparallel()679if direction == 1 # small cells left, mortar in x-direction680lower_cell_id = mesh.tree.child_ids[2, neighbor_id]681upper_cell_id = mesh.tree.child_ids[4, neighbor_id]682elseif direction == 2 # small cells right, mortar in x-direction683lower_cell_id = mesh.tree.child_ids[1, neighbor_id]684upper_cell_id = mesh.tree.child_ids[3, neighbor_id]685elseif direction == 3 # small cells left, mortar in y-direction686lower_cell_id = mesh.tree.child_ids[3, neighbor_id]687upper_cell_id = mesh.tree.child_ids[4, neighbor_id]688else # direction == 4, small cells right, mortar in y-direction689lower_cell_id = mesh.tree.child_ids[1, neighbor_id]690upper_cell_id = mesh.tree.child_ids[2, neighbor_id]691end692small_cell_ids = (lower_cell_id, upper_cell_id)693if any(cell -> !is_own_cell(mesh.tree, cell), small_cell_ids)694continue695end696end697698count += 1699end700end701702return count703end704705# Initialize connectivity between elements and mortars706function init_mortars!(mortars, elements, mesh::TreeMesh2D)707# Exit early if there are no mortars to initialize708if nmortars(mortars) == 0709return nothing710end711712# Construct cell -> element mapping for easier algorithm implementation713tree = mesh.tree714c2e = zeros(Int, length(tree))715for element in eachelement(elements)716c2e[elements.cell_ids[element]] = element717end718719# Reset interface count720count = 0721722# Iterate over all elements to find neighbors and to connect via interfaces723for element in eachelement(elements)724# Get cell id725cell_id = elements.cell_ids[element]726727for direction in eachdirection(mesh.tree)728# If no neighbor exists, cell is small with large neighbor -> do nothing729if !has_neighbor(mesh.tree, cell_id, direction)730continue731end732733# If neighbor has no children, this is a conforming interface -> do nothing734neighbor_cell_id = mesh.tree.neighbor_ids[direction, cell_id]735if !has_children(mesh.tree, neighbor_cell_id)736continue737end738739# Skip if one of the small cells is on different rank -> create mpi mortar instead740# (the coarse cell is always on the local rank)741if mpi_isparallel()742if direction == 1 # small cells left, mortar in x-direction743lower_cell_id = mesh.tree.child_ids[2, neighbor_cell_id]744upper_cell_id = mesh.tree.child_ids[4, neighbor_cell_id]745elseif direction == 2 # small cells right, mortar in x-direction746lower_cell_id = mesh.tree.child_ids[1, neighbor_cell_id]747upper_cell_id = mesh.tree.child_ids[3, neighbor_cell_id]748elseif direction == 3 # small cells left, mortar in y-direction749lower_cell_id = mesh.tree.child_ids[3, neighbor_cell_id]750upper_cell_id = mesh.tree.child_ids[4, neighbor_cell_id]751else # direction == 4, small cells right, mortar in y-direction752lower_cell_id = mesh.tree.child_ids[1, neighbor_cell_id]753upper_cell_id = mesh.tree.child_ids[2, neighbor_cell_id]754end755small_cell_ids = (lower_cell_id, upper_cell_id)756if any(cell -> !is_own_cell(mesh.tree, cell), small_cell_ids)757continue758end759end760761# Create mortar between elements:762# 1 -> small element in negative coordinate direction763# 2 -> small element in positive coordinate direction764# 3 -> large element765count += 1766mortars.neighbor_ids[3, count] = element767if direction == 1768mortars.neighbor_ids[1, count] = c2e[mesh.tree.child_ids[2,769neighbor_cell_id]]770mortars.neighbor_ids[2, count] = c2e[mesh.tree.child_ids[4,771neighbor_cell_id]]772elseif direction == 2773mortars.neighbor_ids[1, count] = c2e[mesh.tree.child_ids[1,774neighbor_cell_id]]775mortars.neighbor_ids[2, count] = c2e[mesh.tree.child_ids[3,776neighbor_cell_id]]777elseif direction == 3778mortars.neighbor_ids[1, count] = c2e[mesh.tree.child_ids[3,779neighbor_cell_id]]780mortars.neighbor_ids[2, count] = c2e[mesh.tree.child_ids[4,781neighbor_cell_id]]782elseif direction == 4783mortars.neighbor_ids[1, count] = c2e[mesh.tree.child_ids[1,784neighbor_cell_id]]785mortars.neighbor_ids[2, count] = c2e[mesh.tree.child_ids[2,786neighbor_cell_id]]787else788error("should not happen")789end790791# Set large side, which denotes the direction (1 -> negative, 2 -> positive) of the large side792if iseven(direction)793mortars.large_sides[count] = 1794else795mortars.large_sides[count] = 2796end797798# Set orientation (x -> 1, y -> 2)799if direction in (1, 2)800mortars.orientations[count] = 1801else802mortars.orientations[count] = 2803end804end805end806807@assert count==nmortars(mortars) ("Actual mortar count ($count) does not match "*808"expectations $(nmortars(mortars))")809end810811# Container data structure (structure-of-arrays style) for DG MPI interfaces812mutable struct TreeMPIInterfaceContainer2D{uEltype <: Real} <:813AbstractTreeMPIInterfaceContainer814u::Array{uEltype, 4} # [leftright, variables, i, interfaces]815# Note: `local_neighbor_ids` stores the MPI-local neighbors, but with globally valid index!816local_neighbor_ids::Vector{Int} # [interfaces]817orientations::Vector{Int} # [interfaces]818remote_sides::Vector{Int} # [interfaces]819# internal `resize!`able storage820_u::Vector{uEltype}821end822823# See explanation of Base.resize! for the element container824function Base.resize!(mpi_interfaces::TreeMPIInterfaceContainer2D, capacity)825n_nodes = nnodes(mpi_interfaces)826n_variables = nvariables(mpi_interfaces)827@unpack _u, local_neighbor_ids, orientations, remote_sides = mpi_interfaces828829resize!(_u, 2 * n_variables * n_nodes * capacity)830mpi_interfaces.u = unsafe_wrap(Array, pointer(_u),831(2, n_variables, n_nodes, capacity))832833resize!(local_neighbor_ids, capacity)834835resize!(orientations, capacity)836837resize!(remote_sides, capacity)838839return nothing840end841842function TreeMPIInterfaceContainer2D{uEltype}(capacity::Integer, n_variables,843n_nodes) where {uEltype <: Real}844nan = convert(uEltype, NaN)845846# Initialize fields with defaults847_u = fill(nan, 2 * n_variables * n_nodes * capacity)848u = unsafe_wrap(Array, pointer(_u),849(2, n_variables, n_nodes, capacity))850851local_neighbor_ids = fill(typemin(Int), capacity)852853orientations = fill(typemin(Int), capacity)854855remote_sides = fill(typemin(Int), capacity)856857return TreeMPIInterfaceContainer2D{uEltype}(u, local_neighbor_ids, orientations,858remote_sides, _u)859end860861# Create MPI interface container and initialize MPI interface data in `elements`.862function init_mpi_interfaces(cell_ids, mesh::TreeMesh2D,863elements::TreeElementContainer2D)864# Initialize container865n_mpi_interfaces = count_required_mpi_interfaces(mesh, cell_ids)866mpi_interfaces = TreeMPIInterfaceContainer2D{eltype(elements)}(n_mpi_interfaces,867nvariables(elements),868nnodes(elements))869870# Connect elements with interfaces871init_mpi_interfaces!(mpi_interfaces, elements, mesh)872return mpi_interfaces873end874875# Count the number of MPI interfaces that need to be created876function count_required_mpi_interfaces(mesh::TreeMesh2D, cell_ids)877# No MPI interfaces needed if MPI is not used878if !mpi_isparallel()879return 0880end881882count = 0883884# Iterate over all cells885for cell_id in cell_ids886for direction in eachdirection(mesh.tree)887# If no neighbor exists, current cell is small or at boundary and thus we need a mortar888if !has_neighbor(mesh.tree, cell_id, direction)889continue890end891892# Skip if neighbor has children893neighbor_cell_id = mesh.tree.neighbor_ids[direction, cell_id]894if has_children(mesh.tree, neighbor_cell_id)895continue896end897898# Skip if neighbor is on this rank -> create regular interface instead899if is_own_cell(mesh.tree, neighbor_cell_id)900continue901end902903count += 1904end905end906907return count908end909910# Initialize connectivity between elements and interfaces911function init_mpi_interfaces!(mpi_interfaces, elements, mesh::TreeMesh2D)912# Exit early if there are no MPI interfaces to initialize913if nmpiinterfaces(mpi_interfaces) == 0914return nothing915end916917# Reset interface count918count = 0919920# Iterate over all elements to find neighbors and to connect via mpi_interfaces921for element in eachelement(elements)922# Get cell id923cell_id = elements.cell_ids[element]924925# Loop over directions926for direction in eachdirection(mesh.tree)927# If no neighbor exists, current cell is small and thus we need a mortar928if !has_neighbor(mesh.tree, cell_id, direction)929continue930end931932# Skip if neighbor has children933neighbor_cell_id = mesh.tree.neighbor_ids[direction, cell_id]934if has_children(mesh.tree, neighbor_cell_id)935continue936end937938# Skip if neighbor is on this MPI rank -> create regular interface instead939if is_own_cell(mesh.tree, neighbor_cell_id)940continue941end942943# Create interface between elements944count += 1945# Note: `local_neighbor_ids` stores the MPI-local neighbors,946# but with globally valid index!947mpi_interfaces.local_neighbor_ids[count] = element948949if iseven(direction) # element is "left" of interface, remote cell is "right" of interface950mpi_interfaces.remote_sides[count] = 2951else952mpi_interfaces.remote_sides[count] = 1953end954955# Set orientation (x -> 1, y -> 2)956if direction in (1, 2) # x-direction957mpi_interfaces.orientations[count] = 1958else # y-direction959mpi_interfaces.orientations[count] = 2960end961end962end963964@assert count==nmpiinterfaces(mpi_interfaces) ("Actual interface count ($count) does not match "965*"expectations $(nmpiinterfaces(mpi_interfaces))")966end967968# Container data structure (structure-of-arrays style) for DG L2 mortars969# Positions/directions for orientations = 1, large_sides = 2:970# mortar is orthogonal to x-axis, large side is in positive coordinate direction wrt mortar971# | |972# upper = 2 | |973# | |974# | 3 = large side975# | |976# lower = 1 | |977# | |978mutable struct TreeMPIL2MortarContainer2D{uEltype <: Real} <:979AbstractTreeL2MPIMortarContainer980u_upper::Array{uEltype, 4} # [leftright, variables, i, mortars]981u_lower::Array{uEltype, 4} # [leftright, variables, i, mortars]982# Note: `local_neighbor_ids` stores the MPI-local neighbors, but with globally valid index!983local_neighbor_ids::Vector{Vector{Int}} # [mortars][ids]984local_neighbor_positions::Vector{Vector{Int}} # [mortars][positions]985# Large sides: left -> 1, right -> 2986large_sides::Vector{Int} # [mortars]987orientations::Vector{Int} # [mortars]988# internal `resize!`able storage989_u_upper::Vector{uEltype}990_u_lower::Vector{uEltype}991end992993# Return number of mortar nodes (L2 mortars are only h-adaptive, not p-adaptive)994@inline nnodes(mortars::TreeMPIL2MortarContainer2D) = size(mortars.u_upper, 3)995# Return number of equation variables996@inline nvariables(mortars::TreeMPIL2MortarContainer2D) = size(mortars.u_upper, 2)997998# See explanation of Base.resize! for the element container999function Base.resize!(mpi_mortars::TreeMPIL2MortarContainer2D, capacity)1000n_nodes = nnodes(mpi_mortars)1001n_variables = nvariables(mpi_mortars)1002@unpack _u_upper, _u_lower, local_neighbor_ids, local_neighbor_positions,1003large_sides, orientations = mpi_mortars10041005resize!(_u_upper, 2 * n_variables * n_nodes * capacity)1006mpi_mortars.u_upper = unsafe_wrap(Array, pointer(_u_upper),1007(2, n_variables, n_nodes, capacity))10081009resize!(_u_lower, 2 * n_variables * n_nodes * capacity)1010mpi_mortars.u_lower = unsafe_wrap(Array, pointer(_u_lower),1011(2, n_variables, n_nodes, capacity))10121013resize!(local_neighbor_ids, capacity)1014resize!(local_neighbor_positions, capacity)10151016resize!(large_sides, capacity)10171018resize!(orientations, capacity)10191020return nothing1021end10221023function TreeMPIL2MortarContainer2D{uEltype}(capacity::Integer, n_variables,1024n_nodes) where {uEltype <: Real}1025nan = convert(uEltype, NaN)10261027# Initialize fields with defaults1028_u_upper = fill(nan, 2 * n_variables * n_nodes * capacity)1029u_upper = unsafe_wrap(Array, pointer(_u_upper),1030(2, n_variables, n_nodes, capacity))10311032_u_lower = fill(nan, 2 * n_variables * n_nodes * capacity)1033u_lower = unsafe_wrap(Array, pointer(_u_lower),1034(2, n_variables, n_nodes, capacity))10351036local_neighbor_ids = fill(Vector{Int}(), capacity)1037local_neighbor_positions = fill(Vector{Int}(), capacity)10381039large_sides = fill(typemin(Int), capacity)10401041orientations = fill(typemin(Int), capacity)10421043return TreeMPIL2MortarContainer2D{uEltype}(u_upper, u_lower,1044local_neighbor_ids,1045local_neighbor_positions,1046large_sides, orientations,1047_u_upper, _u_lower)1048end10491050# Create MPI mortar container and initialize MPI mortar data in `elements`.1051function init_mpi_mortars(cell_ids, mesh::TreeMesh2D,1052elements::TreeElementContainer2D,1053::LobattoLegendreMortarL2)1054# Initialize containers1055n_mpi_mortars = count_required_mpi_mortars(mesh, cell_ids)1056mpi_mortars = TreeMPIL2MortarContainer2D{eltype(elements)}(n_mpi_mortars,1057nvariables(elements),1058nnodes(elements))10591060# Connect elements with mortars1061init_mpi_mortars!(mpi_mortars, elements, mesh)1062return mpi_mortars1063end10641065# Count the number of MPI mortars that need to be created1066function count_required_mpi_mortars(mesh::TreeMesh2D, cell_ids)1067# No MPI mortars needed if MPI is not used1068if !mpi_isparallel()1069return 01070end10711072count = 010731074for cell_id in cell_ids1075for direction in eachdirection(mesh.tree)1076# If no neighbor exists, cell is small with large neighbor or at boundary1077if !has_neighbor(mesh.tree, cell_id, direction)1078# If no large neighbor exists, cell is at boundary -> do nothing1079if !has_coarse_neighbor(mesh.tree, cell_id, direction)1080continue1081end10821083# Skip if the large neighbor is on the same rank to prevent double counting1084parent_id = mesh.tree.parent_ids[cell_id]1085large_cell_id = mesh.tree.neighbor_ids[direction, parent_id]1086if is_own_cell(mesh.tree, large_cell_id)1087continue1088end10891090# Current cell is small with large neighbor on a different rank, find the other1091# small cell1092if direction == 1 # small cells right, mortar in x-direction1093lower_cell_id = mesh.tree.child_ids[1, parent_id]1094upper_cell_id = mesh.tree.child_ids[3, parent_id]1095elseif direction == 2 # small cells left, mortar in x-direction1096lower_cell_id = mesh.tree.child_ids[2, parent_id]1097upper_cell_id = mesh.tree.child_ids[4, parent_id]1098elseif direction == 3 # small cells right, mortar in y-direction1099lower_cell_id = mesh.tree.child_ids[1, parent_id]1100upper_cell_id = mesh.tree.child_ids[2, parent_id]1101else # direction == 4, small cells left, mortar in y-direction1102lower_cell_id = mesh.tree.child_ids[3, parent_id]1103upper_cell_id = mesh.tree.child_ids[4, parent_id]1104end11051106if cell_id == lower_cell_id1107sibling_id = upper_cell_id1108elseif cell_id == upper_cell_id1109sibling_id = lower_cell_id1110else1111error("should not happen")1112end11131114# Skip if the other small cell is on the same rank and its id is smaller than the current1115# cell id to prevent double counting1116if is_own_cell(mesh.tree, sibling_id) && sibling_id < cell_id1117continue1118end1119else # Cell has a neighbor1120# If neighbor has no children, this is a conforming interface -> do nothing1121neighbor_id = mesh.tree.neighbor_ids[direction, cell_id]1122if !has_children(mesh.tree, neighbor_id)1123continue1124end11251126# Skip if both small cells are on this rank -> create regular mortar instead1127if direction == 1 # small cells left, mortar in x-direction1128lower_cell_id = mesh.tree.child_ids[2, neighbor_id]1129upper_cell_id = mesh.tree.child_ids[4, neighbor_id]1130elseif direction == 2 # small cells right, mortar in x-direction1131lower_cell_id = mesh.tree.child_ids[1, neighbor_id]1132upper_cell_id = mesh.tree.child_ids[3, neighbor_id]1133elseif direction == 3 # small cells left, mortar in y-direction1134lower_cell_id = mesh.tree.child_ids[3, neighbor_id]1135upper_cell_id = mesh.tree.child_ids[4, neighbor_id]1136else # direction == 4, small cells right, mortar in y-direction1137lower_cell_id = mesh.tree.child_ids[1, neighbor_id]1138upper_cell_id = mesh.tree.child_ids[2, neighbor_id]1139end1140small_cell_ids = (lower_cell_id, upper_cell_id)1141if all(cell -> is_own_cell(mesh.tree, cell), small_cell_ids)1142continue1143end1144end11451146count += 11147end1148end11491150return count1151end11521153# Initialize connectivity between elements and mortars1154function init_mpi_mortars!(mpi_mortars, elements, mesh::TreeMesh2D)1155# Exit early if there are no MPI mortars to initialize1156if nmpimortars(mpi_mortars) == 01157return nothing1158end11591160# Construct cell -> element mapping for easier algorithm implementation1161tree = mesh.tree1162c2e = zeros(Int, length(tree))1163for element in eachelement(elements)1164c2e[elements.cell_ids[element]] = element1165end11661167# Reset mortar count1168count = 011691170# Iterate over all elements to find neighbors and to connect via mortars1171for element in eachelement(elements)1172cell_id = elements.cell_ids[element]11731174for direction in eachdirection(mesh.tree)1175# If no neighbor exists, cell is small with large neighbor or at boundary1176if !has_neighbor(mesh.tree, cell_id, direction)1177# If no large neighbor exists, cell is at boundary -> do nothing1178if !has_coarse_neighbor(mesh.tree, cell_id, direction)1179continue1180end11811182# Skip if the large neighbor is on the same rank -> will be handled in another iteration1183parent_cell_id = mesh.tree.parent_ids[cell_id]1184large_cell_id = mesh.tree.neighbor_ids[direction, parent_cell_id]1185if is_own_cell(mesh.tree, large_cell_id)1186continue1187end11881189# Current cell is small with large neighbor on a different rank, find the other1190# small cell1191if direction == 1 # small cells right, mortar in x-direction1192lower_cell_id = mesh.tree.child_ids[1, parent_cell_id]1193upper_cell_id = mesh.tree.child_ids[3, parent_cell_id]1194elseif direction == 2 # small cells left, mortar in x-direction1195lower_cell_id = mesh.tree.child_ids[2, parent_cell_id]1196upper_cell_id = mesh.tree.child_ids[4, parent_cell_id]1197elseif direction == 3 # small cells right, mortar in y-direction1198lower_cell_id = mesh.tree.child_ids[1, parent_cell_id]1199upper_cell_id = mesh.tree.child_ids[2, parent_cell_id]1200else # direction == 4, small cells left, mortar in y-direction1201lower_cell_id = mesh.tree.child_ids[3, parent_cell_id]1202upper_cell_id = mesh.tree.child_ids[4, parent_cell_id]1203end12041205if cell_id == lower_cell_id1206sibling_id = upper_cell_id1207elseif cell_id == upper_cell_id1208sibling_id = lower_cell_id1209else1210error("should not happen")1211end12121213# Skip if the other small cell is on the same rank and its id is smaller than the current1214# cell id to prevent double counting1215if is_own_cell(mesh.tree, sibling_id) && sibling_id < cell_id1216continue1217end1218else # Cell has a neighbor1219large_cell_id = cell_id # save explicitly for later processing12201221# If neighbor has no children, this is a conforming interface -> do nothing1222neighbor_cell_id = mesh.tree.neighbor_ids[direction, cell_id]1223if !has_children(mesh.tree, neighbor_cell_id)1224continue1225end12261227# Skip if both small cells are on this rank -> create regular mortar instead1228if direction == 1 # small cells left, mortar in x-direction1229lower_cell_id = mesh.tree.child_ids[2, neighbor_cell_id]1230upper_cell_id = mesh.tree.child_ids[4, neighbor_cell_id]1231elseif direction == 2 # small cells right, mortar in x-direction1232lower_cell_id = mesh.tree.child_ids[1, neighbor_cell_id]1233upper_cell_id = mesh.tree.child_ids[3, neighbor_cell_id]1234elseif direction == 3 # small cells left, mortar in y-direction1235lower_cell_id = mesh.tree.child_ids[3, neighbor_cell_id]1236upper_cell_id = mesh.tree.child_ids[4, neighbor_cell_id]1237else # direction == 4, small cells right, mortar in y-direction1238lower_cell_id = mesh.tree.child_ids[1, neighbor_cell_id]1239upper_cell_id = mesh.tree.child_ids[2, neighbor_cell_id]1240end1241small_cell_ids = (lower_cell_id, upper_cell_id)1242if all(cell -> is_own_cell(mesh.tree, cell), small_cell_ids)1243continue1244end1245end12461247# Create mortar between elements:1248# 1 -> small element in negative coordinate direction1249# 2 -> small element in positive coordinate direction1250# 3 -> large element1251count += 112521253# Note: `local_neighbor_ids` stores the MPI-local neighbors,1254# but with globally valid index!1255local_neighbor_ids = Vector{Int}()1256local_neighbor_positions = Vector{Int}()1257if is_own_cell(mesh.tree, lower_cell_id)1258push!(local_neighbor_ids, c2e[lower_cell_id])1259push!(local_neighbor_positions, 1)1260end1261if is_own_cell(mesh.tree, upper_cell_id)1262push!(local_neighbor_ids, c2e[upper_cell_id])1263push!(local_neighbor_positions, 2)1264end1265if is_own_cell(mesh.tree, large_cell_id)1266push!(local_neighbor_ids, c2e[large_cell_id])1267push!(local_neighbor_positions, 3)1268end12691270mpi_mortars.local_neighbor_ids[count] = local_neighbor_ids1271mpi_mortars.local_neighbor_positions[count] = local_neighbor_positions12721273# Set large side, which denotes the direction (1 -> negative, 2 -> positive) of the large side1274# To prevent double counting, the mortars are always identified from the point of view of1275# a large cell, if it is on this rank. In that case, direction points towards the small cells.1276# If the large cell is not on this rank, the point of view of a small cell is taken instead,1277# hence direction points towards the large cell in this case.1278if iseven(direction)1279mpi_mortars.large_sides[count] = is_own_cell(mesh.tree, large_cell_id) ?12801 : 21281else1282mpi_mortars.large_sides[count] = is_own_cell(mesh.tree, large_cell_id) ?12832 : 11284end12851286# Set orientation (1, 2 -> x; 3, 4 -> y)1287if direction in (1, 2)1288mpi_mortars.orientations[count] = 11289else1290mpi_mortars.orientations[count] = 21291end1292end1293end12941295return nothing1296end12971298# Container data structure (structure-of-arrays style) for FCT-type antidiffusive fluxes1299# (i, j+1)1300# |1301# flux2(i, j+1)1302# |1303# (i-1, j) ---flux1(i, j)--- (i, j) ---flux1(i+1, j)--- (i+1, j)1304# |1305# flux2(i, j)1306# |1307# (i, j-1)1308mutable struct ContainerAntidiffusiveFlux2D{uEltype <: Real} <:1309AbstractContainerAntidiffusiveFlux1310antidiffusive_flux1_L::Array{uEltype, 4} # [variables, i, j, elements]1311antidiffusive_flux1_R::Array{uEltype, 4} # [variables, i, j, elements]1312antidiffusive_flux2_L::Array{uEltype, 4} # [variables, i, j, elements]1313antidiffusive_flux2_R::Array{uEltype, 4} # [variables, i, j, elements]1314# internal `resize!`able storage1315_antidiffusive_flux1_L::Vector{uEltype}1316_antidiffusive_flux1_R::Vector{uEltype}1317_antidiffusive_flux2_L::Vector{uEltype}1318_antidiffusive_flux2_R::Vector{uEltype}1319end13201321function ContainerAntidiffusiveFlux2D{uEltype}(capacity::Integer, n_variables,1322n_nodes) where {uEltype <: Real}1323nan_uEltype = convert(uEltype, NaN)13241325# Initialize fields with defaults1326_antidiffusive_flux1_L = fill(nan_uEltype,1327n_variables * (n_nodes + 1) * n_nodes * capacity)1328antidiffusive_flux1_L = unsafe_wrap(Array, pointer(_antidiffusive_flux1_L),1329(n_variables, n_nodes + 1, n_nodes, capacity))1330_antidiffusive_flux1_R = fill(nan_uEltype,1331n_variables * (n_nodes + 1) * n_nodes * capacity)1332antidiffusive_flux1_R = unsafe_wrap(Array, pointer(_antidiffusive_flux1_R),1333(n_variables, n_nodes + 1, n_nodes, capacity))13341335_antidiffusive_flux2_L = fill(nan_uEltype,1336n_variables * n_nodes * (n_nodes + 1) * capacity)1337antidiffusive_flux2_L = unsafe_wrap(Array, pointer(_antidiffusive_flux2_L),1338(n_variables, n_nodes, n_nodes + 1, capacity))1339_antidiffusive_flux2_R = fill(nan_uEltype,1340n_variables * n_nodes * (n_nodes + 1) * capacity)1341antidiffusive_flux2_R = unsafe_wrap(Array, pointer(_antidiffusive_flux2_R),1342(n_variables, n_nodes, n_nodes + 1, capacity))13431344reset_antidiffusive_fluxes!(antidiffusive_flux1_L, antidiffusive_flux1_R,1345antidiffusive_flux2_L, antidiffusive_flux2_R)13461347return ContainerAntidiffusiveFlux2D{uEltype}(antidiffusive_flux1_L,1348antidiffusive_flux1_R,1349antidiffusive_flux2_L,1350antidiffusive_flux2_R,1351_antidiffusive_flux1_L,1352_antidiffusive_flux1_R,1353_antidiffusive_flux2_L,1354_antidiffusive_flux2_R)1355end13561357# Only one-dimensional `Array`s are `resize!`able in Julia.1358# Hence, we use `Vector`s as internal storage and `resize!`1359# them whenever needed. Then, we reuse the same memory by1360# `unsafe_wrap`ping multi-dimensional `Array`s around the1361# internal storage.1362function Base.resize!(fluxes::ContainerAntidiffusiveFlux2D, capacity)1363n_nodes = nnodes(fluxes)1364n_variables = nvariables(fluxes)13651366@unpack _antidiffusive_flux1_L, _antidiffusive_flux2_L, _antidiffusive_flux1_R, _antidiffusive_flux2_R = fluxes13671368resize!(_antidiffusive_flux1_L, n_variables * (n_nodes + 1) * n_nodes * capacity)1369fluxes.antidiffusive_flux1_L = unsafe_wrap(Array, pointer(_antidiffusive_flux1_L),1370(n_variables, n_nodes + 1, n_nodes,1371capacity))1372resize!(_antidiffusive_flux1_R, n_variables * (n_nodes + 1) * n_nodes * capacity)1373fluxes.antidiffusive_flux1_R = unsafe_wrap(Array, pointer(_antidiffusive_flux1_R),1374(n_variables, n_nodes + 1, n_nodes,1375capacity))1376resize!(_antidiffusive_flux2_L, n_variables * n_nodes * (n_nodes + 1) * capacity)1377fluxes.antidiffusive_flux2_L = unsafe_wrap(Array, pointer(_antidiffusive_flux2_L),1378(n_variables, n_nodes, n_nodes + 1,1379capacity))1380resize!(_antidiffusive_flux2_R, n_variables * n_nodes * (n_nodes + 1) * capacity)1381fluxes.antidiffusive_flux2_R = unsafe_wrap(Array, pointer(_antidiffusive_flux2_R),1382(n_variables, n_nodes, n_nodes + 1,1383capacity))13841385return nothing1386end13871388function reset_antidiffusive_fluxes!(antidiffusive_flux1_L, antidiffusive_flux1_R,1389antidiffusive_flux2_L, antidiffusive_flux2_R)1390uEltype = eltype(antidiffusive_flux1_L)1391@threaded for element in axes(antidiffusive_flux1_L, 4)1392antidiffusive_flux1_L[:, 1, :, element] .= zero(uEltype)1393antidiffusive_flux1_L[:, end, :, element] .= zero(uEltype)1394antidiffusive_flux1_R[:, 1, :, element] .= zero(uEltype)1395antidiffusive_flux1_R[:, end, :, element] .= zero(uEltype)13961397antidiffusive_flux2_L[:, :, 1, element] .= zero(uEltype)1398antidiffusive_flux2_L[:, :, end, element] .= zero(uEltype)1399antidiffusive_flux2_R[:, :, 1, element] .= zero(uEltype)1400antidiffusive_flux2_R[:, :, end, element] .= zero(uEltype)1401end14021403return nothing1404end14051406function reinitialize_containers!(mesh::Union{TreeMesh{2}, TreeMesh{3}}, equations,1407dg::DGSEM, cache)1408# Get new list of leaf cells1409leaf_cell_ids = local_leaf_cells(mesh.tree)1410n_cells = length(leaf_cell_ids)14111412# re-initialize elements container1413@unpack elements = cache1414resize!(elements, n_cells)1415init_elements!(elements, leaf_cell_ids, mesh, dg.basis)14161417# Resize volume integral and related datastructures1418@unpack volume_integral = dg1419resize_volume_integral_cache!(cache, mesh, volume_integral, n_cells)1420reinit_volume_integral_cache!(cache, mesh, dg, volume_integral, n_cells)14211422# re-initialize interfaces container1423@unpack interfaces = cache1424resize!(interfaces, count_required_interfaces(mesh, leaf_cell_ids))1425init_interfaces!(interfaces, elements, mesh)14261427# re-initialize boundaries container1428@unpack boundaries = cache1429resize!(boundaries, count_required_boundaries(mesh, leaf_cell_ids))1430init_boundaries!(boundaries, elements, mesh, dg.basis)14311432# re-initialize mortars container1433@unpack mortars = cache1434resize!(mortars, count_required_mortars(mesh, leaf_cell_ids))1435init_mortars!(mortars, elements, mesh)14361437if mpi_isparallel() # currently only implemented for 2D1438# re-initialize mpi_interfaces container1439@unpack mpi_interfaces = cache1440resize!(mpi_interfaces, count_required_mpi_interfaces(mesh, leaf_cell_ids))1441init_mpi_interfaces!(mpi_interfaces, elements, mesh)14421443# re-initialize mpi_mortars container1444@unpack mpi_mortars = cache1445resize!(mpi_mortars, count_required_mpi_mortars(mesh, leaf_cell_ids))1446init_mpi_mortars!(mpi_mortars, elements, mesh)14471448# re-initialize mpi cache1449@unpack mpi_cache = cache1450init_mpi_cache!(mpi_cache, mesh, elements, mpi_interfaces, mpi_mortars,1451nvariables(equations), nnodes(dg), eltype(elements))1452end14531454return nothing1455end1456end # @muladd145714581459