Path: blob/main/src/solvers/dgsem_structured/containers_2d.jl
5590 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, mesh::Union{StructuredMesh{2}, StructuredMeshView{2}},9basis::LobattoLegendreBasis)10@unpack node_coordinates, left_neighbors,11jacobian_matrix, contravariant_vectors, inverse_jacobian = elements1213linear_indices = LinearIndices(size(mesh))1415# Calculate node coordinates, Jacobian matrix, and inverse Jacobian determinant16for cell_y in 1:size(mesh, 2), cell_x in 1:size(mesh, 1)17element = linear_indices[cell_x, cell_y]1819calc_node_coordinates!(node_coordinates, element, cell_x, cell_y, mesh.mapping,20mesh, basis)2122calc_jacobian_matrix!(jacobian_matrix, element, node_coordinates, basis)2324calc_contravariant_vectors!(contravariant_vectors, element, jacobian_matrix)2526calc_inverse_jacobian!(inverse_jacobian, element, jacobian_matrix)27end2829initialize_left_neighbor_connectivity!(left_neighbors, mesh, linear_indices)3031return nothing32end3334# Calculate physical coordinates to which every node of the reference element is mapped35# `mesh.mapping` is passed as an additional argument for type stability (function barrier)36function calc_node_coordinates!(node_coordinates, element,37cell_x, cell_y, mapping,38mesh::StructuredMesh{2},39basis::LobattoLegendreBasis)40@unpack nodes = basis4142# Get cell length in reference mesh43dx = 2 / size(mesh, 1)44dy = 2 / size(mesh, 2)4546# Calculate node coordinates of reference mesh47cell_x_offset = -1 + (cell_x - 1) * dx + dx / 248cell_y_offset = -1 + (cell_y - 1) * dy + dy / 24950for j in eachnode(basis), i in eachnode(basis)51# node_coordinates are the mapped reference node_coordinates52node_coordinates[:, i, j, element] .= mapping(cell_x_offset + dx / 2 * nodes[i],53cell_y_offset + dy / 2 * nodes[j])54end5556return nothing57end5859# Calculate Jacobian matrix of the mapping from the reference element to the element in the physical domain60function calc_jacobian_matrix!(jacobian_matrix, element,61node_coordinates::AbstractArray{<:Any, 4},62basis::AbstractBasisSBP)63@unpack derivative_matrix = basis6465# The code below is equivalent to the following matrix multiplications, which66# seem to end up calling generic linear algebra code from Julia. Thus, the67# optimized code below using `@turbo` is much faster.68# jacobian_matrix[1, 1, :, :, element] = derivative_matrix * node_coordinates[1, :, :, element] # x_ξ69# jacobian_matrix[2, 1, :, :, element] = derivative_matrix * node_coordinates[2, :, :, element] # y_ξ70# jacobian_matrix[1, 2, :, :, element] = node_coordinates[1, :, :, element] * derivative_matrix' # x_η71# jacobian_matrix[2, 2, :, :, element] = node_coordinates[2, :, :, element] * derivative_matrix' # y_η7273# x_ξ, y_ξ74@turbo for xy in indices((jacobian_matrix, node_coordinates), (1, 1))75for j in indices((jacobian_matrix, node_coordinates), (4, 3)),76i in indices((jacobian_matrix, derivative_matrix), (3, 1))7778result = zero(eltype(jacobian_matrix))79for ii in indices((node_coordinates, derivative_matrix), (2, 2))80result += derivative_matrix[i, ii] *81node_coordinates[xy, ii, j, element]82end83jacobian_matrix[xy, 1, i, j, element] = result84end85end8687# x_η, y_η88@turbo for xy in indices((jacobian_matrix, node_coordinates), (1, 1))89for j in indices((jacobian_matrix, derivative_matrix), (4, 1)),90i in indices((jacobian_matrix, node_coordinates), (3, 2))9192result = zero(eltype(jacobian_matrix))93for jj in indices((node_coordinates, derivative_matrix), (3, 2))94result += derivative_matrix[j, jj] *95node_coordinates[xy, i, jj, element]96end97jacobian_matrix[xy, 2, i, j, element] = result98end99end100101return jacobian_matrix102end103104# Calculate contravariant vectors, multiplied by the Jacobian determinant J of the transformation mapping.105# Those are called Ja^i in Kopriva's blue book.106function calc_contravariant_vectors!(contravariant_vectors::AbstractArray{<:Any, 5},107element, jacobian_matrix)108# The code below is equivalent to the following using broadcasting but much faster.109# # First contravariant vector Ja^1110# contravariant_vectors[1, 1, :, :, element] = jacobian_matrix[2, 2, :, :, element]111# contravariant_vectors[2, 1, :, :, element] = -jacobian_matrix[1, 2, :, :, element]112# # Second contravariant vector Ja^2113# contravariant_vectors[1, 2, :, :, element] = -jacobian_matrix[2, 1, :, :, element]114# contravariant_vectors[2, 2, :, :, element] = jacobian_matrix[1, 1, :, :, element]115116@turbo for j in indices((contravariant_vectors, jacobian_matrix), (4, 4)),117i in indices((contravariant_vectors, jacobian_matrix), (3, 3))118# First contravariant vector Ja^1119contravariant_vectors[1, 1, i, j, element] = jacobian_matrix[2, 2, i, j,120element]121contravariant_vectors[2, 1, i, j, element] = -jacobian_matrix[1, 2, i, j,122element]123124# Second contravariant vector Ja^2125contravariant_vectors[1, 2, i, j, element] = -jacobian_matrix[2, 1, i, j,126element]127contravariant_vectors[2, 2, i, j, element] = jacobian_matrix[1, 1, i, j,128element]129end130131return contravariant_vectors132end133134# Calculate inverse Jacobian (determinant of Jacobian matrix of the mapping) in each node135function calc_inverse_jacobian!(inverse_jacobian::AbstractArray{<:Any, 3}, element,136jacobian_matrix)137# The code below is equivalent to the following high-level code but much faster.138# inverse_jacobian[i, j, element] = inv(det(jacobian_matrix[:, :, i, j, element])139140@turbo for j in indices((inverse_jacobian, jacobian_matrix), (2, 4)),141i in indices((inverse_jacobian, jacobian_matrix), (1, 3))142143inverse_jacobian[i, j, element] = inv(jacobian_matrix[1, 1, i, j, element] *144jacobian_matrix[2, 2, i, j, element] -145jacobian_matrix[1, 2, i, j, element] *146jacobian_matrix[2, 1, i, j, element])147end148149return inverse_jacobian150end151152# Save id of left neighbor of every element153function initialize_left_neighbor_connectivity!(left_neighbors,154mesh::Union{StructuredMesh{2},155StructuredMeshView{2}},156linear_indices)157# Neighbors in x-direction158for cell_y in 1:size(mesh, 2)159# Inner elements160for cell_x in 2:size(mesh, 1)161element = linear_indices[cell_x, cell_y]162left_neighbors[1, element] = linear_indices[cell_x - 1, cell_y]163end164165if isperiodic(mesh, 1)166# Periodic boundary167left_neighbors[1, linear_indices[1, cell_y]] = linear_indices[end, cell_y]168else169# Use boundary conditions170left_neighbors[1, linear_indices[1, cell_y]] = 0171end172end173174# Neighbors in y-direction175for cell_x in 1:size(mesh, 1)176# Inner elements177for cell_y in 2:size(mesh, 2)178element = linear_indices[cell_x, cell_y]179left_neighbors[2, element] = linear_indices[cell_x, cell_y - 1]180end181182if isperiodic(mesh, 2)183# Periodic boundary184left_neighbors[2, linear_indices[cell_x, 1]] = linear_indices[cell_x, end]185else186# Use boundary conditions187left_neighbors[2, linear_indices[cell_x, 1]] = 0188end189end190191return left_neighbors192end193194# Compute the normal vectors for freestream-preserving FV method on curvilinear subcells, see195# equations (14) and (B.53) in:196# - Hennemann, Rueda-Ramírez, Hindenlang, Gassner (2020)197# A provably entropy stable subcell shock capturing approach for high order split form DG for the compressible Euler equations198# [arXiv: 2008.12044v2](https://arxiv.org/pdf/2008.12044)199function calc_normalvectors_subcell_fv!(normal_vectors_1, normal_vectors_2,200mesh::Union{StructuredMesh{2},201UnstructuredMesh2D,202P4estMesh{2}, T8codeMesh{2}},203dg, cache_containers)204@unpack contravariant_vectors = cache_containers.elements205@unpack weights, derivative_matrix = dg.basis206207@threaded for element in eachelement(dg, cache_containers)208# First contravariant vector/direction209for j in eachnode(dg)210# We do not store i = 1, as it is never used, see `calcflux_fv!`.211# => Store i = 2 at position 1212@views normal_vectors_1[:, 1, j, element] .= get_contravariant_vector(1,213contravariant_vectors,2141,215j,216element)217for m in eachnode(dg)218wD_im = weights[1] * derivative_matrix[1, m]219@views normal_vectors_1[:, 1, j, element] .+= wD_im *220get_contravariant_vector(1,221contravariant_vectors,222m,223j,224element)225end226227for i in 2:(nnodes(dg) - 1) # Actual indices: 3 to nnodes(dg)228@views normal_vectors_1[:, i, j, element] .= normal_vectors_1[:,229i - 1,230j,231element]232for m in eachnode(dg)233wD_im = weights[i] * derivative_matrix[i, m]234@views normal_vectors_1[:, i, j, element] .+= wD_im *235get_contravariant_vector(1,236contravariant_vectors,237m,238j,239element)240end241end242end243244# Second contravariant vector/direction245for i in eachnode(dg)246# We do not store j = 1, as it is never used.247# => Store physical j = 2 at position 1248@views normal_vectors_2[:, i, 1, element] .= get_contravariant_vector(2,249contravariant_vectors,250i,2511,252element)253for m in eachnode(dg)254wD_jm = weights[1] * derivative_matrix[1, m]255@views normal_vectors_2[:, i, 1, element] .+= wD_jm *256get_contravariant_vector(2,257contravariant_vectors,258i,259m,260element)261end262263for j in 2:(nnodes(dg) - 1) # Actual indices: 3 to nnodes(dg)264@views normal_vectors_2[:, i, j, element] .= normal_vectors_2[:,265i,266j - 1,267element]268269for m in eachnode(dg)270wD_jm = weights[j] * derivative_matrix[j, m]271@views normal_vectors_2[:, i, j, element] .+= wD_jm *272get_contravariant_vector(2,273contravariant_vectors,274i,275m,276element)277end278end279end280end281282return nothing283end284285# Used for both fixed (`StructuredMesh{2}` or `UnstructuredMesh2D`)286# and adaptive meshes (`P4estMesh{2}` or `T8codeMesh{2}`)287mutable struct NormalVectorContainer2D{RealT <: Real} <:288AbstractNormalVectorContainer289const n_nodes::Int290# For normal vectors computed from first contravariant vectors291normal_vectors_1::Array{RealT, 4} # [NDIMS, NNODES - 1, NNODES, NELEMENTS]292# For normal vectors computed from second contravariant vectors293normal_vectors_2::Array{RealT, 4} # [NDIMS, NNODES, NNODES - 1, NELEMENTS]294295# internal `resize!`able storage296_normal_vectors_1::Vector{RealT}297_normal_vectors_2::Vector{RealT}298end299300function NormalVectorContainer2D(mesh::Union{StructuredMesh{2}, UnstructuredMesh2D,301P4estMesh{2}, T8codeMesh{2}},302dg, cache_containers)303@unpack contravariant_vectors = cache_containers.elements304RealT = eltype(contravariant_vectors)305n_elements = nelements(dg, cache_containers)306n_nodes = nnodes(dg.basis)307308_normal_vectors_1 = Vector{RealT}(undef, 2 * (n_nodes - 1) * n_nodes * n_elements)309normal_vectors_1 = unsafe_wrap(Array, pointer(_normal_vectors_1),310(2, n_nodes - 1, n_nodes,311n_elements))312313_normal_vectors_2 = Vector{RealT}(undef, 2 * n_nodes * (n_nodes - 1) * n_elements)314normal_vectors_2 = unsafe_wrap(Array, pointer(_normal_vectors_2),315(2, n_nodes, n_nodes - 1,316n_elements))317318calc_normalvectors_subcell_fv!(normal_vectors_1, normal_vectors_2,319mesh, dg, cache_containers)320321return NormalVectorContainer2D{RealT}(n_nodes,322normal_vectors_1, normal_vectors_2,323_normal_vectors_1, _normal_vectors_2)324end325326# For `TreeMesh`, no subcell normal vectors need to be computed327resize_normal_vectors!(cache, mesh::TreeMesh, capacity) = nothing328# It suffices to specialize on the non-Cartesian mesh types with AMR only329function resize_normal_vectors!(cache, mesh::Union{P4estMesh, T8codeMesh}, capacity)330resize!(cache.normal_vectors, capacity)331332return nothing333end334335# For `TreeMesh`, no subcell normal vectors need to be computed336reinit_normal_vectors!(cache, mesh::TreeMesh, dg) = nothing337# It suffices to specialize on the non-Cartesian mesh types with AMR only338function reinit_normal_vectors!(cache, mesh::Union{P4estMesh, T8codeMesh}, dg)339init_normal_vectors!(cache.normal_vectors, mesh, dg, cache)340341return nothing342end343344# Required only for adaptive meshes (`P4estMesh` or `T8codeMesh`)345function Base.resize!(normal_vectors::NormalVectorContainer2D, capacity)346@unpack n_nodes, _normal_vectors_1, _normal_vectors_2 = normal_vectors347ArrayType = storage_type(normal_vectors)348349resize!(_normal_vectors_1, 2 * (n_nodes - 1) * n_nodes * capacity)350normal_vectors.normal_vectors_1 = unsafe_wrap_or_alloc(ArrayType, _normal_vectors_1,351(2,352n_nodes - 1, n_nodes,353capacity))354355resize!(_normal_vectors_2, 2 * n_nodes * (n_nodes - 1) * capacity)356normal_vectors.normal_vectors_2 = unsafe_wrap_or_alloc(ArrayType, _normal_vectors_2,357(2,358n_nodes, n_nodes - 1,359capacity))360361return nothing362end363364# Required only for adaptive meshes (`P4estMesh` or `T8codeMesh`)365function init_normal_vectors!(normal_vectors::NormalVectorContainer2D,366mesh::Union{P4estMesh{2}, T8codeMesh{2}}, dg, cache)367@unpack normal_vectors_1, normal_vectors_2 = normal_vectors368calc_normalvectors_subcell_fv!(normal_vectors_1, normal_vectors_2,369mesh, dg, cache)370371return nothing372end373end # @muladd374375376