Path: blob/main/src/solvers/dgsem_unstructured/dg_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# This method is called when a SemidiscretizationHyperbolic is constructed.8# It constructs the basic `cache` used throughout the simulation to compute9# the RHS etc.10function create_cache(mesh::UnstructuredMesh2D, equations,11dg::DG, RealT, uEltype)12elements = init_elements(mesh, equations, dg.basis, RealT, uEltype)1314interfaces = init_interfaces(mesh, elements)1516boundaries = init_boundaries(mesh, elements)1718# Container cache19cache = (; elements, interfaces, boundaries)2021# perform a check on the sufficient metric identities condition for free-stream preservation22# and halt computation if it fails23# For `Float64`, this gives 1.8189894035458565e-1224# For `Float32`, this gives 1.1920929f-525atol = max(100 * eps(RealT), eps(RealT)^convert(RealT, 0.75f0))26if !isapprox(max_discrete_metric_identities(dg, cache), 0, atol = atol)27error("metric terms fail free-stream preservation check with maximum error $(max_discrete_metric_identities(dg, cache))")28end2930# Add Volume-Integral cache31cache = (; cache...,32create_cache(mesh, equations, dg.volume_integral, dg, cache, uEltype)...)3334return cache35end3637# prolong the solution into the convenience array in the interior interface container38# Note! this routine is for quadrilateral elements with "right-handed" orientation39function prolong2interfaces!(cache, u, mesh::UnstructuredMesh2D, equations, dg::DG)40@unpack interfaces = cache41@unpack element_ids, element_side_ids = interfaces42interfaces_u = interfaces.u4344@threaded for interface in eachinterface(dg, cache)45primary_element = element_ids[1, interface]46secondary_element = element_ids[2, interface]4748primary_side = element_side_ids[1, interface]49secondary_side = element_side_ids[2, interface]5051if primary_side == 152for i in eachnode(dg), v in eachvariable(equations)53interfaces_u[1, v, i, interface] = u[v, i, 1,54primary_element]55end56elseif primary_side == 257for i in eachnode(dg), v in eachvariable(equations)58interfaces_u[1, v, i, interface] = u[v, nnodes(dg), i,59primary_element]60end61elseif primary_side == 362for i in eachnode(dg), v in eachvariable(equations)63interfaces_u[1, v, i, interface] = u[v, i, nnodes(dg),64primary_element]65end66else # primary_side == 467for i in eachnode(dg), v in eachvariable(equations)68interfaces_u[1, v, i, interface] = u[v, 1, i,69primary_element]70end71end7273if secondary_side == 174for i in eachnode(dg), v in eachvariable(equations)75interfaces_u[2, v, i, interface] = u[v, i, 1,76secondary_element]77end78elseif secondary_side == 279for i in eachnode(dg), v in eachvariable(equations)80interfaces_u[2, v, i, interface] = u[v, nnodes(dg), i,81secondary_element]82end83elseif secondary_side == 384for i in eachnode(dg), v in eachvariable(equations)85interfaces_u[2, v, i, interface] = u[v, i, nnodes(dg),86secondary_element]87end88else # secondary_side == 489for i in eachnode(dg), v in eachvariable(equations)90interfaces_u[2, v, i, interface] = u[v, 1, i,91secondary_element]92end93end94end9596return nothing97end9899# compute the numerical flux interface coupling between two elements on an unstructured100# quadrilateral mesh101function calc_interface_flux!(surface_flux_values,102mesh::UnstructuredMesh2D,103have_nonconservative_terms::False, equations,104surface_integral, dg::DG, cache)105@unpack surface_flux = surface_integral106@unpack u, start_index, index_increment, element_ids, element_side_ids = cache.interfaces107@unpack normal_directions = cache.elements108109@threaded for interface in eachinterface(dg, cache)110# Get neighboring elements111primary_element = element_ids[1, interface]112secondary_element = element_ids[2, interface]113114# Get the local side id on which to compute the flux115primary_side = element_side_ids[1, interface]116secondary_side = element_side_ids[2, interface]117118# initial index for the coordinate system on the secondary element119secondary_index = start_index[interface]120121# loop through the primary element coordinate system and compute the interface coupling122for primary_index in eachnode(dg)123# pull the primary and secondary states from the boundary u values124u_ll = get_one_sided_surface_node_vars(u, equations, dg, 1, primary_index,125interface)126u_rr = get_one_sided_surface_node_vars(u, equations, dg, 2, secondary_index,127interface)128129# pull the outward pointing (normal) directional vector130# Note! this assumes a conforming approximation, more must be done in terms of the normals131# for hanging nodes and other non-conforming approximation spaces132outward_direction = get_surface_normal(normal_directions, primary_index,133primary_side, primary_element)134135# Call pointwise numerical flux with rotation. Direction is normalized inside this function136flux = surface_flux(u_ll, u_rr, outward_direction, equations)137138# Copy flux back to primary/secondary element storage139# Note the sign change for the normal flux in the secondary element!140for v in eachvariable(equations)141surface_flux_values[v, primary_index, primary_side, primary_element] = flux[v]142surface_flux_values[v, secondary_index, secondary_side, secondary_element] = -flux[v]143end144145# increment the index of the coordinate system in the secondary element146secondary_index += index_increment[interface]147end148end149150return nothing151end152153# compute the numerical flux interface with nonconservative terms coupling between two elements154# on an unstructured quadrilateral mesh155function calc_interface_flux!(surface_flux_values,156mesh::UnstructuredMesh2D,157have_nonconservative_terms::True, equations,158surface_integral, dg::DG, cache)159surface_flux, nonconservative_flux = surface_integral.surface_flux160@unpack u, start_index, index_increment, element_ids, element_side_ids = cache.interfaces161@unpack normal_directions = cache.elements162163@threaded for interface in eachinterface(dg, cache)164# Get the primary element index and local side index165primary_element = element_ids[1, interface]166primary_side = element_side_ids[1, interface]167168# Get neighboring element, local side index, and index increment on the169# secondary element170secondary_element = element_ids[2, interface]171secondary_side = element_side_ids[2, interface]172secondary_index_increment = index_increment[interface]173174secondary_index = start_index[interface]175for primary_index in eachnode(dg)176# pull the primary and secondary states from the boundary u values177u_ll = get_one_sided_surface_node_vars(u, equations, dg, 1, primary_index,178interface)179u_rr = get_one_sided_surface_node_vars(u, equations, dg, 2, secondary_index,180interface)181182# pull the outward pointing (normal) directional vector183# Note! This assumes a conforming approximation, more must be done in terms184# of the normals for hanging nodes and other non-conforming approximation spaces185outward_direction = get_surface_normal(normal_directions, primary_index,186primary_side, primary_element)187188# Calculate the conservative portion of the numerical flux189# Call pointwise numerical flux with rotation. Direction is normalized190# inside this function191flux = surface_flux(u_ll, u_rr, outward_direction, equations)192193# Compute both nonconservative fluxes194noncons_primary = nonconservative_flux(u_ll, u_rr, outward_direction,195equations)196noncons_secondary = nonconservative_flux(u_rr, u_ll, outward_direction,197equations)198199# Copy flux to primary and secondary element storage200# Note the sign change for the components in the secondary element!201for v in eachvariable(equations)202# Note the factor 0.5 necessary for the nonconservative fluxes based on203# the interpretation of global SBP operators coupled discontinuously via204# central fluxes/SATs205surface_flux_values[v, primary_index, primary_side, primary_element] = (flux[v] +2060.5f0 *207noncons_primary[v])208surface_flux_values[v, secondary_index, secondary_side, secondary_element] = -(flux[v] +2090.5f0 *210noncons_secondary[v])211end212213# increment the index of the coordinate system in the secondary element214secondary_index += secondary_index_increment215end216end217218return nothing219end220221# move the approximate solution onto physical boundaries within a "right-handed" element222function prolong2boundaries!(cache, u,223mesh::UnstructuredMesh2D,224equations, dg::DG)225@unpack boundaries = cache226@unpack element_id, element_side_id = boundaries227boundaries_u = boundaries.u228229@threaded for boundary in eachboundary(dg, cache)230element = element_id[boundary]231side = element_side_id[boundary]232233if side == 1234for l in eachnode(dg), v in eachvariable(equations)235boundaries_u[v, l, boundary] = u[v, l, 1, element]236end237elseif side == 2238for l in eachnode(dg), v in eachvariable(equations)239boundaries_u[v, l, boundary] = u[v, nnodes(dg), l, element]240end241elseif side == 3242for l in eachnode(dg), v in eachvariable(equations)243boundaries_u[v, l, boundary] = u[v, l, nnodes(dg), element]244end245else # side == 4246for l in eachnode(dg), v in eachvariable(equations)247boundaries_u[v, l, boundary] = u[v, 1, l, element]248end249end250end251252return nothing253end254255function calc_boundary_flux!(cache, t, boundary_condition::BoundaryConditionPeriodic,256mesh::Union{UnstructuredMesh2D, P4estMesh, T8codeMesh},257equations, surface_integral, dg::DG)258@assert isempty(eachboundary(dg, cache))259260return nothing261end262263# Function barrier for type stability264function calc_boundary_flux!(cache, t, boundary_conditions,265mesh::Union{UnstructuredMesh2D, P4estMesh, T8codeMesh},266equations, surface_integral, dg::DG)267@unpack boundary_condition_types, boundary_indices = boundary_conditions268269calc_boundary_flux_by_type!(cache, t, boundary_condition_types, boundary_indices,270mesh, equations, surface_integral, dg)271return nothing272end273274# Iterate over tuples of boundary condition types and associated indices275# in a type-stable way using "lispy tuple programming".276function calc_boundary_flux_by_type!(cache, t, BCs::NTuple{N, Any},277BC_indices::NTuple{N, Vector{Int}},278mesh::Union{UnstructuredMesh2D, P4estMesh,279T8codeMesh},280equations, surface_integral, dg::DG) where {N}281# Extract the boundary condition type and index vector282boundary_condition = first(BCs)283boundary_condition_indices = first(BC_indices)284# Extract the remaining types and indices to be processed later285remaining_boundary_conditions = Base.tail(BCs)286remaining_boundary_condition_indices = Base.tail(BC_indices)287288# process the first boundary condition type289calc_boundary_flux!(cache, t, boundary_condition, boundary_condition_indices,290mesh, equations, surface_integral, dg)291292# recursively call this method with the unprocessed boundary types293calc_boundary_flux_by_type!(cache, t, remaining_boundary_conditions,294remaining_boundary_condition_indices,295mesh, equations, surface_integral, dg)296297return nothing298end299300# terminate the type-stable iteration over tuples301function calc_boundary_flux_by_type!(cache, t, BCs::Tuple{}, BC_indices::Tuple{},302mesh::Union{UnstructuredMesh2D, P4estMesh,303T8codeMesh},304equations, surface_integral, dg::DG)305return nothing306end307308function calc_boundary_flux!(cache, t, boundary_condition::BC, boundary_indexing,309mesh::UnstructuredMesh2D, equations,310surface_integral, dg::DG) where {BC}311@unpack surface_flux_values = cache.elements312@unpack element_id, element_side_id = cache.boundaries313314@threaded for local_index in eachindex(boundary_indexing)315# use the local index to get the global boundary index from the pre-sorted list316boundary = boundary_indexing[local_index]317318# get the element and side IDs on the boundary element319element = element_id[boundary]320side = element_side_id[boundary]321322# calc boundary flux on the current boundary interface323for node in eachnode(dg)324calc_boundary_flux!(surface_flux_values, t, boundary_condition,325mesh, have_nonconservative_terms(equations),326equations, surface_integral, dg, cache,327node, side, element, boundary)328end329end330331return nothing332end333334# inlined version of the boundary flux calculation along a physical interface where the335# boundary flux values are set according to a particular `boundary_condition` function336@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,337mesh::UnstructuredMesh2D,338have_nonconservative_terms::False, equations,339surface_integral, dg::DG, cache,340node_index, side_index, element_index,341boundary_index)342@unpack normal_directions = cache.elements343@unpack u, node_coordinates = cache.boundaries344@unpack surface_flux = surface_integral345346# pull the inner solution state from the boundary u values on the boundary element347u_inner = get_node_vars(u, equations, dg, node_index, boundary_index)348349# pull the outward pointing (normal) directional vector350outward_direction = get_surface_normal(normal_directions, node_index, side_index,351element_index)352353# get the external solution values from the prescribed external state354x = get_node_coords(node_coordinates, equations, dg, node_index, boundary_index)355356# Call pointwise numerical flux function in the normal direction on the boundary357flux = boundary_condition(u_inner, outward_direction, x, t, surface_flux, equations)358359for v in eachvariable(equations)360surface_flux_values[v, node_index, side_index, element_index] = flux[v]361end362363return nothing364end365366# inlined version of the boundary flux and nonconseravtive terms calculation along a367# physical interface. The conservative portion of the boundary flux values368# are set according to a particular `boundary_condition` function369# Note, it is necessary to set and add in the nonconservative values because370# the upper left/lower right diagonal terms have been peeled off due to the use of371# `derivative_split` from `dg.basis` in [`flux_differencing_kernel!`](@ref)372@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,373mesh::UnstructuredMesh2D,374have_nonconservative_terms::True, equations,375surface_integral, dg::DG, cache,376node_index, side_index, element_index,377boundary_index)378@unpack normal_directions = cache.elements379@unpack u, node_coordinates = cache.boundaries380381# pull the inner solution state from the boundary u values on the boundary element382u_inner = get_node_vars(u, equations, dg, node_index, boundary_index)383384# pull the outward pointing (normal) directional vector385outward_direction = get_surface_normal(normal_directions, node_index, side_index,386element_index)387388# get the external solution values from the prescribed external state389x = get_node_coords(node_coordinates, equations, dg, node_index, boundary_index)390391# Call pointwise numerical flux functions for the conservative and nonconservative part392# in the normal direction on the boundary393flux, noncons_flux = boundary_condition(u_inner, outward_direction, x, t,394surface_integral.surface_flux, equations)395396for v in eachvariable(equations)397# Note the factor 0.5 necessary for the nonconservative fluxes based on398# the interpretation of global SBP operators coupled discontinuously via399# central fluxes/SATs400surface_flux_values[v, node_index, side_index, element_index] = flux[v] +4010.5f0 *402noncons_flux[v]403end404405return nothing406end407408# Note! The local side numbering for the unstructured quadrilateral element implementation differs409# from the structured TreeMesh or StructuredMesh local side numbering:410#411# TreeMesh/StructuredMesh sides versus UnstructuredMesh sides412# 4 3413# ----------------- -----------------414# | | | |415# | ^ eta | | ^ eta |416# 1 | | | 2 4 | | | 2417# | | | | | |418# | ---> xi | | ---> xi |419# ----------------- -----------------420# 3 1421# Therefore, we require a different surface integral routine here despite their similar structure.422function calc_surface_integral!(backend, du, u, mesh::UnstructuredMesh2D,423equations, surface_integral, dg::DGSEM, cache)424@unpack inverse_weights = dg.basis425@unpack surface_flux_values = cache.elements426427# Note that all fluxes have been computed with outward-pointing normal vectors.428# This computes the **negative** surface integral contribution,429# i.e., M^{-1} * boundary_interpolation^T430# and the missing "-" is taken care of by `apply_jacobian!`.431#432# We also use explicit assignments instead of `+=` and `-=` to let `@muladd`433# turn these into FMAs (see comment at the top of the file).434factor = inverse_weights[1] # For LGL basis: Identical to weighted boundary interpolation at x = ±1435@threaded for element in eachelement(dg, cache)436for l in eachnode(dg), v in eachvariable(equations)437# surface contribution along local sides 2 and 4 (fixed x and y varies)438du[v, 1, l, element] = du[v, 1, l, element] +439surface_flux_values[v, l, 4, element] *440factor441du[v, nnodes(dg), l, element] = du[v, nnodes(dg), l, element] +442surface_flux_values[v, l, 2, element] *443factor444445# surface contribution along local sides 1 and 3 (fixed y and x varies)446du[v, l, 1, element] = du[v, l, 1, element] +447surface_flux_values[v, l, 1, element] *448factor449du[v, l, nnodes(dg), element] = du[v, l, nnodes(dg), element] +450surface_flux_values[v, l, 3, element] *451factor452end453end454455return nothing456end457458# This routine computes the maximum value of the discrete metric identities necessary to ensure459# that the approximation will be free-stream preserving (i.e. a constant solution remains constant)460# on a curvilinear mesh.461# Note! Independent of the equation system and is only a check on the discrete mapping terms.462# Can be used for a metric identities check on StructuredMesh{2} or UnstructuredMesh2D463function max_discrete_metric_identities(dg::DGSEM, cache)464@unpack derivative_matrix = dg.basis465@unpack contravariant_vectors = cache.elements466467ndims_ = size(contravariant_vectors, 1)468469metric_id_dx = zeros(eltype(contravariant_vectors), nnodes(dg), nnodes(dg))470metric_id_dy = zeros(eltype(contravariant_vectors), nnodes(dg), nnodes(dg))471472max_metric_ids = zero(eltype(contravariant_vectors))473474for i in 1:ndims_, element in eachelement(dg, cache)475# compute D*Ja_1^i + Ja_2^i*D^T476@views mul!(metric_id_dx, derivative_matrix,477contravariant_vectors[i, 1, :, :, element])478@views mul!(metric_id_dy, contravariant_vectors[i, 2, :, :, element],479derivative_matrix')480local_max_metric_ids = maximum(abs.(metric_id_dx + metric_id_dy))481482max_metric_ids = max(max_metric_ids, local_max_metric_ids)483end484485return max_metric_ids486end487end # @muladd488489490