Path: blob/main/src/solvers/dgsem_tree/containers_1d.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 TreeElementContainer1D{RealT <: Real, uEltype <: Real} <:9AbstractTreeElementContainer10inverse_jacobian::Vector{RealT} # [elements]11node_coordinates::Array{RealT, 3} # [orientation, i, elements]12surface_flux_values::Array{uEltype, 3} # [variables, 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::TreeElementContainer1D, 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, 1 * n_nodes * capacity)33elements.node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates),34(1, n_nodes, capacity))3536resize!(_surface_flux_values, n_variables * 2 * 1 * capacity)37elements.surface_flux_values = unsafe_wrap(Array, pointer(_surface_flux_values),38(n_variables, 2 * 1, capacity))3940resize!(cell_ids, capacity)4142return nothing43end4445function TreeElementContainer1D{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, 1 * n_nodes * capacity)55node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates),56(1, n_nodes, capacity))5758_surface_flux_values = fill(nan_uEltype, n_variables * 2 * 1 * capacity)59surface_flux_values = unsafe_wrap(Array, pointer(_surface_flux_values),60(n_variables, 2 * 1, capacity))6162cell_ids = fill(typemin(Int), capacity)6364return TreeElementContainer1D{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::TreeMesh1D,72equations::AbstractEquations{1},73basis, ::Type{RealT},74::Type{uEltype}) where {RealT <: Real, uEltype <: Real}75# Initialize container76n_elements = length(cell_ids)77elements = TreeElementContainer1D{RealT, uEltype}(n_elements, nvariables(equations),78nnodes(basis))7980init_elements!(elements, cell_ids, mesh, basis)81return elements82end8384function init_elements!(elements, cell_ids, mesh::TreeMesh1D, 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 i in eachnode(basis)114elements.node_coordinates[1, i, element] = (mesh.tree.coordinates[1,115cell_id] +116jacobian *117(nodes[i] - reference_offset))118end119end120121return elements122end123124# Container data structure (structure-of-arrays style) for DG interfaces125mutable struct TreeInterfaceContainer1D{uEltype <: Real} <:126AbstractTreeInterfaceContainer127u::Array{uEltype, 3} # [leftright, variables, interfaces]128neighbor_ids::Matrix{Int} # [leftright, interfaces]129orientations::Vector{Int} # [interfaces]130# internal `resize!`able storage131_u::Vector{uEltype}132_neighbor_ids::Vector{Int}133end134135# 1D: Only one node per interface136nnodes(interfaces::TreeInterfaceContainer1D) = 1137138# See explanation of Base.resize! for the element container139function Base.resize!(interfaces::TreeInterfaceContainer1D, capacity)140n_variables = nvariables(interfaces)141@unpack _u, _neighbor_ids, orientations = interfaces142143resize!(_u, 2 * n_variables * capacity)144interfaces.u = unsafe_wrap(Array, pointer(_u),145(2, n_variables, capacity))146147resize!(_neighbor_ids, 2 * capacity)148interfaces.neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids),149(2, capacity))150151resize!(orientations, capacity)152153return nothing154end155156function TreeInterfaceContainer1D{uEltype}(capacity::Integer,157n_variables) where {uEltype <: Real}158nan = convert(uEltype, NaN)159160# Initialize fields with defaults161_u = fill(nan, 2 * n_variables * capacity)162u = unsafe_wrap(Array, pointer(_u),163(2, n_variables, capacity))164165_neighbor_ids = fill(typemin(Int), 2 * capacity)166neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids),167(2, capacity))168169orientations = fill(typemin(Int), capacity)170171return TreeInterfaceContainer1D{uEltype}(u, neighbor_ids, orientations,172_u, _neighbor_ids)173end174175# Create interface container and initialize interface data in `elements`.176function init_interfaces(cell_ids, mesh::TreeMesh1D,177elements::TreeElementContainer1D)178# Initialize container179n_interfaces = count_required_interfaces(mesh, cell_ids)180interfaces = TreeInterfaceContainer1D{eltype(elements)}(n_interfaces,181nvariables(elements))182183# Connect elements with interfaces184init_interfaces!(interfaces, elements, mesh)185return interfaces186end187188# Count the number of interfaces that need to be created189function count_required_interfaces(mesh::TreeMesh1D, cell_ids)190count = 0191192# Iterate over all cells193for cell_id in cell_ids194for direction in eachdirection(mesh.tree)195# Only count interfaces in positive direction to avoid double counting196if direction == 1197continue198end199200# Skip if no neighbor exists201if !has_any_neighbor(mesh.tree, cell_id, direction)202continue203end204205count += 1206end207end208209return count210end211212# Initialize connectivity between elements and interfaces213function init_interfaces!(interfaces, elements, mesh::TreeMesh1D)214# Construct cell -> element mapping for easier algorithm implementation215tree = mesh.tree216c2e = zeros(Int, length(tree))217for element in eachelement(elements)218c2e[elements.cell_ids[element]] = element219end220221# Reset interface count222count = 0223224# Iterate over all elements to find neighbors and to connect via interfaces225for element in eachelement(elements)226# Get cell id227cell_id = elements.cell_ids[element]228229# Loop over directions230for direction in eachdirection(mesh.tree)231# Only create interfaces in positive direction232if direction == 1233continue234end235236# Skip if no neighbor exists and current cell is not small237if !has_any_neighbor(mesh.tree, cell_id, direction)238continue239end240241count += 1242243if has_neighbor(mesh.tree, cell_id, direction)244neighbor_cell_id = mesh.tree.neighbor_ids[direction, cell_id]245if has_children(mesh.tree, neighbor_cell_id) # Cell has small neighbor246interfaces.neighbor_ids[2, count] = c2e[mesh.tree.child_ids[1,247neighbor_cell_id]]248else # Cell has same refinement level neighbor249interfaces.neighbor_ids[2, count] = c2e[neighbor_cell_id]250end251else # Cell is small and has large neighbor252parent_id = mesh.tree.parent_ids[cell_id]253neighbor_cell_id = mesh.tree.neighbor_ids[direction, parent_id]254interfaces.neighbor_ids[2, count] = c2e[neighbor_cell_id]255end256257interfaces.neighbor_ids[1, count] = element258# Set orientation (x -> 1)259interfaces.orientations[count] = 1260end261end262263@assert count==ninterfaces(interfaces) ("Actual interface count ($count) does not match "*264"expectations $(ninterfaces(interfaces))")265end266267# Container data structure (structure-of-arrays style) for DG boundaries268mutable struct TreeBoundaryContainer1D{RealT <: Real, uEltype <: Real} <:269AbstractTreeBoundaryContainer270u::Array{uEltype, 3} # [leftright, variables, boundaries]271neighbor_ids::Vector{Int} # [boundaries]272orientations::Vector{Int} # [boundaries]273neighbor_sides::Vector{Int} # [boundaries]274node_coordinates::Array{RealT, 2} # [orientation, elements]275n_boundaries_per_direction::SVector{2, Int} # [direction]276# internal `resize!`able storage277_u::Vector{uEltype}278_node_coordinates::Vector{RealT}279end280281# 1D: Only one boundary node282nnodes(boundaries::TreeBoundaryContainer1D) = 1283284# See explanation of Base.resize! for the element container285function Base.resize!(boundaries::TreeBoundaryContainer1D, capacity)286n_variables = nvariables(boundaries)287@unpack _u, _node_coordinates,288neighbor_ids, orientations, neighbor_sides = boundaries289290resize!(_u, 2 * n_variables * capacity)291boundaries.u = unsafe_wrap(Array, pointer(_u),292(2, n_variables, capacity))293294resize!(_node_coordinates, 1 * capacity)295boundaries.node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates),296(1, capacity))297298resize!(neighbor_ids, capacity)299300resize!(orientations, capacity)301302resize!(neighbor_sides, capacity)303304return nothing305end306307function TreeBoundaryContainer1D{RealT, uEltype}(capacity::Integer,308n_variables) where {RealT <: Real,309uEltype <: Real}310nan_RealT = convert(RealT, NaN)311nan_uEltype = convert(uEltype, NaN)312313# Initialize fields with defaults314_u = fill(nan_uEltype, 2 * n_variables * capacity)315u = unsafe_wrap(Array, pointer(_u),316(2, n_variables, capacity))317318neighbor_ids = fill(typemin(Int), capacity)319320orientations = fill(typemin(Int), capacity)321322neighbor_sides = fill(typemin(Int), capacity)323324_node_coordinates = fill(nan_RealT, 1 * capacity)325node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates),326(1, capacity))327328n_boundaries_per_direction = SVector(0, 0)329330return TreeBoundaryContainer1D{RealT, uEltype}(u, neighbor_ids, orientations,331neighbor_sides,332node_coordinates,333n_boundaries_per_direction,334_u, _node_coordinates)335end336337# Create boundaries container and initialize boundary data in `elements`.338function init_boundaries(cell_ids, mesh::TreeMesh1D,339elements::TreeElementContainer1D, basis)340# Initialize container341n_boundaries = count_required_boundaries(mesh, cell_ids)342boundaries = TreeBoundaryContainer1D{real(elements), eltype(elements)}(n_boundaries,343nvariables(elements))344345# Connect elements with boundaries346init_boundaries!(boundaries, elements, mesh, basis)347return boundaries348end349350# Count the number of boundaries that need to be created351function count_required_boundaries(mesh::TreeMesh1D, cell_ids)352count = 0353354# Iterate over all cells355for cell_id in cell_ids356for direction in eachdirection(mesh.tree)357# If neighbor exists, current cell is not at a boundary358if has_neighbor(mesh.tree, cell_id, direction)359continue360end361362# If coarse neighbor exists, current cell is not at a boundary363if has_coarse_neighbor(mesh.tree, cell_id, direction)364continue365end366367# No neighbor exists in this direction -> must be a boundary368count += 1369end370end371372return count373end374375# For Lobtto points, we can simply use the outer nodes of the elements as boundary nodes.376function calc_boundary_node_coordinates!(boundaries, element, count, direction,377elements, mesh::TreeMesh1D,378basis::LobattoLegendreBasis)379el_node_coords = elements.node_coordinates380bnd_node_coords = boundaries.node_coordinates381382orientation = 1 # always 1 in 1D383if direction == 1384bnd_node_coords[orientation, count] = el_node_coords[orientation, 1,385element]386elseif direction == 2387bnd_node_coords[orientation, count] = el_node_coords[orientation, end,388element]389else390error("should not happen")391end392393return nothing394end395396# For Gauss points, we need to interpolate the boundary node coordinates.397function calc_boundary_node_coordinates!(boundaries, element, count, direction,398elements, mesh::TreeMesh1D,399basis::GaussLegendreBasis)400boundary_matrix = basis.boundary_interpolation401el_node_coords = elements.node_coordinates402bnd_node_coords = boundaries.node_coordinates403404orientation = 1 # always 1 in 1D405if direction == 1406@views x_interpolated_left = dot(boundary_matrix[:, 1],407el_node_coords[orientation, :,408element])409bnd_node_coords[orientation, count] = x_interpolated_left410elseif direction == 2411@views x_interpolated_right = dot(boundary_matrix[:, 2],412el_node_coords[orientation, :,413element])414bnd_node_coords[orientation, count] = x_interpolated_right415else416error("should not happen")417end418419return nothing420end421422# Initialize connectivity between elements and boundaries423function init_boundaries!(boundaries, elements, mesh::TreeMesh1D, basis)424# Reset boundaries count425count = 0426427# Initialize boundary counts428counts_per_direction = MVector(0, 0)429430# OBS! Iterate over directions first, then over elements, and count boundaries in each direction431# Rationale: This way the boundaries are internally sorted by the directions -x, +x, -y etc.,432# obviating the need to store the boundary condition to be applied explicitly.433# Loop over directions434for direction in eachdirection(mesh.tree)435# Iterate over all elements to find missing neighbors and to connect to boundaries436for element in eachelement(elements)437# Get cell id438cell_id = elements.cell_ids[element]439440# If neighbor exists, current cell is not at a boundary441if has_neighbor(mesh.tree, cell_id, direction)442continue443end444445# If coarse neighbor exists, current cell is not at a boundary446if has_coarse_neighbor(mesh.tree, cell_id, direction)447continue448end449450# Create boundary451count += 1452counts_per_direction[direction] += 1453454# Set neighbor element id455boundaries.neighbor_ids[count] = element456457# Set neighbor side, which denotes the direction (1 -> negative, 2 -> positive) of the element458if direction == 2459boundaries.neighbor_sides[count] = 1460else461boundaries.neighbor_sides[count] = 2462end463464# Set orientation (x -> 1)465boundaries.orientations[count] = 1466467# Calculate node coordinates468calc_boundary_node_coordinates!(boundaries, element, count, direction,469elements, mesh, basis)470end471end472473@assert count==nboundaries(boundaries) ("Actual boundaries count ($count) does not match "*474"expectations $(nboundaries(boundaries))")475@assert sum(counts_per_direction) == count476477boundaries.n_boundaries_per_direction = SVector(counts_per_direction)478479return boundaries.n_boundaries_per_direction480end481482function reinitialize_containers!(mesh::TreeMesh{1}, equations, dg::DGSEM, cache)483# Get new list of leaf cells484leaf_cell_ids = local_leaf_cells(mesh.tree)485n_cells = length(leaf_cell_ids)486487# re-initialize elements container488@unpack elements = cache489resize!(elements, n_cells)490init_elements!(elements, leaf_cell_ids, mesh, dg.basis)491492# Resize volume integral and related datastructures493@unpack volume_integral = dg494resize_volume_integral_cache!(cache, mesh, volume_integral, n_cells)495reinit_volume_integral_cache!(cache, mesh, dg, volume_integral, n_cells)496497# re-initialize interfaces container498@unpack interfaces = cache499resize!(interfaces, count_required_interfaces(mesh, leaf_cell_ids))500init_interfaces!(interfaces, elements, mesh)501502# re-initialize boundaries container503@unpack boundaries = cache504resize!(boundaries, count_required_boundaries(mesh, leaf_cell_ids))505init_boundaries!(boundaries, elements, mesh, dg.basis)506507return nothing508end509end # @muladd510511512