Path: blob/main/src/solvers/dgsem_p4est/dg_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# 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{2}, P4estMeshView{2}, T8codeMesh{2}},10equations,11mortar_l2::LobattoLegendreMortarL2, uEltype)12MA2d = MArray{Tuple{nvariables(equations), nnodes(mortar_l2)},13uEltype, 2,14nvariables(equations) * nnodes(mortar_l2)}15fstar_primary_upper_threaded = MA2d[MA2d(undef) for _ in 1:Threads.maxthreadid()]16fstar_primary_lower_threaded = MA2d[MA2d(undef) for _ in 1:Threads.maxthreadid()]17fstar_secondary_upper_threaded = MA2d[MA2d(undef) for _ in 1:Threads.maxthreadid()]18fstar_secondary_lower_threaded = MA2d[MA2d(undef) for _ in 1:Threads.maxthreadid()]19u_threaded = MA2d[MA2d(undef) for _ in 1:Threads.maxthreadid()]2021cache = (; fstar_primary_upper_threaded, fstar_primary_lower_threaded,22fstar_secondary_upper_threaded, fstar_secondary_lower_threaded,23u_threaded)2425return cache26end2728# index_to_start_step_2d(index::Symbol, index_range)29#30# Given a symbolic `index` and an `indexrange` (usually `eachnode(dg)`),31# return `index_start, index_step`, i.e., a tuple containing32# - `index_start`, an index value to begin a loop33# - `index_step`, an index step to update during a loop34# The resulting indices translate surface indices to volume indices.35#36# !!! warning37# This assumes that loops using the return values are written as38#39# i_volume_start, i_volume_step = index_to_start_step_2d(symbolic_index_i, index_range)40# j_volume_start, j_volume_step = index_to_start_step_2d(symbolic_index_j, index_range)41#42# i_volume, j_volume = i_volume_start, j_volume_start43# for i_surface in index_range44# # do stuff with `i_surface` and `(i_volume, j_volume)`45#46# i_volume += i_volume_step47# j_volume += j_volume_step48# end49@inline function index_to_start_step_2d(index::Symbol, index_range)50index_begin = first(index_range)51index_end = last(index_range)5253if index === :begin54return index_begin, 055elseif index === :end56return index_end, 057elseif index === :i_forward58return index_begin, 159else # if index === :i_backward60return index_end, -161end62end6364# Infer interpolation side, i.e., left (1) or right (2) for an element.65# Required for boundary interpolation with Gauss-Legendre nodes.66@inline function interpolation_side(index)67return (index === :begin) ? 1 : 268end6970function prolong2interfaces!(backend::Nothing, cache, u,71mesh::Union{P4estMesh{2}, P4estMeshView{2},72T8codeMesh{2}},73equations, dg::DGSEM{<:LobattoLegendreBasis})74@unpack interfaces = cache75@unpack neighbor_ids, node_indices = cache.interfaces76index_range = eachnode(dg)7778@threaded for interface in eachinterface(dg, cache)79prolong2interfaces_per_interface!(interfaces.u, u, interface,80typeof(mesh), equations,81neighbor_ids, node_indices, index_range)82end83return nothing84end8586function prolong2interfaces!(backend::Backend, cache, u,87mesh::Union{P4estMesh{2}, P4estMeshView{2},88T8codeMesh{2}},89equations, dg::DGSEM{<:LobattoLegendreBasis})90@unpack interfaces = cache91ninterfaces(interfaces) == 0 && return nothing92@unpack neighbor_ids, node_indices = cache.interfaces93index_range = eachnode(dg)9495kernel! = prolong2interfaces_KAkernel!(backend)96kernel!(interfaces.u, u, typeof(mesh), equations, neighbor_ids, node_indices,97index_range, ndrange = ninterfaces(interfaces))98return nothing99end100101@kernel function prolong2interfaces_KAkernel!(interfaces_u, u,102MeshT::Type{<:Union{P4estMesh{2},103P4estMeshView{2},104T8codeMesh{2}}},105equations, neighbor_ids,106node_indices, index_range)107interface = @index(Global)108prolong2interfaces_per_interface!(interfaces_u, u, interface, MeshT, equations,109neighbor_ids, node_indices, index_range)110end111112# Version for Gauss-Lobatto-Legendre113@inline function prolong2interfaces_per_interface!(interfaces_u, u, interface,114::Type{<:Union{P4estMesh{2},115P4estMeshView{2},116T8codeMesh{2}}},117equations, neighbor_ids,118node_indices, index_range)119primary_element = neighbor_ids[1, interface]120primary_indices = node_indices[1, interface]121122i_primary_start, i_primary_step = index_to_start_step_2d(primary_indices[1],123index_range)124j_primary_start, j_primary_step = index_to_start_step_2d(primary_indices[2],125index_range)126127i_primary = i_primary_start128j_primary = j_primary_start129for i in index_range130for v in eachvariable(equations)131interfaces_u[1, v, i, interface] = u[v, i_primary, j_primary,132primary_element]133end134i_primary += i_primary_step135j_primary += j_primary_step136end137138# Copy solution data from the secondary element using "delayed indexing" with139# a start value and a step size to get the correct face and orientation.140secondary_element = neighbor_ids[2, interface]141secondary_indices = node_indices[2, interface]142143i_secondary_start, i_secondary_step = index_to_start_step_2d(secondary_indices[1],144index_range)145j_secondary_start, j_secondary_step = index_to_start_step_2d(secondary_indices[2],146index_range)147148i_secondary = i_secondary_start149j_secondary = j_secondary_start150for i in index_range151for v in eachvariable(equations)152interfaces_u[2, v, i, interface] = u[v, i_secondary, j_secondary,153secondary_element]154end155i_secondary += i_secondary_step156j_secondary += j_secondary_step157end158159return nothing160end161162function prolong2interfaces!(backend::Nothing, cache, u,163mesh::Union{P4estMesh{2}, P4estMeshView{2}},164equations, dg::DGSEM{<:GaussLegendreBasis})165@unpack interfaces = cache166@unpack neighbor_ids, node_indices = cache.interfaces167@unpack boundary_interpolation = dg.basis168index_range = eachnode(dg)169170@threaded for interface in eachinterface(dg, cache)171prolong2interfaces_per_interface!(interfaces.u, u, interface,172typeof(mesh), equations,173neighbor_ids, node_indices, index_range,174boundary_interpolation)175end176177return nothing178end179180# Version for Gauss-Legendre, which requires passing in the boundary interpolation matrix181@inline function prolong2interfaces_per_interface!(interfaces_u, u, interface,182::Type{<:Union{P4estMesh{2},183P4estMeshView{2}}},184equations, neighbor_ids,185node_indices, index_range,186boundary_interpolation)187# Interpolate solution data from the primary element to the interface.188primary_element = neighbor_ids[1, interface]189primary_indices = node_indices[1, interface]190191i_primary_start, i_primary_step = index_to_start_step_2d(primary_indices[1],192index_range)193j_primary_start, j_primary_step = index_to_start_step_2d(primary_indices[2],194index_range)195# The index direction is identified based on `{i,j}_{primary, secondary}_step`.196# For step = 0, the direction identified by this index is normal to the face.197# For step != 0 (1 or -1), the direction identified by this index is tangential to the face.198199# Note that in the current implementation, the interface will be200# "aligned at the primary element", i.e., the index of the primary side201# will always run forwards.202203i_primary = i_primary_start204j_primary = j_primary_start205206if i_primary_step == 0207# i is the normal direction (constant), j varies along the surface208# => Interpolate in first/normal direction209# Interpolation side is governed by element orientation210side = interpolation_side(primary_indices[1])211for i in index_range212for v in eachvariable(equations)213u_primary = zero(eltype(interfaces_u))214for ii in index_range215u_primary = (u_primary +216u[v, ii, j_primary, primary_element] *217boundary_interpolation[ii, side])218end219interfaces_u[1, v, i, interface] = u_primary220end221j_primary += j_primary_step # incrementing j_primary suffices222end223else # j_primary_step == 0224# j is the normal direction (constant), i varies along the surface225# => Interpolate in second/normal direction226# Interpolation side is governed by element orientation227side = interpolation_side(primary_indices[2])228for i in index_range229for v in eachvariable(equations)230u_primary = zero(eltype(interfaces_u))231for jj in index_range232u_primary = (u_primary +233u[v, i_primary, jj, primary_element] *234boundary_interpolation[jj, side])235end236interfaces_u[1, v, i, interface] = u_primary237end238i_primary += i_primary_step # incrementing i_primary suffices239end240end241242# Interpolate solution data from the secondary element to the interface.243secondary_element = neighbor_ids[2, interface]244secondary_indices = node_indices[2, interface]245246i_secondary_start, i_secondary_step = index_to_start_step_2d(secondary_indices[1],247index_range)248j_secondary_start, j_secondary_step = index_to_start_step_2d(secondary_indices[2],249index_range)250251i_secondary = i_secondary_start252j_secondary = j_secondary_start253if i_secondary_step == 0254side = interpolation_side(secondary_indices[1])255for i in index_range256for v in eachvariable(equations)257u_secondary = zero(eltype(interfaces_u))258for ii in index_range259u_secondary = (u_secondary +260u[v, ii, j_secondary, secondary_element] *261boundary_interpolation[ii, side])262end263interfaces_u[2, v, i, interface] = u_secondary264end265j_secondary += j_secondary_step # incrementing j_secondary suffices266end267else # j_secondary_step == 0268side = interpolation_side(secondary_indices[2])269for i in index_range270for v in eachvariable(equations)271u_secondary = zero(eltype(interfaces_u))272for jj in index_range273u_secondary = (u_secondary +274u[v, i_secondary, jj, secondary_element] *275boundary_interpolation[jj, side])276end277interfaces_u[2, v, i, interface] = u_secondary278end279i_secondary += i_secondary_step # incrementing i_secondary suffices280end281end282283return nothing284end285286function calc_interface_flux!(backend::Nothing, surface_flux_values,287mesh::Union{P4estMesh{2}, P4estMeshView{2},288T8codeMesh{2}},289have_nonconservative_terms,290equations, surface_integral,291dg::DGSEM{<:LobattoLegendreBasis}, cache)292@unpack neighbor_ids, node_indices = cache.interfaces293# Take for Gauss-Lobatto-Legendre (GLL) the interface normals from the outer volume nodes, i.e.,294# element data.295@unpack contravariant_vectors = cache.elements296index_range = eachnode(dg)297298@threaded for interface in eachinterface(dg, cache)299calc_interface_flux_per_interface!(surface_flux_values, typeof(mesh),300have_nonconservative_terms,301equations, surface_integral, typeof(dg),302cache.interfaces.u, interface,303neighbor_ids, node_indices,304contravariant_vectors, index_range)305end306307return nothing308end309310function calc_interface_flux!(backend::Backend, surface_flux_values,311mesh::Union{P4estMesh{2}, P4estMeshView{2},312T8codeMesh{2}},313have_nonconservative_terms,314equations, surface_integral,315dg::DGSEM{<:LobattoLegendreBasis}, cache)316ninterfaces(cache.interfaces) == 0 && return nothing317@unpack neighbor_ids, node_indices = cache.interfaces318@unpack contravariant_vectors = cache.elements319index_range = eachnode(dg)320321kernel! = calc_interface_flux_KAkernel!(backend)322kernel!(surface_flux_values, typeof(mesh), have_nonconservative_terms,323equations, surface_integral, typeof(dg), cache.interfaces.u,324neighbor_ids, node_indices, contravariant_vectors, index_range,325ndrange = ninterfaces(cache.interfaces))326327return nothing328end329330@kernel function calc_interface_flux_KAkernel!(surface_flux_values,331MeshT::Type{<:Union{P4estMesh{2},332P4estMeshView{2},333T8codeMesh{2}}},334have_nonconservative_terms,335equations, surface_integral,336SolverT::Type{<:DG}, u_interface,337neighbor_ids, node_indices,338contravariant_vectors, index_range)339interface = @index(Global)340calc_interface_flux_per_interface!(surface_flux_values, MeshT,341have_nonconservative_terms, equations,342surface_integral, SolverT, u_interface,343interface, neighbor_ids, node_indices,344contravariant_vectors, index_range)345end346347@inline function calc_interface_flux_per_interface!(surface_flux_values,348MeshT::Type{<:Union{P4estMesh{2},349P4estMeshView{2},350T8codeMesh{2}}},351have_nonconservative_terms,352equations, surface_integral,353SolverT::Type{<:DGSEM{<:LobattoLegendreBasis}},354u_interface, interface,355neighbor_ids,356node_indices, contravariant_vectors,357index_range)358index_end = last(index_range)359360# Get element and side index information on the primary element361primary_element = neighbor_ids[1, interface]362primary_indices = node_indices[1, interface]363primary_direction = indices2direction(primary_indices)364365# Create the local i,j indexing on the primary element used to pull normal direction information366i_primary_start, i_primary_step = index_to_start_step_2d(primary_indices[1],367index_range)368j_primary_start, j_primary_step = index_to_start_step_2d(primary_indices[2],369index_range)370371i_primary = i_primary_start372j_primary = j_primary_start373374# Get element and side index information on the secondary element375secondary_element = neighbor_ids[2, interface]376secondary_indices = node_indices[2, interface]377secondary_direction = indices2direction(secondary_indices)378379# Initiate the secondary index to be used in the surface for loop.380# This index on the primary side will always run forward but381# the secondary index might need to run backwards for flipped sides.382if :i_backward in secondary_indices383node_secondary = index_end384node_secondary_step = -1385else386node_secondary = 1387node_secondary_step = 1388end389390for node in index_range391# Get the normal direction on the primary element.392# Contravariant vectors at interfaces in negative coordinate direction393# are pointing inwards. This is handled by `get_normal_direction`.394normal_direction = get_normal_direction(primary_direction,395contravariant_vectors,396i_primary, j_primary,397primary_element)398399calc_interface_flux!(surface_flux_values, MeshT, have_nonconservative_terms,400equations, surface_integral, SolverT, u_interface,401interface, normal_direction, node,402primary_direction, primary_element,403node_secondary,404secondary_direction, secondary_element)405406# Increment primary element indices to pull the normal direction407i_primary += i_primary_step408j_primary += j_primary_step409# Increment the surface node index along the secondary element410node_secondary += node_secondary_step411end412413return nothing414end415416function calc_interface_flux!(backend::Nothing, surface_flux_values,417mesh::Union{P4estMesh{2}, P4estMeshView{2}},418have_nonconservative_terms,419equations, surface_integral,420dg::DGSEM{<:GaussLegendreBasis}, cache)421@unpack neighbor_ids, node_indices = cache.interfaces422# Take for Gauss-Legendre (GL) the interface normals from the interfaces, i.e.,423# interface data.424@unpack normal_directions = cache.interfaces425index_range = eachnode(dg)426427@threaded for interface in eachinterface(dg, cache)428calc_interface_flux_per_interface!(surface_flux_values, typeof(mesh),429have_nonconservative_terms,430equations, surface_integral, typeof(dg),431cache.interfaces.u, interface,432neighbor_ids, node_indices,433normal_directions, index_range)434end435436return nothing437end438439@inline function calc_interface_flux_per_interface!(surface_flux_values,440MeshT::Type{<:Union{P4estMesh{2},441P4estMeshView{2}}},442have_nonconservative_terms,443equations, surface_integral,444SolverT::Type{<:DGSEM{<:GaussLegendreBasis}},445u_interface, interface,446neighbor_ids,447node_indices, normal_directions,448index_range)449index_end = last(index_range)450451# Get element and side index information on the primary element452primary_element = neighbor_ids[1, interface]453primary_indices = node_indices[1, interface]454primary_direction = indices2direction(primary_indices)455456# Get element and side index information on the secondary element457secondary_element = neighbor_ids[2, interface]458secondary_indices = node_indices[2, interface]459secondary_direction = indices2direction(secondary_indices)460461# Initiate the secondary index to be used in the surface for loop.462# This index on the primary side will always run forward but463# the secondary index might need to run backwards for flipped sides.464if :i_backward in secondary_indices465node_secondary = index_end466node_secondary_step = -1467else468node_secondary = 1469node_secondary_step = 1470end471472for node in index_range473# This seems to be faster than `@views normal_direction = normal_directions[:, node, interface]`474normal_direction = SVector(normal_directions[1, node, interface],475normal_directions[2, node, interface])476477calc_interface_flux!(surface_flux_values, MeshT, have_nonconservative_terms,478equations, surface_integral, SolverT, u_interface,479interface, normal_direction, node,480primary_direction, primary_element,481node_secondary,482secondary_direction, secondary_element)483484# Increment the surface node index along the secondary element485node_secondary += node_secondary_step486end487488return nothing489end490491# Inlined version of the interface flux computation for conservation laws492@inline function calc_interface_flux!(surface_flux_values,493::Type{<:Union{P4estMesh{2},494P4estMeshView{2},495T8codeMesh{2}}},496have_nonconservative_terms::False, equations,497surface_integral, SolverT::Type{<:DG},498u_interface, interface_index,499normal_direction, primary_node_index,500primary_direction_index,501primary_element_index,502secondary_node_index,503secondary_direction_index,504secondary_element_index)505@unpack surface_flux = surface_integral506507u_ll, u_rr = get_surface_node_vars(u_interface, equations, SolverT,508primary_node_index,509interface_index)510511flux_ = surface_flux(u_ll, u_rr, normal_direction, equations)512513for v in eachvariable(equations)514surface_flux_values[v, primary_node_index, primary_direction_index, primary_element_index] = flux_[v]515surface_flux_values[v, secondary_node_index, secondary_direction_index, secondary_element_index] = -flux_[v]516end517518return nothing519end520521# Inlined version of the interface flux computation for equations with conservative and nonconservative terms522@inline function calc_interface_flux!(surface_flux_values,523MeshT::Type{<:Union{P4estMesh{2}, T8codeMesh{2}}},524have_nonconservative_terms::True, equations,525surface_integral, SolverT::Type{<:DG},526u_interface, interface_index,527normal_direction, primary_node_index,528primary_direction_index,529primary_element_index,530secondary_node_index,531secondary_direction_index,532secondary_element_index)533@unpack surface_flux = surface_integral534calc_interface_flux!(surface_flux_values, MeshT, have_nonconservative_terms,535combine_conservative_and_nonconservative_fluxes(surface_flux,536equations),537equations,538surface_integral, SolverT, u_interface,539interface_index, normal_direction,540primary_node_index, primary_direction_index,541primary_element_index,542secondary_node_index, secondary_direction_index,543secondary_element_index)544return nothing545end546547@inline function calc_interface_flux!(surface_flux_values,548::Type{<:Union{P4estMesh{2}, T8codeMesh{2}}},549have_nonconservative_terms::True,550combine_conservative_and_nonconservative_fluxes::False,551equations,552surface_integral, SolverT::Type{<:DG},553u_interface, interface_index, normal_direction,554primary_node_index, primary_direction_index,555primary_element_index,556secondary_node_index, secondary_direction_index,557secondary_element_index)558surface_flux, nonconservative_flux = surface_integral.surface_flux559560u_ll, u_rr = get_surface_node_vars(u_interface, equations, SolverT,561primary_node_index, interface_index)562563flux_ = surface_flux(u_ll, u_rr, normal_direction, equations)564565# Compute both nonconservative fluxes566noncons_primary = nonconservative_flux(u_ll, u_rr, normal_direction, equations)567noncons_secondary = nonconservative_flux(u_rr, u_ll, normal_direction, equations)568569# Store the flux with nonconservative terms on the primary and secondary elements570for v in eachvariable(equations)571# Note the factor 0.5 necessary for the nonconservative fluxes based on572# the interpretation of global SBP operators coupled discontinuously via573# central fluxes/SATs574surface_flux_values[v, primary_node_index, primary_direction_index, primary_element_index] = flux_[v] +5750.5f0 *576noncons_primary[v]577surface_flux_values[v, secondary_node_index, secondary_direction_index, secondary_element_index] = -(flux_[v] +5780.5f0 *579noncons_secondary[v])580end581582return nothing583end584585@inline function calc_interface_flux!(surface_flux_values,586::Type{<:Union{P4estMesh{2}, T8codeMesh{2}}},587have_nonconservative_terms::True,588combine_conservative_and_nonconservative_fluxes::True,589equations,590surface_integral, SolverT::Type{<:DG},591u_interface, interface_index, normal_direction,592primary_node_index, primary_direction_index,593primary_element_index,594secondary_node_index, secondary_direction_index,595secondary_element_index)596@unpack surface_flux = surface_integral597598u_ll, u_rr = get_surface_node_vars(u_interface, equations, SolverT,599primary_node_index, interface_index)600601flux_left, flux_right = surface_flux(u_ll, u_rr, normal_direction, equations)602603for v in eachvariable(equations)604surface_flux_values[v, primary_node_index, primary_direction_index, primary_element_index] = flux_left[v]605surface_flux_values[v, secondary_node_index, secondary_direction_index, secondary_element_index] = -flux_right[v]606end607608return nothing609end610611function prolong2boundaries!(cache, u,612mesh::Union{P4estMesh{2}, P4estMeshView{2}, T8codeMesh{2}},613equations, dg::DG)614@unpack boundaries = cache615index_range = eachnode(dg)616617@threaded for boundary in eachboundary(dg, cache)618# Copy solution data from the element using "delayed indexing" with619# a start value and a step size to get the correct face and orientation.620element = boundaries.neighbor_ids[boundary]621node_indices = boundaries.node_indices[boundary]622623i_node_start, i_node_step = index_to_start_step_2d(node_indices[1], index_range)624j_node_start, j_node_step = index_to_start_step_2d(node_indices[2], index_range)625626i_node = i_node_start627j_node = j_node_start628for i in eachnode(dg)629for v in eachvariable(equations)630boundaries.u[v, i, boundary] = u[v, i_node, j_node, element]631end632i_node += i_node_step633j_node += j_node_step634end635end636637return nothing638end639640# We require this function definition, as the function calls for the641# coupled simulations pass the u_parent variable642# Note: Since the implementation is identical, we forward to the original function643function prolong2boundaries!(cache, u, u_parent, semis,644mesh::P4estMeshView{2},645equations, surface_integral, dg::DG)646return prolong2boundaries!(cache, u, mesh, equations, dg)647end648649function calc_boundary_flux!(cache, t, boundary_condition::BC, boundary_indexing,650mesh::Union{P4estMesh{2}, T8codeMesh{2}},651equations, surface_integral, dg::DG) where {BC}652@unpack boundaries = cache653@unpack surface_flux_values = cache.elements654index_range = eachnode(dg)655656@threaded for local_index in eachindex(boundary_indexing)657# Use the local index to get the global boundary index from the pre-sorted list658boundary = boundary_indexing[local_index]659660# Get information on the adjacent element, compute the surface fluxes,661# and store them662element = boundaries.neighbor_ids[boundary]663node_indices = boundaries.node_indices[boundary]664direction = indices2direction(node_indices)665666i_node_start, i_node_step = index_to_start_step_2d(node_indices[1], index_range)667j_node_start, j_node_step = index_to_start_step_2d(node_indices[2], index_range)668669i_node = i_node_start670j_node = j_node_start671for node in eachnode(dg)672calc_boundary_flux!(surface_flux_values, t, boundary_condition,673mesh, have_nonconservative_terms(equations),674equations, surface_integral, dg, cache,675i_node, j_node,676node, direction, element, boundary)677678i_node += i_node_step679j_node += j_node_step680end681end682683return nothing684end685686# inlined version of the boundary flux calculation along a physical interface687@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,688mesh::Union{P4estMesh{2}, T8codeMesh{2}},689have_nonconservative_terms::False, equations,690surface_integral, dg::DG, cache,691i_index, j_index,692node_index, direction_index, element_index,693boundary_index)694@unpack boundaries = cache695@unpack node_coordinates, contravariant_vectors = cache.elements696@unpack surface_flux = surface_integral697698# Extract solution data from boundary container699u_inner = get_node_vars(boundaries.u, equations, dg, node_index, boundary_index)700701# Outward-pointing normal direction (not normalized)702normal_direction = get_normal_direction(direction_index, contravariant_vectors,703i_index, j_index, element_index)704705# Coordinates at boundary node706x = get_node_coords(node_coordinates, equations, dg,707i_index, j_index, element_index)708709flux_ = boundary_condition(u_inner, normal_direction, x, t, surface_flux, equations)710711# Copy flux to element storage in the correct orientation712for v in eachvariable(equations)713surface_flux_values[v, node_index, direction_index, element_index] = flux_[v]714end715716return nothing717end718719# inlined version of the boundary flux calculation along a physical interface720@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,721mesh::P4estMeshView{2},722nonconservative_terms::False, equations,723surface_integral, dg::DG, cache,724i_index, j_index,725node_index, direction_index, element_index,726boundary_index, u_parent)727@unpack boundaries = cache728@unpack contravariant_vectors = cache.elements729@unpack surface_flux = surface_integral730731# Extract solution data from boundary container732u_inner = get_node_vars(boundaries.u, equations, dg, node_index, boundary_index)733734# Outward-pointing normal direction (not normalized)735normal_direction = get_normal_direction(direction_index, contravariant_vectors,736i_index, j_index, element_index)737738flux_ = boundary_condition(u_inner, mesh, equations, cache, i_index, j_index,739element_index, normal_direction, surface_flux,740normal_direction, u_parent)741742# Copy flux to element storage in the correct orientation743for v in eachvariable(equations)744surface_flux_values[v, node_index, direction_index, element_index] = flux_[v]745end746return nothing747end748749# inlined version of the boundary flux with nonconservative terms calculation along a physical interface750@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,751mesh::Union{P4estMesh{2}, T8codeMesh{2}},752have_nonconservative_terms::True, equations,753surface_integral, dg::DG, cache,754i_index, j_index,755node_index, direction_index, element_index,756boundary_index)757@unpack surface_flux = surface_integral758calc_boundary_flux!(surface_flux_values, t, boundary_condition,759mesh, have_nonconservative_terms,760combine_conservative_and_nonconservative_fluxes(surface_flux,761equations),762equations, surface_integral, dg, cache,763i_index, j_index,764node_index, direction_index, element_index, boundary_index)765return nothing766end767768@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,769mesh::Union{P4estMesh{2}, T8codeMesh{2}},770have_nonconservative_terms::True,771combine_conservative_and_nonconservative_fluxes::False,772equations,773surface_integral, dg::DG, cache,774i_index, j_index,775node_index, direction_index, element_index,776boundary_index)777@unpack boundaries = cache778@unpack node_coordinates, contravariant_vectors = cache.elements779780# Extract solution data from boundary container781u_inner = get_node_vars(boundaries.u, equations, dg, node_index, boundary_index)782783# Outward-pointing normal direction (not normalized)784normal_direction = get_normal_direction(direction_index, contravariant_vectors,785i_index, j_index, element_index)786787# Coordinates at boundary node788x = get_node_coords(node_coordinates, equations, dg,789i_index, j_index, element_index)790791# Call pointwise numerical flux functions for the conservative and nonconservative part792# in the normal direction on the boundary793flux, noncons_flux = boundary_condition(u_inner, normal_direction, x, t,794surface_integral.surface_flux, equations)795796# Copy flux to element storage in the correct orientation797for v in eachvariable(equations)798# Note the factor 0.5 necessary for the nonconservative fluxes based on799# the interpretation of global SBP operators coupled discontinuously via800# central fluxes/SATs801surface_flux_values[v, node_index, direction_index, element_index] = flux[v] +8020.5f0 *803noncons_flux[v]804end805806return nothing807end808809@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,810mesh::Union{P4estMesh{2}, T8codeMesh{2}},811have_nonconservative_terms::True,812combine_conservative_and_nonconservative_fluxes::True,813equations,814surface_integral, dg::DG, cache,815i_index, j_index,816node_index, direction_index, element_index,817boundary_index)818@unpack boundaries = cache819@unpack node_coordinates, contravariant_vectors = cache.elements820821# Extract solution data from boundary container822u_inner = get_node_vars(boundaries.u, equations, dg, node_index, boundary_index)823824# Outward-pointing normal direction (not normalized)825normal_direction = get_normal_direction(direction_index, contravariant_vectors,826i_index, j_index, element_index)827828# Coordinates at boundary node829x = get_node_coords(node_coordinates, equations, dg,830i_index, j_index, element_index)831832# Call pointwise numerical flux functions for the conservative and nonconservative part833# in the normal direction on the boundary834flux = boundary_condition(u_inner, normal_direction, x, t,835surface_integral.surface_flux, equations)836837# Copy flux to element storage in the correct orientation838for v in eachvariable(equations)839surface_flux_values[v, node_index, direction_index, element_index] = flux[v]840end841842return nothing843end844845# Function barrier for type stability846function calc_boundary_flux!(cache, t, boundary_conditions,847mesh::P4estMeshView,848equations, surface_integral, dg::DG, u_parent)849@unpack boundary_condition_types, boundary_indices = boundary_conditions850851calc_boundary_flux_by_type!(cache, t, boundary_condition_types, boundary_indices,852mesh, equations, surface_integral, dg, u_parent)853return nothing854end855856function prolong2mortars!(cache, u,857mesh::Union{P4estMesh{2}, P4estMeshView{2}, T8codeMesh{2}},858equations,859mortar_l2::LobattoLegendreMortarL2,860dg::DGSEM)861@unpack neighbor_ids, node_indices = cache.mortars862index_range = eachnode(dg)863864@threaded for mortar in eachmortar(dg, cache)865# Copy solution data from the small elements using "delayed indexing" with866# a start value and a step size to get the correct face and orientation.867small_indices = node_indices[1, mortar]868869i_small_start, i_small_step = index_to_start_step_2d(small_indices[1],870index_range)871j_small_start, j_small_step = index_to_start_step_2d(small_indices[2],872index_range)873874for position in 1:2875i_small = i_small_start876j_small = j_small_start877element = neighbor_ids[position, mortar]878for i in eachnode(dg)879for v in eachvariable(equations)880cache.mortars.u[1, v, position, i, mortar] = u[v, i_small, j_small,881element]882end883i_small += i_small_step884j_small += j_small_step885end886end887888# Buffer to copy solution values of the large element in the correct orientation889# before interpolating890u_buffer = cache.u_threaded[Threads.threadid()]891892# Copy solution of large element face to buffer in the893# correct orientation894large_indices = node_indices[2, mortar]895896i_large_start, i_large_step = index_to_start_step_2d(large_indices[1],897index_range)898j_large_start, j_large_step = index_to_start_step_2d(large_indices[2],899index_range)900901i_large = i_large_start902j_large = j_large_start903element = neighbor_ids[3, mortar]904for i in eachnode(dg)905for v in eachvariable(equations)906u_buffer[v, i] = u[v, i_large, j_large, element]907end908i_large += i_large_step909j_large += j_large_step910end911912# Interpolate large element face data from buffer to small face locations913multiply_dimensionwise!(view(cache.mortars.u, 2, :, 1, :, mortar),914mortar_l2.forward_lower,915u_buffer)916multiply_dimensionwise!(view(cache.mortars.u, 2, :, 2, :, mortar),917mortar_l2.forward_upper,918u_buffer)919end920921return nothing922end923924function calc_mortar_flux!(surface_flux_values,925mesh::Union{P4estMesh{2}, P4estMeshView{2}, T8codeMesh{2}},926have_nonconservative_terms, equations,927mortar_l2::LobattoLegendreMortarL2,928surface_integral, dg::DG, cache)929@unpack neighbor_ids, node_indices = cache.mortars930@unpack contravariant_vectors = cache.elements931@unpack (fstar_primary_upper_threaded, fstar_primary_lower_threaded,932fstar_secondary_upper_threaded, fstar_secondary_lower_threaded) = cache933index_range = eachnode(dg)934935@threaded for mortar in eachmortar(dg, cache)936# Choose thread-specific pre-allocated container937fstar_primary = (fstar_primary_lower_threaded[Threads.threadid()],938fstar_primary_upper_threaded[Threads.threadid()])939940fstar_secondary = (fstar_secondary_lower_threaded[Threads.threadid()],941fstar_secondary_upper_threaded[Threads.threadid()])942943# Get index information on the small elements944small_indices = node_indices[1, mortar]945small_direction = indices2direction(small_indices)946947i_small_start, i_small_step = index_to_start_step_2d(small_indices[1],948index_range)949j_small_start, j_small_step = index_to_start_step_2d(small_indices[2],950index_range)951952for position in 1:2953i_small = i_small_start954j_small = j_small_start955element = neighbor_ids[position, mortar]956for node in eachnode(dg)957# Get the normal direction on the small element.958# Note, contravariant vectors at interfaces in negative coordinate direction959# are pointing inwards. This is handled by `get_normal_direction`.960normal_direction = get_normal_direction(small_direction,961contravariant_vectors,962i_small, j_small, element)963964calc_mortar_flux!(fstar_primary, fstar_secondary,965mesh, have_nonconservative_terms, equations,966surface_integral, dg, cache,967mortar, position, normal_direction,968node)969970i_small += i_small_step971j_small += j_small_step972end973end974975# Buffer to interpolate flux values of the large element to before976# copying in the correct orientation977u_buffer = cache.u_threaded[Threads.threadid()]978979# in calc_interface_flux!, the interface flux is computed once over each980# interface using the normal from the "primary" element. The result is then981# passed back to the "secondary" element, flipping the sign to account for the982# change in the normal direction. For mortars, this sign flip occurs in983# "mortar_fluxes_to_elements!" instead.984mortar_fluxes_to_elements!(surface_flux_values,985mesh, equations, mortar_l2, dg, cache,986mortar, fstar_primary, fstar_secondary,987u_buffer)988end989990return nothing991end992993# Inlined version of the mortar flux computation on small elements for conservation laws994@inline function calc_mortar_flux!(fstar_primary, fstar_secondary,995mesh::Union{P4estMesh{2}, T8codeMesh{2}},996have_nonconservative_terms::False, equations,997surface_integral, dg::DG, cache,998mortar_index, position_index, normal_direction,999node_index)1000@unpack u = cache.mortars1001@unpack surface_flux = surface_integral10021003u_ll, u_rr = get_surface_node_vars(u, equations, dg, position_index,1004node_index, mortar_index)10051006flux = surface_flux(u_ll, u_rr, normal_direction, equations)10071008# Copy flux to buffer1009set_node_vars!(fstar_primary[position_index], flux, equations, dg, node_index)1010set_node_vars!(fstar_secondary[position_index], flux, equations, dg, node_index)10111012return nothing1013end10141015# Inlined version of the mortar flux computation on small elements for equations with conservative and1016# nonconservative terms1017@inline function calc_mortar_flux!(fstar_primary, fstar_secondary,1018mesh::Union{P4estMesh{2}, T8codeMesh{2}},1019have_nonconservative_terms::True, equations,1020surface_integral, dg::DG, cache,1021mortar_index, position_index, normal_direction,1022node_index)1023@unpack u = cache.mortars1024surface_flux, nonconservative_flux = surface_integral.surface_flux10251026u_ll, u_rr = get_surface_node_vars(u, equations, dg, position_index,1027node_index, mortar_index)10281029# Compute conservative flux1030flux = surface_flux(u_ll, u_rr, normal_direction, equations)10311032# Compute nonconservative flux and add it to the conservative flux.1033# The nonconservative flux is scaled by a factor of 0.5 based on1034# the interpretation of global SBP operators coupled discontinuously via1035# central fluxes/SATs1036noncons_primary = nonconservative_flux(u_ll, u_rr, normal_direction, equations)1037noncons_secondary = nonconservative_flux(u_rr, u_ll, normal_direction, equations)10381039flux_plus_noncons_primary = flux + 0.5f0 * noncons_primary1040flux_plus_noncons_secondary = flux + 0.5f0 * noncons_secondary10411042# Copy to buffer1043set_node_vars!(fstar_primary[position_index], flux_plus_noncons_primary, equations,1044dg, node_index)1045set_node_vars!(fstar_secondary[position_index], flux_plus_noncons_secondary,1046equations, dg, node_index)10471048return nothing1049end10501051@inline function mortar_fluxes_to_elements!(surface_flux_values,1052mesh::Union{P4estMesh{2}, T8codeMesh{2}},1053equations,1054mortar_l2::LobattoLegendreMortarL2,1055dg::DGSEM, cache, mortar, fstar_primary,1056fstar_secondary, u_buffer)1057@unpack neighbor_ids, node_indices = cache.mortars10581059# Copy solution small to small1060small_indices = node_indices[1, mortar]1061small_direction = indices2direction(small_indices)10621063for position in 1:21064element = neighbor_ids[position, mortar]1065for i in eachnode(dg)1066for v in eachvariable(equations)1067surface_flux_values[v, i, small_direction, element] = fstar_primary[position][v,1068i]1069end1070end1071end10721073# Project small fluxes to large element.1074multiply_dimensionwise!(u_buffer,1075mortar_l2.reverse_upper, fstar_secondary[2],1076mortar_l2.reverse_lower, fstar_secondary[1])10771078# The flux is calculated in the outward direction of the small elements,1079# so the sign must be switched to get the flux in outward direction1080# of the large element.1081# The contravariant vectors of the large element (and therefore the normal1082# vectors of the large element as well) are twice as large as the1083# contravariant vectors of the small elements. Therefore, the flux needs1084# to be scaled by a factor of 2 to obtain the flux of the large element.1085u_buffer .*= -210861087# Copy interpolated flux values from buffer to large element face in the1088# correct orientation.1089# Note that the index of the small sides will always run forward but1090# the index of the large side might need to run backwards for flipped sides.1091large_element = neighbor_ids[3, mortar]1092large_indices = node_indices[2, mortar]1093large_direction = indices2direction(large_indices)10941095if :i_backward in large_indices1096for i in eachnode(dg)1097for v in eachvariable(equations)1098surface_flux_values[v, end + 1 - i, large_direction, large_element] = u_buffer[v,1099i]1100end1101end1102else1103for i in eachnode(dg)1104for v in eachvariable(equations)1105surface_flux_values[v, i, large_direction, large_element] = u_buffer[v,1106i]1107end1108end1109end11101111return nothing1112end11131114function calc_surface_integral!(backend::Nothing, du, u,1115mesh::Union{P4estMesh{2}, P4estMeshView{2},1116T8codeMesh{2}},1117equations, surface_integral::SurfaceIntegralWeakForm,1118dg::DGSEM{<:LobattoLegendreBasis}, cache)1119@unpack inverse_weights = dg.basis1120@unpack surface_flux_values = cache.elements11211122@threaded for element in eachelement(dg, cache)1123calc_surface_integral_per_element!(du, typeof(mesh), equations,1124surface_integral, dg, inverse_weights[1],1125surface_flux_values, element)1126end1127end11281129function calc_surface_integral!(backend::Nothing, du, u,1130mesh::Union{P4estMesh{2}, P4estMeshView{2}},1131equations, surface_integral::SurfaceIntegralWeakForm,1132dg::DGSEM{<:GaussLegendreBasis}, cache)1133@unpack boundary_interpolation_inverse_weights = dg.basis1134@unpack surface_flux_values = cache.elements11351136@threaded for element in eachelement(dg, cache)1137calc_surface_integral_per_element!(du, typeof(mesh), equations,1138surface_integral, dg,1139boundary_interpolation_inverse_weights,1140surface_flux_values, element)1141end1142end11431144function calc_surface_integral!(backend::Backend, du, u,1145mesh::Union{P4estMesh{2}, P4estMeshView{2},1146T8codeMesh{2}},1147equations,1148surface_integral::SurfaceIntegralWeakForm,1149dg::DGSEM{<:LobattoLegendreBasis}, cache)1150nelements(dg, cache) == 0 && return nothing1151@unpack inverse_weights = dg.basis1152@unpack surface_flux_values = cache.elements11531154kernel! = calc_surface_integral_KAkernel!(backend)1155kernel!(du, typeof(mesh), equations, surface_integral, dg, inverse_weights[1],1156surface_flux_values, ndrange = nelements(dg, cache))1157return nothing1158end11591160@kernel function calc_surface_integral_KAkernel!(du,1161MeshT::Type{<:Union{P4estMesh{2},1162P4estMeshView{2},1163T8codeMesh{2}}},1164equations,1165surface_integral::SurfaceIntegralWeakForm,1166dg::DGSEM{<:LobattoLegendreBasis},1167factor,1168surface_flux_values)1169element = @index(Global)1170calc_surface_integral_per_element!(du, MeshT, equations, surface_integral,1171dg, factor, surface_flux_values, element)1172end11731174@inline function calc_surface_integral_per_element!(du,1175::Type{<:Union{P4estMesh{2},1176P4estMeshView{2},1177T8codeMesh{2}}},1178equations,1179surface_integral::SurfaceIntegralWeakForm,1180dg::DGSEM{<:LobattoLegendreBasis},1181factor,1182surface_flux_values, element)1183# Note that all fluxes have been computed with outward-pointing normal vectors.1184# This computes the **negative** surface integral contribution,1185# i.e., M^{-1} * boundary_interpolation^T (which is for Gauss-Lobatto DGSEM just M^{-1} * B)1186# and the missing "-" is taken care of by `apply_jacobian!`.1187#1188# We also use explicit assignments instead of `+=` to let `@muladd` turn these1189# into FMAs (see comment at the top of the file).1190#1191# factor = inverse_weights[1]1192# For LGL basis: Identical to weighted boundary interpolation at x = ±11193for l in eachnode(dg)1194for v in eachvariable(equations)1195# surface at -x1196du[v, 1, l, element] = (du[v, 1, l, element] +1197surface_flux_values[v, l, 1, element] *1198factor)11991200# surface at +x1201du[v, nnodes(dg), l, element] = (du[v, nnodes(dg), l, element] +1202surface_flux_values[v, l, 2, element] *1203factor)12041205# surface at -y1206du[v, l, 1, element] = (du[v, l, 1, element] +1207surface_flux_values[v, l, 3, element] *1208factor)12091210# surface at +y1211du[v, l, nnodes(dg), element] = (du[v, l, nnodes(dg), element] +1212surface_flux_values[v, l, 4, element] *1213factor)1214end1215end1216return nothing1217end12181219function calc_surface_integral_per_element!(du,1220::Type{<:Union{P4estMesh{2},1221P4estMeshView{2}}},1222equations,1223surface_integral::SurfaceIntegralWeakForm,1224dg::DGSEM{<:GaussLegendreBasis},1225boundary_interpolation_inverse_weights,1226surface_flux_values, element)1227# Note that all fluxes have been computed with outward-pointing normal vectors.1228# This computes the **negative** surface integral contribution,1229# i.e., M^{-1} * boundary_interpolation^T1230# and the missing "-" is taken care of by `apply_jacobian!`.1231#1232# We also use explicit assignments instead of `+=` to let `@muladd` turn these1233# into FMAs (see comment at the top of the file).1234for l in eachnode(dg)1235for v in eachvariable(equations)1236# Aliases for repeatedly accessed variables1237surface_flux_minus_x = surface_flux_values[v, l, 1, element]1238surface_flux_plus_x = surface_flux_values[v, l, 2, element]1239for ii in eachnode(dg)1240# surface at -x1241du[v, ii, l, element] = (du[v, ii, l, element] +1242surface_flux_minus_x *1243boundary_interpolation_inverse_weights[ii,12441])1245# surface at +x1246du[v, ii, l, element] = (du[v, ii, l, element] +1247surface_flux_plus_x *1248boundary_interpolation_inverse_weights[ii,12492])1250end12511252surface_flux_minus_y = surface_flux_values[v, l, 3, element]1253surface_flux_plus_y = surface_flux_values[v, l, 4, element]1254for jj in eachnode(dg)1255# surface at -y1256du[v, l, jj, element] = (du[v, l, jj, element] +1257surface_flux_minus_y *1258boundary_interpolation_inverse_weights[jj,12591])1260# surface at +y1261du[v, l, jj, element] = (du[v, l, jj, element] +1262surface_flux_plus_y *1263boundary_interpolation_inverse_weights[jj,12642])1265end1266end1267end12681269return nothing1270end12711272# Call this for coupled P4estMeshView simulations.1273# The coupling calculations (especially boundary conditions) require data from the parent mesh, which is why1274# the additional variable u_parent is needed, compared to non-coupled systems.1275function rhs!(du, u, t, u_parent, semis,1276mesh::P4estMeshView{2},1277equations,1278boundary_conditions, source_terms::Source,1279dg::DG, cache) where {Source}1280backend = nothing1281# Reset du1282@trixi_timeit timer() "reset ∂u/∂t" set_zero!(du, dg, cache)12831284# Calculate volume integral1285@trixi_timeit timer() "volume integral" begin1286calc_volume_integral!(backend, du, u, mesh,1287have_nonconservative_terms(equations), equations,1288dg.volume_integral, dg, cache)1289end12901291# Prolong solution to interfaces1292@trixi_timeit timer() "prolong2interfaces" begin1293prolong2interfaces!(backend, cache, u, mesh, equations, dg)1294end12951296# Calculate interface fluxes1297@trixi_timeit timer() "interface flux" begin1298calc_interface_flux!(backend, cache.elements.surface_flux_values, mesh,1299have_nonconservative_terms(equations), equations,1300dg.surface_integral, dg, cache)1301end13021303# Prolong solution to boundaries1304@trixi_timeit timer() "prolong2boundaries" begin1305prolong2boundaries!(cache, u, u_parent, semis, mesh, equations,1306dg.surface_integral, dg)1307end13081309# Calculate boundary fluxes1310@trixi_timeit timer() "boundary flux" begin1311calc_boundary_flux!(cache, t, boundary_conditions, mesh, equations,1312dg.surface_integral, dg, u_parent)1313end13141315# Prolong solution to mortars1316@trixi_timeit timer() "prolong2mortars" begin1317prolong2mortars!(cache, u, mesh, equations,1318dg.mortar, dg)1319end13201321# Calculate mortar fluxes1322@trixi_timeit timer() "mortar flux" begin1323calc_mortar_flux!(cache.elements.surface_flux_values, mesh,1324have_nonconservative_terms(equations), equations,1325dg.mortar, dg.surface_integral, dg, cache)1326end13271328# Calculate surface integrals1329@trixi_timeit timer() "surface integral" begin1330calc_surface_integral!(backend, du, u, mesh, equations,1331dg.surface_integral, dg, cache)1332end13331334# Apply Jacobian from mapping to reference element1335@trixi_timeit timer() "Jacobian" apply_jacobian!(backend, du, mesh, equations, dg,1336cache)13371338# Calculate source terms1339@trixi_timeit timer() "source terms" begin1340calc_sources!(du, u, t, source_terms, equations, dg, cache)1341end13421343return nothing1344end1345end # @muladd134613471348