Path: blob/main/src/solvers/dgsem_p4est/containers_2d.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: noindent67# Initialize data structures in element container8function init_elements!(elements,9mesh::Union{P4estMesh{2}, P4estMeshView{2}, T8codeMesh{2}},10basis::AbstractBasisSBP)11@unpack node_coordinates, jacobian_matrix,12contravariant_vectors, inverse_jacobian = elements1314calc_node_coordinates!(node_coordinates, mesh, basis)1516for element in 1:ncells(mesh)17calc_jacobian_matrix!(jacobian_matrix, element, node_coordinates, basis)1819calc_contravariant_vectors!(contravariant_vectors, element, jacobian_matrix)2021calc_inverse_jacobian!(inverse_jacobian, element, jacobian_matrix)22end2324return nothing25end2627# Interpolate tree_node_coordinates to each quadrant at the nodes of the specified basis28function calc_node_coordinates!(node_coordinates,29mesh::Union{P4estMesh{2}, P4estMeshView{2},30T8codeMesh{2}},31basis::AbstractBasisSBP)32# Hanging nodes will cause holes in the mesh if its polydeg is higher33# than the polydeg of the solver.34@assert length(basis.nodes)>=length(mesh.nodes) "The solver can't have a lower polydeg than the mesh"3536return calc_node_coordinates!(node_coordinates, mesh, basis.nodes)37end3839# Interpolate tree_node_coordinates to each quadrant at the specified nodes40function calc_node_coordinates!(node_coordinates,41mesh::P4estMesh{2, NDIMS_AMBIENT},42nodes::AbstractVector) where {NDIMS_AMBIENT}43# We use `StrideArray`s here since these buffers are used in performance-critical44# places and the additional information passed to the compiler makes them faster45# than native `Array`s.46tmp1 = StrideArray(undef, real(mesh),47StaticInt(NDIMS_AMBIENT), static_length(nodes),48static_length(mesh.nodes))49matrix1 = StrideArray(undef, real(mesh),50static_length(nodes), static_length(mesh.nodes))51matrix2 = similar(matrix1)52baryweights_in = barycentric_weights(mesh.nodes)5354# Macros from `p4est`55p4est_root_len = 1 << P4EST_MAXLEVEL56p4est_quadrant_len(l) = 1 << (P4EST_MAXLEVEL - l)5758trees = unsafe_wrap_sc(p4est_tree_t, mesh.p4est.trees)5960for tree in eachindex(trees)61offset = trees[tree].quadrants_offset62quadrants = unsafe_wrap_sc(p4est_quadrant_t, trees[tree].quadrants)6364for i in eachindex(quadrants)65element = offset + i66quad = quadrants[i]6768quad_length = p4est_quadrant_len(quad.level) / p4est_root_len6970nodes_out_x = 2 * (quad_length * 1 / 2 * (nodes .+ 1) .+71quad.x / p4est_root_len) .- 172nodes_out_y = 2 * (quad_length * 1 / 2 * (nodes .+ 1) .+73quad.y / p4est_root_len) .- 174polynomial_interpolation_matrix!(matrix1, mesh.nodes, nodes_out_x,75baryweights_in)76polynomial_interpolation_matrix!(matrix2, mesh.nodes, nodes_out_y,77baryweights_in)7879multiply_dimensionwise!(view(node_coordinates, :, :, :, element),80matrix1, matrix2,81view(mesh.tree_node_coordinates, :, :, :, tree),82tmp1)83end84end8586return node_coordinates87end8889# For Gauss-Lobatto-Legendre (GLL) nodes, interface normals are computed on-the-fly90# during flux evaluation and do not need dedicated storage in the interface container.91function init_normal_directions!(interfaces::P4estInterfaceContainer{2},92basis::LobattoLegendreBasis, elements)93return nothing94end9596# For Gauss-Legendre (GL) nodes, the interface normals are97# computed from interpolation of the volume node normals to the surface nodes.98function init_normal_directions!(interfaces::P4estInterfaceContainer{2},99basis::GaussLegendreBasis, elements)100@unpack neighbor_ids, node_indices, normal_directions = interfaces101@unpack contravariant_vectors = elements102@unpack boundary_interpolation = basis103index_range = eachnode(basis)104105for interface in axes(neighbor_ids, 2)106primary_element = neighbor_ids[1, interface]107primary_indices = node_indices[1, interface]108primary_direction = indices2direction(primary_indices)109110i_primary_start, i_primary_step = index_to_start_step_2d(primary_indices[1],111index_range)112j_primary_start, j_primary_step = index_to_start_step_2d(primary_indices[2],113index_range)114115# The index direction is identified based on `{i,j}_{primary, secondary}_step`.116# For step = 0, the direction identified by this index is normal to the face.117# For step != 0 (1 or -1), the direction identified by this index is tangential to the face.118119i_primary = i_primary_start120j_primary = j_primary_start121122if i_primary_step == 0123# i is the normal direction (constant), j varies along the surface124# => Interpolate in first/normal direction125# Interpolation side is governed by element orientation126side = interpolation_side(primary_indices[1])127for i in index_range128normal_1 = zero(eltype(normal_directions))129normal_2 = zero(eltype(normal_directions))130for ii in index_range131factor = boundary_interpolation[ii, side]132# Retrieve normal directions at element/volume nodes133normal_direction = get_normal_direction(primary_direction,134contravariant_vectors,135ii, j_primary,136primary_element)137normal_1 = normal_1 + normal_direction[1] * factor138normal_2 = normal_2 + normal_direction[2] * factor139end140normal_directions[1, i, interface] = normal_1141normal_directions[2, i, interface] = normal_2142j_primary += j_primary_step # incrementing j_primary suffices (i_primary_step = 0)143end144else # j_primary_step == 0145# j is the normal direction (constant), i varies along the surface146# => Interpolate in second/normal direction147# Interpolation side is governed by element orientation148side = interpolation_side(primary_indices[2])149for i in index_range150normal_1 = zero(eltype(normal_directions))151normal_2 = zero(eltype(normal_directions))152for jj in index_range153factor = boundary_interpolation[jj, side]154# Retrieve normal directions at element/volume nodes155normal_direction = get_normal_direction(primary_direction,156contravariant_vectors,157i_primary, jj,158primary_element)159normal_1 = normal_1 + normal_direction[1] * factor160normal_2 = normal_2 + normal_direction[2] * factor161end162normal_directions[1, i, interface] = normal_1163normal_directions[2, i, interface] = normal_2164i_primary += i_primary_step # incrementing i_primary suffices (j_primary_step = 0)165end166end167end168169return nothing170end171172# Initialize node_indices of interface container173@inline function init_interface_node_indices!(interfaces::P4estInterfaceContainer{2},174faces, orientation, interface_id)175# Iterate over primary and secondary element176for side in 1:2177# Align interface in positive coordinate direction of primary element.178# For orientation == 1, the secondary element needs to be indexed backwards179# relative to the interface.180if side == 1 || orientation == 0181# Forward indexing182i = :i_forward183else184# Backward indexing185i = :i_backward186end187188if faces[side] == 0189# Index face in negative x-direction190interfaces.node_indices[side, interface_id] = (:begin, i)191elseif faces[side] == 1192# Index face in positive x-direction193interfaces.node_indices[side, interface_id] = (:end, i)194elseif faces[side] == 2195# Index face in negative y-direction196interfaces.node_indices[side, interface_id] = (i, :begin)197else # faces[side] == 3198# Index face in positive y-direction199interfaces.node_indices[side, interface_id] = (i, :end)200end201end202203return interfaces204end205206# Initialize node_indices of boundary container207@inline function init_boundary_node_indices!(boundaries::P4estBoundaryContainer{2},208face, boundary_id)209if face == 0210# Index face in negative x-direction211boundaries.node_indices[boundary_id] = (:begin, :i_forward)212elseif face == 1213# Index face in positive x-direction214boundaries.node_indices[boundary_id] = (:end, :i_forward)215elseif face == 2216# Index face in negative y-direction217boundaries.node_indices[boundary_id] = (:i_forward, :begin)218else # face == 3219# Index face in positive y-direction220boundaries.node_indices[boundary_id] = (:i_forward, :end)221end222223return boundaries224end225226# Initialize node_indices of mortar container227# faces[1] is expected to be the face of the small side.228@inline function init_mortar_node_indices!(mortars, faces, orientation, mortar_id)229for side in 1:2230# Align mortar in positive coordinate direction of small side.231# For orientation == 1, the large side needs to be indexed backwards232# relative to the mortar.233if side == 1 || orientation == 0234# Forward indexing for small side or orientation == 0235i = :i_forward236else237# Backward indexing for large side with reversed orientation238i = :i_backward239end240241if faces[side] == 0242# Index face in negative x-direction243mortars.node_indices[side, mortar_id] = (:begin, i)244elseif faces[side] == 1245# Index face in positive x-direction246mortars.node_indices[side, mortar_id] = (:end, i)247elseif faces[side] == 2248# Index face in negative y-direction249mortars.node_indices[side, mortar_id] = (i, :begin)250else # faces[side] == 3251# Index face in positive y-direction252mortars.node_indices[side, mortar_id] = (i, :end)253end254end255256return mortars257end258end # @muladd259260261