Path: blob/main/src/solvers/dgsem_p4est/dg_3d.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# The methods below are specialized on the mortar type8# and called from the basic `create_cache` method at the top.9function create_cache(mesh::Union{P4estMesh{3}, T8codeMesh{3}}, equations,10mortar_l2::LobattoLegendreMortarL2, uEltype)11A4d = Array{uEltype, 4}12fstar_primary_threaded = A4d[A4d(undef, nvariables(equations),13nnodes(mortar_l2), nnodes(mortar_l2), 4)14for _ in 1:Threads.maxthreadid()] |> VecOfArrays15fstar_secondary_threaded = A4d[A4d(undef, nvariables(equations),16nnodes(mortar_l2), nnodes(mortar_l2), 4)17for _ in 1:Threads.maxthreadid()] |> VecOfArrays1819A3d = Array{uEltype, 3}20fstar_tmp_threaded = A3d[A3d(undef, nvariables(equations),21nnodes(mortar_l2), nnodes(mortar_l2))22for _ in 1:Threads.maxthreadid()] |> VecOfArrays23u_threaded = A3d[A3d(undef, nvariables(equations),24nnodes(mortar_l2), nnodes(mortar_l2))25for _ in 1:Threads.maxthreadid()] |> VecOfArrays2627return (; fstar_primary_threaded, fstar_secondary_threaded, fstar_tmp_threaded,28u_threaded)29end3031# index_to_start_step_3d(index::Symbol, index_range)32#33# Given a symbolic `index` and an `indexrange` (usually `eachnode(dg)`),34# return `index_start, index_step_i, index_step_j`, i.e., a tuple containing35# - `index_start`, an index value to begin a loop36# - `index_step_i`, an index step to update during an `i` loop37# - `index_step_j`, an index step to update during a `j` loop38# The resulting indices translate surface indices to volume indices.39#40# !!! warning41# This assumes that loops using the return values are written as42#43# i_volume_start, i_volume_step_i, i_volume_step_j = index_to_start_step_3d(symbolic_index_i, index_range)44# j_volume_start, j_volume_step_i, j_volume_step_j = index_to_start_step_3d(symbolic_index_j, index_range)45# k_volume_start, k_volume_step_i, k_volume_step_j = index_to_start_step_3d(symbolic_index_k, index_range)46#47# i_volume, j_volume, k_volume = i_volume_start, j_volume_start, k_volume_start48# for j_surface in index_range49# for i_surface in index_range50# # do stuff with `(i_surface, j_surface)` and `(i_volume, j_volume, k_volume)`51#52# i_volume += i_volume_step_i53# j_volume += j_volume_step_i54# k_volume += k_volume_step_i55# end56# i_volume += i_volume_step_j57# j_volume += j_volume_step_j58# k_volume += k_volume_step_j59# end60@inline function index_to_start_step_3d(index::Symbol, index_range)61index_begin = first(index_range)62index_end = last(index_range)6364if index === :begin65return index_begin, 0, 066elseif index === :end67return index_end, 0, 068elseif index === :i_forward69return index_begin, 1, index_begin - index_end - 170elseif index === :i_backward71return index_end, -1, index_end + 1 - index_begin72elseif index === :j_forward73return index_begin, 0, 174else # if index === :j_backward75return index_end, 0, -176end77end7879# Extract the two varying indices from a symbolic index tuple.80# For example, `surface_indices((:i_forward, :end, :j_forward)) == (:i_forward, :j_forward)`.81@inline function surface_indices(indices::NTuple{3, Symbol})82i1, i2, i3 = indices83index = i184(index === :begin || index === :end) && return (i2, i3)8586index = i287(index === :begin || index === :end) && return (i1, i3)8889# i3 in (:begin, :end)90return (i1, i2)91end9293function prolong2interfaces!(backend::Nothing, cache, u,94mesh::Union{P4estMesh{3}, T8codeMesh{3}},95equations, dg::DG)96@unpack interfaces = cache97@unpack neighbor_ids, node_indices = cache.interfaces98index_range = eachnode(dg)99100@threaded for interface in eachinterface(dg, cache)101prolong2interfaces_per_interface!(interfaces.u, u, typeof(mesh), equations,102neighbor_ids, node_indices, index_range,103interface)104end105return nothing106end107108function prolong2interfaces!(backend::Backend, cache, u,109mesh::Union{P4estMesh{3}, T8codeMesh{3}},110equations, dg::DG)111@unpack interfaces = cache112@unpack neighbor_ids, node_indices = cache.interfaces113index_range = eachnode(dg)114115kernel! = prolong2interfaces_KAkernel!(backend)116kernel!(interfaces.u, u, typeof(mesh), equations, neighbor_ids, node_indices,117index_range,118ndrange = ninterfaces(interfaces))119return nothing120end121122@kernel function prolong2interfaces_KAkernel!(interface_u, u, MeshT, equations,123neighbor_ids, node_indices, index_range)124interface = @index(Global)125prolong2interfaces_per_interface!(interface_u, u, MeshT, equations, neighbor_ids,126node_indices, index_range, interface)127end128129@inline function prolong2interfaces_per_interface!(u_interface, u,130::Type{<:Union{P4estMesh{3},131T8codeMesh{3}}},132equations, neighbor_ids,133node_indices,134index_range, interface)135# Copy solution data from the primary element using "delayed indexing" with136# a start value and two step sizes to get the correct face and orientation.137# Note that in the current implementation, the interface will be138# "aligned at the primary element", i.e., the indices of the primary side139# will always run forwards.140primary_element = neighbor_ids[1, interface]141primary_indices = node_indices[1, interface]142143i_primary_start, i_primary_step_i, i_primary_step_j = index_to_start_step_3d(primary_indices[1],144index_range)145j_primary_start, j_primary_step_i, j_primary_step_j = index_to_start_step_3d(primary_indices[2],146index_range)147k_primary_start, k_primary_step_i, k_primary_step_j = index_to_start_step_3d(primary_indices[3],148index_range)149150i_primary = i_primary_start151j_primary = j_primary_start152k_primary = k_primary_start153for j in index_range154for i in index_range155for v in eachvariable(equations)156u_interface[1, v, i, j, interface] = u[v,157i_primary, j_primary,158k_primary,159primary_element]160end161i_primary += i_primary_step_i162j_primary += j_primary_step_i163k_primary += k_primary_step_i164end165i_primary += i_primary_step_j166j_primary += j_primary_step_j167k_primary += k_primary_step_j168end169170# Copy solution data from the secondary element using "delayed indexing" with171# a start value and two step sizes to get the correct face and orientation.172secondary_element = neighbor_ids[2, interface]173secondary_indices = node_indices[2, interface]174175i_secondary_start, i_secondary_step_i, i_secondary_step_j = index_to_start_step_3d(secondary_indices[1],176index_range)177j_secondary_start, j_secondary_step_i, j_secondary_step_j = index_to_start_step_3d(secondary_indices[2],178index_range)179k_secondary_start, k_secondary_step_i, k_secondary_step_j = index_to_start_step_3d(secondary_indices[3],180index_range)181182i_secondary = i_secondary_start183j_secondary = j_secondary_start184k_secondary = k_secondary_start185for j in index_range186for i in index_range187for v in eachvariable(equations)188u_interface[2, v, i, j, interface] = u[v,189i_secondary, j_secondary,190k_secondary,191secondary_element]192end193i_secondary += i_secondary_step_i194j_secondary += j_secondary_step_i195k_secondary += k_secondary_step_i196end197i_secondary += i_secondary_step_j198j_secondary += j_secondary_step_j199k_secondary += k_secondary_step_j200end201return nothing202end203204function calc_interface_flux!(backend::Nothing, surface_flux_values,205mesh::Union{P4estMesh{3}, T8codeMesh{3}},206have_nonconservative_terms,207equations, surface_integral, dg::DG, cache)208@unpack neighbor_ids, node_indices = cache.interfaces209@unpack contravariant_vectors = cache.elements210index_range = eachnode(dg)211212@threaded for interface in eachinterface(dg, cache)213calc_interface_flux_per_interface!(surface_flux_values,214typeof(mesh),215have_nonconservative_terms,216equations, surface_integral, typeof(dg),217cache.interfaces.u, neighbor_ids,218node_indices,219contravariant_vectors, index_range,220interface)221end222return nothing223end224225function calc_interface_flux!(backend::Backend, surface_flux_values,226mesh::Union{P4estMesh{3}, T8codeMesh{3}},227have_nonconservative_terms,228equations, surface_integral, dg::DG, cache)229@unpack neighbor_ids, node_indices = cache.interfaces230@unpack contravariant_vectors = cache.elements231index_range = eachnode(dg)232233kernel! = calc_interface_flux_KAkernel!(backend)234kernel!(surface_flux_values, typeof(mesh), have_nonconservative_terms, equations,235surface_integral, typeof(dg), cache.interfaces.u,236neighbor_ids, node_indices, contravariant_vectors, index_range,237ndrange = ninterfaces(cache.interfaces))238return nothing239end240241@kernel function calc_interface_flux_KAkernel!(surface_flux_values, MeshT,242have_nonconservative_terms, equations,243surface_integral, SolverT, u_interface,244neighbor_ids, node_indices,245contravariant_vectors, index_range)246interface = @index(Global)247calc_interface_flux_per_interface!(surface_flux_values,248MeshT,249have_nonconservative_terms,250equations, surface_integral, SolverT,251u_interface,252neighbor_ids, node_indices,253contravariant_vectors,254index_range, interface)255end256257@inline function calc_interface_flux_per_interface!(surface_flux_values,258MeshT::Type{<:Union{P4estMesh{3},259T8codeMesh{3}}},260have_nonconservative_terms,261equations, surface_integral,262SolverT::Type{<:DG}, u_interface,263neighbor_ids,264node_indices, contravariant_vectors,265index_range, interface)266# Get element and side information on the primary element267primary_element = neighbor_ids[1, interface]268primary_indices = node_indices[1, interface]269primary_direction = indices2direction(primary_indices)270271i_primary_start, i_primary_step_i, i_primary_step_j = index_to_start_step_3d(primary_indices[1],272index_range)273j_primary_start, j_primary_step_i, j_primary_step_j = index_to_start_step_3d(primary_indices[2],274index_range)275k_primary_start, k_primary_step_i, k_primary_step_j = index_to_start_step_3d(primary_indices[3],276index_range)277278i_primary = i_primary_start279j_primary = j_primary_start280k_primary = k_primary_start281282# Get element and side information on the secondary element283secondary_element = neighbor_ids[2, interface]284secondary_indices = node_indices[2, interface]285secondary_direction = indices2direction(secondary_indices)286secondary_surface_indices = surface_indices(secondary_indices)287288# Get the surface indexing on the secondary element.289# Note that the indices of the primary side will always run forward but290# the secondary indices might need to run backwards for flipped sides.291i_secondary_start, i_secondary_step_i, i_secondary_step_j = index_to_start_step_3d(secondary_surface_indices[1],292index_range)293j_secondary_start, j_secondary_step_i, j_secondary_step_j = index_to_start_step_3d(secondary_surface_indices[2],294index_range)295i_secondary = i_secondary_start296j_secondary = j_secondary_start297298for j in index_range299for i in index_range300# Get the normal direction from the primary element.301# Note, contravariant vectors at interfaces in negative coordinate direction302# are pointing inwards. This is handled by `get_normal_direction`.303normal_direction = get_normal_direction(primary_direction,304contravariant_vectors,305i_primary, j_primary, k_primary,306primary_element)307308calc_interface_flux!(surface_flux_values, MeshT, have_nonconservative_terms,309equations,310surface_integral, SolverT, u_interface,311interface, normal_direction,312i, j, primary_direction, primary_element,313i_secondary, j_secondary, secondary_direction,314secondary_element)315316# Increment the primary element indices317i_primary += i_primary_step_i318j_primary += j_primary_step_i319k_primary += k_primary_step_i320# Increment the secondary element surface indices321i_secondary += i_secondary_step_i322j_secondary += j_secondary_step_i323end324# Increment the primary element indices325i_primary += i_primary_step_j326j_primary += j_primary_step_j327k_primary += k_primary_step_j328# Increment the secondary element surface indices329i_secondary += i_secondary_step_j330j_secondary += j_secondary_step_j331end332return nothing333end334335# Inlined function for interface flux computation for conservative flux terms336@inline function calc_interface_flux!(surface_flux_values,337::Type{<:Union{P4estMesh{3}, T8codeMesh{3}}},338have_nonconservative_terms::False, equations,339surface_integral, SolverT::Type{<:DG},340u_interface,341interface_index, normal_direction,342primary_i_node_index, primary_j_node_index,343primary_direction_index, primary_element_index,344secondary_i_node_index, secondary_j_node_index,345secondary_direction_index,346secondary_element_index)347@unpack surface_flux = surface_integral348349u_ll, u_rr = get_surface_node_vars(u_interface, equations, SolverT,350primary_i_node_index,351primary_j_node_index, interface_index)352353flux_ = surface_flux(u_ll, u_rr, normal_direction, equations)354355for v in eachvariable(equations)356surface_flux_values[v, primary_i_node_index, primary_j_node_index,357primary_direction_index, primary_element_index] = flux_[v]358surface_flux_values[v, secondary_i_node_index, secondary_j_node_index,359secondary_direction_index, secondary_element_index] = -flux_[v]360end361362return nothing363end364365# Inlined function for interface flux computation for flux + nonconservative terms366@inline function calc_interface_flux!(surface_flux_values,367MeshT::Type{<:Union{P4estMesh{3}, T8codeMesh{3}}},368have_nonconservative_terms::True, equations,369surface_integral, SolverT::Type{<:DG},370u_interface,371interface_index, normal_direction,372primary_i_node_index, primary_j_node_index,373primary_direction_index, primary_element_index,374secondary_i_node_index, secondary_j_node_index,375secondary_direction_index,376secondary_element_index)377calc_interface_flux!(surface_flux_values, MeshT, have_nonconservative_terms,378combine_conservative_and_nonconservative_fluxes(surface_integral.surface_flux,379equations),380equations, surface_integral, SolverT, u_interface,381interface_index,382normal_direction, primary_i_node_index, primary_j_node_index,383primary_direction_index, primary_element_index,384secondary_i_node_index, secondary_j_node_index,385secondary_direction_index, secondary_element_index)386return nothing387end388389@inline function calc_interface_flux!(surface_flux_values,390::Type{<:Union{P4estMesh{3}, T8codeMesh{3}}},391have_nonconservative_terms::True,392combine_conservative_and_nonconservative_fluxes::False,393equations,394surface_integral, SolverT::Type{<:DG},395u_interface,396interface_index, normal_direction,397primary_i_node_index, primary_j_node_index,398primary_direction_index, primary_element_index,399secondary_i_node_index, secondary_j_node_index,400secondary_direction_index,401secondary_element_index)402surface_flux, nonconservative_flux = surface_integral.surface_flux403u_ll, u_rr = get_surface_node_vars(u_interface, equations, SolverT,404primary_i_node_index,405primary_j_node_index, interface_index)406407flux_ = surface_flux(u_ll, u_rr, normal_direction, equations)408409# Compute both nonconservative fluxes410noncons_primary = nonconservative_flux(u_ll, u_rr, normal_direction, equations)411noncons_secondary = nonconservative_flux(u_rr, u_ll, normal_direction, equations)412413# Store the flux with nonconservative terms on the primary and secondary elements414for v in eachvariable(equations)415# Note the factor 0.5 necessary for the nonconservative fluxes based on416# the interpretation of global SBP operators coupled discontinuously via417# central fluxes/SATs418surface_flux_values[v, primary_i_node_index, primary_j_node_index,419primary_direction_index, primary_element_index] = flux_[v] +4200.5f0 * noncons_primary[v]421surface_flux_values[v, secondary_i_node_index, secondary_j_node_index,422secondary_direction_index, secondary_element_index] = -(flux_[v] +4230.5f0 *424noncons_secondary[v])425end426427return nothing428end429430@inline function calc_interface_flux!(surface_flux_values,431::Type{<:Union{P4estMesh{3}, T8codeMesh{3}}},432have_nonconservative_terms::True,433combine_conservative_and_nonconservative_fluxes::True,434equations,435surface_integral, SolverT::Type{<:DG},436u_interface,437interface_index, normal_direction,438primary_i_node_index, primary_j_node_index,439primary_direction_index, primary_element_index,440secondary_i_node_index, secondary_j_node_index,441secondary_direction_index,442secondary_element_index)443@unpack surface_flux = surface_integral444u_ll, u_rr = get_surface_node_vars(u_interface, equations, SolverT,445primary_i_node_index, primary_j_node_index,446interface_index)447448flux_left, flux_right = surface_flux(u_ll, u_rr, normal_direction, equations)449450# Store the flux with nonconservative terms on the primary and secondary elements451for v in eachvariable(equations)452surface_flux_values[v, primary_i_node_index, primary_j_node_index,453primary_direction_index, primary_element_index] = flux_left[v]454surface_flux_values[v, secondary_i_node_index, secondary_j_node_index,455secondary_direction_index, secondary_element_index] = -flux_right[v]456end457458return nothing459end460461function prolong2boundaries!(cache, u,462mesh::Union{P4estMesh{3}, T8codeMesh{3}},463equations, dg::DG)464@unpack boundaries = cache465index_range = eachnode(dg)466467@threaded for boundary in eachboundary(dg, cache)468# Copy solution data from the element using "delayed indexing" with469# a start value and two step sizes to get the correct face and orientation.470element = boundaries.neighbor_ids[boundary]471node_indices = boundaries.node_indices[boundary]472473i_node_start, i_node_step_i, i_node_step_j = index_to_start_step_3d(node_indices[1],474index_range)475j_node_start, j_node_step_i, j_node_step_j = index_to_start_step_3d(node_indices[2],476index_range)477k_node_start, k_node_step_i, k_node_step_j = index_to_start_step_3d(node_indices[3],478index_range)479480i_node = i_node_start481j_node = j_node_start482k_node = k_node_start483for j in eachnode(dg)484for i in eachnode(dg)485for v in eachvariable(equations)486boundaries.u[v, i, j, boundary] = u[v, i_node, j_node, k_node,487element]488end489i_node += i_node_step_i490j_node += j_node_step_i491k_node += k_node_step_i492end493i_node += i_node_step_j494j_node += j_node_step_j495k_node += k_node_step_j496end497end498499return nothing500end501502function calc_boundary_flux!(cache, t, boundary_condition::BC, boundary_indexing,503mesh::Union{P4estMesh{3}, T8codeMesh{3}},504equations, surface_integral, dg::DG) where {BC}505@unpack boundaries = cache506@unpack surface_flux_values = cache.elements507index_range = eachnode(dg)508509@threaded for local_index in eachindex(boundary_indexing)510# Use the local index to get the global boundary index from the511# pre-sorted list512boundary = boundary_indexing[local_index]513514# Get information on the adjacent element, compute the surface fluxes,515# and store them516element = boundaries.neighbor_ids[boundary]517node_indices = boundaries.node_indices[boundary]518direction = indices2direction(node_indices)519520i_node_start, i_node_step_i, i_node_step_j = index_to_start_step_3d(node_indices[1],521index_range)522j_node_start, j_node_step_i, j_node_step_j = index_to_start_step_3d(node_indices[2],523index_range)524k_node_start, k_node_step_i, k_node_step_j = index_to_start_step_3d(node_indices[3],525index_range)526527i_node = i_node_start528j_node = j_node_start529k_node = k_node_start530for j in eachnode(dg)531for i in eachnode(dg)532calc_boundary_flux!(surface_flux_values, t, boundary_condition, mesh,533have_nonconservative_terms(equations), equations,534surface_integral, dg, cache, i_node, j_node, k_node,535i, j, direction, element, boundary)536i_node += i_node_step_i537j_node += j_node_step_i538k_node += k_node_step_i539end540i_node += i_node_step_j541j_node += j_node_step_j542k_node += k_node_step_j543end544end545546return nothing547end548549# inlined version of the boundary flux calculation along a physical interface550@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,551mesh::Union{P4estMesh{3}, T8codeMesh{3}},552have_nonconservative_terms::False, equations,553surface_integral, dg::DG, cache, i_index, j_index,554k_index, i_node_index, j_node_index,555direction_index,556element_index, boundary_index)557@unpack boundaries = cache558@unpack node_coordinates, contravariant_vectors = cache.elements559@unpack surface_flux = surface_integral560561# Extract solution data from boundary container562u_inner = get_node_vars(boundaries.u, equations, dg, i_node_index, j_node_index,563boundary_index)564565# Outward-pointing normal direction (not normalized)566normal_direction = get_normal_direction(direction_index, contravariant_vectors,567i_index, j_index, k_index, element_index)568569# Coordinates at boundary node570x = get_node_coords(node_coordinates, equations, dg,571i_index, j_index, k_index, element_index)572573flux_ = boundary_condition(u_inner, normal_direction, x, t,574surface_flux, equations)575576# Copy flux to element storage in the correct orientation577for v in eachvariable(equations)578surface_flux_values[v, i_node_index, j_node_index,579direction_index, element_index] = flux_[v]580end581582return nothing583end584585# inlined version of the boundary flux calculation along a physical interface586@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,587mesh::Union{P4estMesh{3}, T8codeMesh{3}},588have_nonconservative_terms::True, equations,589surface_integral, dg::DG, cache, i_index, j_index,590k_index, i_node_index, j_node_index,591direction_index,592element_index, boundary_index)593calc_boundary_flux!(surface_flux_values, t, boundary_condition, mesh,594have_nonconservative_terms,595combine_conservative_and_nonconservative_fluxes(surface_integral.surface_flux,596equations),597equations,598surface_integral, dg, cache,599i_index, j_index, k_index, i_node_index, j_node_index,600direction_index, element_index, boundary_index)601return nothing602end603604@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,605mesh::Union{P4estMesh{3}, T8codeMesh{3}},606have_nonconservative_terms::True,607combine_conservative_and_nonconservative_fluxes::False,608equations,609surface_integral, dg::DG, cache, i_index, j_index,610k_index, i_node_index, j_node_index,611direction_index,612element_index, boundary_index)613@unpack boundaries = cache614@unpack node_coordinates, contravariant_vectors = cache.elements615@unpack surface_flux = surface_integral616617# Extract solution data from boundary container618u_inner = get_node_vars(boundaries.u, equations, dg, i_node_index, j_node_index,619boundary_index)620621# Outward-pointing normal direction (not normalized)622normal_direction = get_normal_direction(direction_index, contravariant_vectors,623i_index, j_index, k_index, element_index)624625# Coordinates at boundary node626x = get_node_coords(node_coordinates, equations, dg,627i_index, j_index, k_index, element_index)628629# Call pointwise numerical flux functions for the conservative and nonconservative part630# in the normal direction on the boundary631flux, noncons_flux = boundary_condition(u_inner, normal_direction, x, t,632surface_flux, equations)633634# Copy flux to element storage in the correct orientation635for v in eachvariable(equations)636# Note the factor 0.5 necessary for the nonconservative fluxes based on637# the interpretation of global SBP operators coupled discontinuously via638# central fluxes/SATs639surface_flux_values[v, i_node_index, j_node_index,640direction_index, element_index] = flux[v] + 0.5f0 *641noncons_flux[v]642end643644return nothing645end646647@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,648mesh::Union{P4estMesh{3}, T8codeMesh{3}},649have_nonconservative_terms::True,650combine_conservative_and_nonconservative_fluxes::True,651equations,652surface_integral, dg::DG, cache, i_index, j_index,653k_index, i_node_index, j_node_index,654direction_index,655element_index, boundary_index)656@unpack boundaries = cache657@unpack node_coordinates, contravariant_vectors = cache.elements658@unpack surface_flux = surface_integral659660# Extract solution data from boundary container661u_inner = get_node_vars(boundaries.u, equations, dg, i_node_index, j_node_index,662boundary_index)663664# Outward-pointing normal direction (not normalized)665normal_direction = get_normal_direction(direction_index, contravariant_vectors,666i_index, j_index, k_index, element_index)667668# Coordinates at boundary node669x = get_node_coords(node_coordinates, equations, dg,670i_index, j_index, k_index, element_index)671672# Call pointwise numerical flux functions for the conservative and nonconservative part673# in the normal direction on the boundary674flux = boundary_condition(u_inner, normal_direction, x, t,675surface_flux, equations)676677# Copy flux to element storage in the correct orientation678for v in eachvariable(equations)679surface_flux_values[v, i_node_index, j_node_index,680direction_index, element_index] = flux[v]681end682683return nothing684end685686function prolong2mortars!(cache, u,687mesh::Union{P4estMesh{3}, T8codeMesh{3}}, equations,688mortar_l2::LobattoLegendreMortarL2,689dg::DGSEM)690@unpack fstar_tmp_threaded = cache691@unpack neighbor_ids, node_indices = cache.mortars692index_range = eachnode(dg)693694@threaded for mortar in eachmortar(dg, cache)695# Copy solution data from the small elements using "delayed indexing" with696# a start value and two step sizes to get the correct face and orientation.697small_indices = node_indices[1, mortar]698699i_small_start, i_small_step_i, i_small_step_j = index_to_start_step_3d(small_indices[1],700index_range)701j_small_start, j_small_step_i, j_small_step_j = index_to_start_step_3d(small_indices[2],702index_range)703k_small_start, k_small_step_i, k_small_step_j = index_to_start_step_3d(small_indices[3],704index_range)705706for position in 1:4707i_small = i_small_start708j_small = j_small_start709k_small = k_small_start710element = neighbor_ids[position, mortar]711for j in eachnode(dg)712for i in eachnode(dg)713for v in eachvariable(equations)714cache.mortars.u[1, v, position, i, j, mortar] = u[v, i_small,715j_small,716k_small,717element]718end719i_small += i_small_step_i720j_small += j_small_step_i721k_small += k_small_step_i722end723i_small += i_small_step_j724j_small += j_small_step_j725k_small += k_small_step_j726end727end728729# Buffer to copy solution values of the large element in the correct orientation730# before interpolating731u_buffer = cache.u_threaded[Threads.threadid()]732# temporary buffer for projections733fstar_tmp = fstar_tmp_threaded[Threads.threadid()]734735# Copy solution of large element face to buffer in the736# correct orientation737large_indices = node_indices[2, mortar]738739i_large_start, i_large_step_i, i_large_step_j = index_to_start_step_3d(large_indices[1],740index_range)741j_large_start, j_large_step_i, j_large_step_j = index_to_start_step_3d(large_indices[2],742index_range)743k_large_start, k_large_step_i, k_large_step_j = index_to_start_step_3d(large_indices[3],744index_range)745746i_large = i_large_start747j_large = j_large_start748k_large = k_large_start749element = neighbor_ids[5, mortar]750for j in eachnode(dg)751for i in eachnode(dg)752for v in eachvariable(equations)753u_buffer[v, i, j] = u[v, i_large, j_large, k_large, element]754end755i_large += i_large_step_i756j_large += j_large_step_i757k_large += k_large_step_i758end759i_large += i_large_step_j760j_large += j_large_step_j761k_large += k_large_step_j762end763764# Interpolate large element face data from buffer to small face locations765multiply_dimensionwise!(view(cache.mortars.u, 2, :, 1, :, :, mortar),766mortar_l2.forward_lower,767mortar_l2.forward_lower,768u_buffer,769fstar_tmp)770multiply_dimensionwise!(view(cache.mortars.u, 2, :, 2, :, :, mortar),771mortar_l2.forward_upper,772mortar_l2.forward_lower,773u_buffer,774fstar_tmp)775multiply_dimensionwise!(view(cache.mortars.u, 2, :, 3, :, :, mortar),776mortar_l2.forward_lower,777mortar_l2.forward_upper,778u_buffer,779fstar_tmp)780multiply_dimensionwise!(view(cache.mortars.u, 2, :, 4, :, :, mortar),781mortar_l2.forward_upper,782mortar_l2.forward_upper,783u_buffer,784fstar_tmp)785end786787return nothing788end789790function calc_mortar_flux!(surface_flux_values,791mesh::Union{P4estMesh{3}, T8codeMesh{3}},792have_nonconservative_terms, equations,793mortar_l2::LobattoLegendreMortarL2,794surface_integral, dg::DG, cache)795@unpack neighbor_ids, node_indices = cache.mortars796@unpack contravariant_vectors = cache.elements797@unpack fstar_primary_threaded, fstar_secondary_threaded, fstar_tmp_threaded = cache798index_range = eachnode(dg)799800@threaded for mortar in eachmortar(dg, cache)801# Choose thread-specific pre-allocated container802fstar_primary = fstar_primary_threaded[Threads.threadid()]803fstar_secondary = fstar_secondary_threaded[Threads.threadid()]804fstar_tmp = fstar_tmp_threaded[Threads.threadid()]805806# Get index information on the small elements807small_indices = node_indices[1, mortar]808small_direction = indices2direction(small_indices)809810i_small_start, i_small_step_i, i_small_step_j = index_to_start_step_3d(small_indices[1],811index_range)812j_small_start, j_small_step_i, j_small_step_j = index_to_start_step_3d(small_indices[2],813index_range)814k_small_start, k_small_step_i, k_small_step_j = index_to_start_step_3d(small_indices[3],815index_range)816817for position in 1:4818i_small = i_small_start819j_small = j_small_start820k_small = k_small_start821element = neighbor_ids[position, mortar]822for j in eachnode(dg)823for i in eachnode(dg)824# Get the normal direction on the small element.825# Note, contravariant vectors at interfaces in negative coordinate direction826# are pointing inwards. This is handled by `get_normal_direction`.827normal_direction = get_normal_direction(small_direction,828contravariant_vectors,829i_small, j_small, k_small,830element)831832calc_mortar_flux!(fstar_primary, fstar_secondary, mesh,833have_nonconservative_terms, equations,834surface_integral, dg, cache,835mortar, position, normal_direction,836i, j)837838i_small += i_small_step_i839j_small += j_small_step_i840k_small += k_small_step_i841end842i_small += i_small_step_j843j_small += j_small_step_j844k_small += k_small_step_j845end846end847848# Buffer to interpolate flux values of the large element to before849# copying in the correct orientation850u_buffer = cache.u_threaded[Threads.threadid()]851852# in calc_interface_flux!, the interface flux is computed once over each853# interface using the normal from the "primary" element. The result is then854# passed back to the "secondary" element, flipping the sign to account for the855# change in the normal direction. For mortars, this sign flip occurs in856# "mortar_fluxes_to_elements!" instead.857mortar_fluxes_to_elements!(surface_flux_values,858mesh, equations, mortar_l2, dg, cache,859mortar, fstar_primary, fstar_secondary, u_buffer,860fstar_tmp)861end862863return nothing864end865866# Inlined version of the mortar flux computation on small elements for conservation fluxes867@inline function calc_mortar_flux!(fstar_primary, fstar_secondary,868mesh::Union{P4estMesh{3}, T8codeMesh{3}},869have_nonconservative_terms::False, equations,870surface_integral, dg::DG, cache,871mortar_index, position_index, normal_direction,872i_node_index, j_node_index)873@unpack u = cache.mortars874@unpack surface_flux = surface_integral875876u_ll, u_rr = get_surface_node_vars(u, equations, dg, position_index,877i_node_index, j_node_index, mortar_index)878879flux = surface_flux(u_ll, u_rr, normal_direction, equations)880881# Copy flux to buffer882set_node_vars!(fstar_primary, flux, equations, dg,883i_node_index, j_node_index, position_index)884set_node_vars!(fstar_secondary, flux, equations, dg,885i_node_index, j_node_index, position_index)886887return nothing888end889890# Inlined version of the mortar flux computation on small elements for conservation fluxes891# with nonconservative terms892@inline function calc_mortar_flux!(fstar_primary, fstar_secondary,893mesh::Union{P4estMesh{3}, T8codeMesh{3}},894have_nonconservative_terms::True, equations,895surface_integral, dg::DG, cache,896mortar_index, position_index, normal_direction,897i_node_index, j_node_index)898@unpack u = cache.mortars899surface_flux, nonconservative_flux = surface_integral.surface_flux900901u_ll, u_rr = get_surface_node_vars(u, equations, dg, position_index, i_node_index,902j_node_index, mortar_index)903904# Compute conservative flux905flux = surface_flux(u_ll, u_rr, normal_direction, equations)906907# Compute nonconservative flux and add it to the flux scaled by a factor of 0.5 based on908# the interpretation of global SBP operators coupled discontinuously via909# central fluxes/SATs910noncons_primary = nonconservative_flux(u_ll, u_rr, normal_direction, equations)911noncons_secondary = nonconservative_flux(u_rr, u_ll, normal_direction, equations)912flux_plus_noncons_primary = flux + 0.5f0 * noncons_primary913flux_plus_noncons_secondary = flux + 0.5f0 * noncons_secondary914915# Copy to buffer916set_node_vars!(fstar_primary, flux_plus_noncons_primary, equations, dg,917i_node_index, j_node_index, position_index)918set_node_vars!(fstar_secondary, flux_plus_noncons_secondary, equations, dg,919i_node_index, j_node_index, position_index)920921return nothing922end923924@inline function mortar_fluxes_to_elements!(surface_flux_values,925mesh::Union{P4estMesh{3}, T8codeMesh{3}},926equations,927mortar_l2::LobattoLegendreMortarL2,928dg::DGSEM, cache, mortar, fstar_primary,929fstar_secondary, u_buffer, fstar_tmp)930@unpack neighbor_ids, node_indices = cache.mortars931index_range = eachnode(dg)932933# Copy solution small to small934small_indices = node_indices[1, mortar]935small_direction = indices2direction(small_indices)936937for position in 1:4938element = neighbor_ids[position, mortar]939for j in eachnode(dg), i in eachnode(dg)940for v in eachvariable(equations)941surface_flux_values[v, i, j, small_direction, element] = fstar_primary[v,942i,943j,944position]945end946end947end948949# Project small fluxes to large element.950multiply_dimensionwise!(u_buffer,951mortar_l2.reverse_lower, mortar_l2.reverse_lower,952view(fstar_secondary, .., 1),953fstar_tmp)954add_multiply_dimensionwise!(u_buffer,955mortar_l2.reverse_upper, mortar_l2.reverse_lower,956view(fstar_secondary, .., 2),957fstar_tmp)958add_multiply_dimensionwise!(u_buffer,959mortar_l2.reverse_lower, mortar_l2.reverse_upper,960view(fstar_secondary, .., 3),961fstar_tmp)962add_multiply_dimensionwise!(u_buffer,963mortar_l2.reverse_upper, mortar_l2.reverse_upper,964view(fstar_secondary, .., 4),965fstar_tmp)966967# The flux is calculated in the outward direction of the small elements,968# so the sign must be switched to get the flux in outward direction969# of the large element.970# The contravariant vectors of the large element (and therefore the normal971# vectors of the large element as well) are four times as large as the972# contravariant vectors of the small elements. Therefore, the flux needs973# to be scaled by a factor of 4 to obtain the flux of the large element.974u_buffer .*= -4975976# Copy interpolated flux values from buffer to large element face in the977# correct orientation.978# Note that the index of the small sides will always run forward but979# the index of the large side might need to run backwards for flipped sides.980large_element = neighbor_ids[5, mortar]981large_indices = node_indices[2, mortar]982large_direction = indices2direction(large_indices)983large_surface_indices = surface_indices(large_indices)984985i_large_start, i_large_step_i, i_large_step_j = index_to_start_step_3d(large_surface_indices[1],986index_range)987j_large_start, j_large_step_i, j_large_step_j = index_to_start_step_3d(large_surface_indices[2],988index_range)989990# Note that the indices of the small sides will always run forward but991# the large indices might need to run backwards for flipped sides.992i_large = i_large_start993j_large = j_large_start994for j in eachnode(dg)995for i in eachnode(dg)996for v in eachvariable(equations)997surface_flux_values[v, i_large, j_large, large_direction, large_element] = u_buffer[v,998i,999j]1000end1001i_large += i_large_step_i1002j_large += j_large_step_i1003end1004i_large += i_large_step_j1005j_large += j_large_step_j1006end10071008return nothing1009end10101011function calc_surface_integral!(backend::Nothing, du, u,1012mesh::Union{P4estMesh{3}, T8codeMesh{3}},1013equations, surface_integral::SurfaceIntegralWeakForm,1014dg::DGSEM, cache)1015@unpack inverse_weights = dg.basis1016@unpack surface_flux_values = cache.elements10171018@threaded for element in eachelement(dg, cache)1019calc_surface_integral_per_element!(du, typeof(mesh),1020equations, surface_integral,1021dg, inverse_weights[1],1022surface_flux_values,1023element)1024end1025return nothing1026end10271028function calc_surface_integral!(backend::Backend, du, u,1029mesh::Union{P4estMesh{3}, T8codeMesh{3}},1030equations,1031surface_integral::SurfaceIntegralWeakForm,1032dg::DGSEM, cache)1033@unpack inverse_weights = dg.basis1034@unpack surface_flux_values = cache.elements10351036kernel! = calc_surface_integral_KAkernel!(backend)1037kernel!(du, typeof(mesh), equations, surface_integral, dg, inverse_weights[1],1038surface_flux_values, ndrange = nelements(cache.elements))1039return nothing1040end10411042@kernel function calc_surface_integral_KAkernel!(du, MeshT, equations,1043surface_integral, dg, factor,1044surface_flux_values)1045element = @index(Global)1046calc_surface_integral_per_element!(du, MeshT,1047equations, surface_integral, dg, factor,1048surface_flux_values, element)1049end10501051@inline function calc_surface_integral_per_element!(du,1052::Type{<:Union{P4estMesh{3},1053T8codeMesh{3}}},1054equations,1055surface_integral::SurfaceIntegralWeakForm,1056dg::DGSEM, factor,1057surface_flux_values,1058element)1059# Note that all fluxes have been computed with outward-pointing normal vectors.1060# This computes the **negative** surface integral contribution,1061# i.e., M^{-1} * boundary_interpolation^T (which is for Gauss-Lobatto DGSEM just M^{-1} * B)1062# and the missing "-" is taken care of by `apply_jacobian!`.1063#1064# We also use explicit assignments instead of `+=` to let `@muladd` turn these1065# into FMAs (see comment at the top of the file).1066#1067# factor = inverse_weights[1]1068# For LGL basis: Identical to weighted boundary interpolation at x = ±11069for m in eachnode(dg), l in eachnode(dg)1070for v in eachvariable(equations)1071# surface at -x1072du[v, 1, l, m, element] = (du[v, 1, l, m, element] +1073surface_flux_values[v, l, m, 1,1074element] *1075factor)10761077# surface at +x1078du[v, nnodes(dg), l, m, element] = (du[v, nnodes(dg), l, m, element] +1079surface_flux_values[v, l, m, 2,1080element] *1081factor)10821083# surface at -y1084du[v, l, 1, m, element] = (du[v, l, 1, m, element] +1085surface_flux_values[v, l, m, 3,1086element] *1087factor)10881089# surface at +y1090du[v, l, nnodes(dg), m, element] = (du[v, l, nnodes(dg), m, element] +1091surface_flux_values[v, l, m, 4,1092element] *1093factor)10941095# surface at -z1096du[v, l, m, 1, element] = (du[v, l, m, 1, element] +1097surface_flux_values[v, l, m, 5,1098element] *1099factor)11001101# surface at +z1102du[v, l, m, nnodes(dg), element] = (du[v, l, m, nnodes(dg), element] +1103surface_flux_values[v, l, m, 6,1104element] *1105factor)1106end1107end1108return nothing1109end1110end # @muladd111111121113