Path: blob/main/src/solvers/dgsem_tree/dg_3d.jl
5591 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# everything related to a DG semidiscretization in 3D,8# currently limited to Lobatto-Legendre nodes910function create_f_threaded(mesh::AbstractMesh{3}, equations,11dg::DG, uEltype)12A4d = Array{uEltype, 4}1314f1_L_threaded = A4d[A4d(undef, nvariables(equations),15nnodes(dg) + 1, nnodes(dg), nnodes(dg))16for _ in 1:Threads.maxthreadid()]17f1_R_threaded = A4d[A4d(undef, nvariables(equations),18nnodes(dg) + 1, nnodes(dg), nnodes(dg))19for _ in 1:Threads.maxthreadid()]20f2_L_threaded = A4d[A4d(undef, nvariables(equations),21nnodes(dg), nnodes(dg) + 1, nnodes(dg))22for _ in 1:Threads.maxthreadid()]23f2_R_threaded = A4d[A4d(undef, nvariables(equations),24nnodes(dg), nnodes(dg) + 1, nnodes(dg))25for _ in 1:Threads.maxthreadid()]26f3_L_threaded = A4d[A4d(undef, nvariables(equations),27nnodes(dg), nnodes(dg), nnodes(dg) + 1)28for _ in 1:Threads.maxthreadid()]29f3_R_threaded = A4d[A4d(undef, nvariables(equations),30nnodes(dg), nnodes(dg), nnodes(dg) + 1)31for _ in 1:Threads.maxthreadid()]3233@threaded for t in eachindex(f1_L_threaded)34f1_L_threaded[t][:, 1, :, :] .= zero(uEltype)35f1_R_threaded[t][:, 1, :, :] .= zero(uEltype)36f1_L_threaded[t][:, nnodes(dg) + 1, :, :] .= zero(uEltype)37f1_R_threaded[t][:, nnodes(dg) + 1, :, :] .= zero(uEltype)3839f2_L_threaded[t][:, :, 1, :] .= zero(uEltype)40f2_R_threaded[t][:, :, 1, :] .= zero(uEltype)41f2_L_threaded[t][:, :, nnodes(dg) + 1, :] .= zero(uEltype)42f2_R_threaded[t][:, :, nnodes(dg) + 1, :] .= zero(uEltype)4344f3_L_threaded[t][:, :, :, 1] .= zero(uEltype)45f3_R_threaded[t][:, :, :, 1] .= zero(uEltype)46f3_L_threaded[t][:, :, :, nnodes(dg) + 1] .= zero(uEltype)47f3_R_threaded[t][:, :, :, nnodes(dg) + 1] .= zero(uEltype)48end4950return f1_L_threaded, f1_R_threaded,51f2_L_threaded, f2_R_threaded,52f3_L_threaded, f3_R_threaded53end5455function create_cache(mesh::TreeMesh{3},56equations,57volume_integral::AbstractVolumeIntegralSubcell,58dg::DG, cache_containers, uEltype)59fstar1_L_threaded, fstar1_R_threaded,60fstar2_L_threaded, fstar2_R_threaded,61fstar3_L_threaded, fstar3_R_threaded = create_f_threaded(mesh, equations, dg,62uEltype)6364cache_subcell_limiting = create_cache_subcell_limiting(mesh, equations,65volume_integral, dg,66cache_containers, uEltype)6768return (; fstar1_L_threaded, fstar1_R_threaded,69fstar2_L_threaded, fstar2_R_threaded,70fstar3_L_threaded, fstar3_R_threaded,71cache_subcell_limiting...)72end7374# The methods below are specialized on the mortar type75# and called from the basic `create_cache` method at the top.76function create_cache(mesh::TreeMesh{3}, equations,77mortar_l2::LobattoLegendreMortarL2, uEltype)78A3d = Array{uEltype, 3}79fstar_primary_upper_left_threaded = A3d[A3d(undef, nvariables(equations),80nnodes(mortar_l2),81nnodes(mortar_l2))82for _ in 1:Threads.maxthreadid()]83fstar_primary_upper_right_threaded = A3d[A3d(undef, nvariables(equations),84nnodes(mortar_l2),85nnodes(mortar_l2))86for _ in 1:Threads.maxthreadid()]87fstar_primary_lower_left_threaded = A3d[A3d(undef, nvariables(equations),88nnodes(mortar_l2),89nnodes(mortar_l2))90for _ in 1:Threads.maxthreadid()]91fstar_primary_lower_right_threaded = A3d[A3d(undef, nvariables(equations),92nnodes(mortar_l2),93nnodes(mortar_l2))94for _ in 1:Threads.maxthreadid()]95fstar_secondary_upper_left_threaded = A3d[A3d(undef, nvariables(equations),96nnodes(mortar_l2),97nnodes(mortar_l2))98for _ in 1:Threads.maxthreadid()]99fstar_secondary_upper_right_threaded = A3d[A3d(undef, nvariables(equations),100nnodes(mortar_l2),101nnodes(mortar_l2))102for _ in 1:Threads.maxthreadid()]103fstar_secondary_lower_left_threaded = A3d[A3d(undef, nvariables(equations),104nnodes(mortar_l2),105nnodes(mortar_l2))106for _ in 1:Threads.maxthreadid()]107fstar_secondary_lower_right_threaded = A3d[A3d(undef, nvariables(equations),108nnodes(mortar_l2),109nnodes(mortar_l2))110for _ in 1:Threads.maxthreadid()]111fstar_tmp1_threaded = A3d[A3d(undef, nvariables(equations),112nnodes(mortar_l2),113nnodes(mortar_l2))114for _ in 1:Threads.maxthreadid()]115116cache = (; fstar_primary_upper_left_threaded, fstar_primary_upper_right_threaded,117fstar_primary_lower_left_threaded, fstar_primary_lower_right_threaded,118fstar_secondary_upper_left_threaded, fstar_secondary_upper_right_threaded,119fstar_secondary_lower_left_threaded, fstar_secondary_lower_right_threaded,120fstar_tmp1_threaded)121122return cache123end124125#=126`weak_form_kernel!` is only implemented for conserved terms as127non-conservative terms should always be discretized in conjunction with a flux-splitting scheme,128see `flux_differencing_kernel!`.129This treatment is required to achieve, e.g., entropy-stability or well-balancedness.130See also https://github.com/trixi-framework/Trixi.jl/issues/1671#issuecomment-1765644064131=#132@inline function weak_form_kernel!(du, u,133element, ::Type{<:TreeMesh{3}},134have_nonconservative_terms::False, equations,135dg::DGSEM, cache, alpha = true)136# true * [some floating point value] == [exactly the same floating point value]137# This can (hopefully) be optimized away due to constant propagation.138@unpack derivative_hat = dg.basis139140for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)141u_node = get_node_vars(u, equations, dg, i, j, k, element)142143flux1 = flux(u_node, 1, equations)144for ii in eachnode(dg)145multiply_add_to_node_vars!(du, alpha * derivative_hat[ii, i], flux1,146equations, dg, ii, j, k, element)147end148149flux2 = flux(u_node, 2, equations)150for jj in eachnode(dg)151multiply_add_to_node_vars!(du, alpha * derivative_hat[jj, j], flux2,152equations, dg, i, jj, k, element)153end154155flux3 = flux(u_node, 3, equations)156for kk in eachnode(dg)157multiply_add_to_node_vars!(du, alpha * derivative_hat[kk, k], flux3,158equations, dg, i, j, kk, element)159end160end161162return nothing163end164165@inline function flux_differencing_kernel!(du, u, element, ::Type{<:TreeMesh{3}},166have_nonconservative_terms::False, equations,167volume_flux, dg::DGSEM, cache, alpha = true)168# true * [some floating point value] == [exactly the same floating point value]169# This can (hopefully) be optimized away due to constant propagation.170@unpack derivative_split = dg.basis171172# Calculate volume integral in one element173for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)174u_node = get_node_vars(u, equations, dg, i, j, k, element)175176# All diagonal entries of `derivative_split` are zero. Thus, we can skip177# the computation of the diagonal terms. In addition, we use the symmetry178# of the `volume_flux` to save half of the possible two-point flux179# computations.180181# x direction182for ii in (i + 1):nnodes(dg)183u_node_ii = get_node_vars(u, equations, dg, ii, j, k, element)184flux1 = volume_flux(u_node, u_node_ii, 1, equations)185multiply_add_to_node_vars!(du, alpha * derivative_split[i, ii], flux1,186equations, dg, i, j, k, element)187multiply_add_to_node_vars!(du, alpha * derivative_split[ii, i], flux1,188equations, dg, ii, j, k, element)189end190191# y direction192for jj in (j + 1):nnodes(dg)193u_node_jj = get_node_vars(u, equations, dg, i, jj, k, element)194flux2 = volume_flux(u_node, u_node_jj, 2, equations)195multiply_add_to_node_vars!(du, alpha * derivative_split[j, jj], flux2,196equations, dg, i, j, k, element)197multiply_add_to_node_vars!(du, alpha * derivative_split[jj, j], flux2,198equations, dg, i, jj, k, element)199end200201# z direction202for kk in (k + 1):nnodes(dg)203u_node_kk = get_node_vars(u, equations, dg, i, j, kk, element)204flux3 = volume_flux(u_node, u_node_kk, 3, equations)205multiply_add_to_node_vars!(du, alpha * derivative_split[k, kk], flux3,206equations, dg, i, j, k, element)207multiply_add_to_node_vars!(du, alpha * derivative_split[kk, k], flux3,208equations, dg, i, j, kk, element)209end210end211212return nothing213end214215@inline function flux_differencing_kernel!(du, u, element, MeshT::Type{<:TreeMesh{3}},216have_nonconservative_terms::True, equations,217volume_flux, dg::DGSEM, cache, alpha = true)218# true * [some floating point value] == [exactly the same floating point value]219# This can (hopefully) be optimized away due to constant propagation.220@unpack derivative_split = dg.basis221symmetric_flux, nonconservative_flux = volume_flux222223# Apply the symmetric flux as usual224flux_differencing_kernel!(du, u, element, MeshT, False(), equations, symmetric_flux,225dg, cache, alpha)226227# Calculate the remaining volume terms using the nonsymmetric generalized flux228for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)229u_node = get_node_vars(u, equations, dg, i, j, k, element)230231# The diagonal terms are zero since the diagonal of `derivative_split`232# is zero. We ignore this for now.233234# x direction235integral_contribution = zero(u_node)236for ii in eachnode(dg)237u_node_ii = get_node_vars(u, equations, dg, ii, j, k, element)238noncons_flux1 = nonconservative_flux(u_node, u_node_ii, 1, equations)239integral_contribution = integral_contribution +240derivative_split[i, ii] * noncons_flux1241end242243# y direction244for jj in eachnode(dg)245u_node_jj = get_node_vars(u, equations, dg, i, jj, k, element)246noncons_flux2 = nonconservative_flux(u_node, u_node_jj, 2, equations)247integral_contribution = integral_contribution +248derivative_split[j, jj] * noncons_flux2249end250251# z direction252for kk in eachnode(dg)253u_node_kk = get_node_vars(u, equations, dg, i, j, kk, element)254noncons_flux3 = nonconservative_flux(u_node, u_node_kk, 3, equations)255integral_contribution = integral_contribution +256derivative_split[k, kk] * noncons_flux3257end258259# The factor 0.5 cancels the factor 2 in the flux differencing form260multiply_add_to_node_vars!(du, alpha * 0.5f0, integral_contribution, equations,261dg, i, j, k, element)262end263264return nothing265end266267@inline function fv_kernel!(du, u,268MeshT::Type{<:Union{TreeMesh{3}, StructuredMesh{3},269P4estMesh{3},270T8codeMesh{3}}},271have_nonconservative_terms, equations,272volume_flux_fv, dg::DGSEM, cache, element, alpha = true)273@unpack fstar1_L_threaded, fstar1_R_threaded, fstar2_L_threaded, fstar2_R_threaded, fstar3_L_threaded, fstar3_R_threaded = cache274@unpack inverse_weights = dg.basis # Plays role of inverse DG-subcell sizes275276# Calculate FV two-point fluxes277fstar1_L = fstar1_L_threaded[Threads.threadid()]278fstar2_L = fstar2_L_threaded[Threads.threadid()]279fstar3_L = fstar3_L_threaded[Threads.threadid()]280fstar1_R = fstar1_R_threaded[Threads.threadid()]281fstar2_R = fstar2_R_threaded[Threads.threadid()]282fstar3_R = fstar3_R_threaded[Threads.threadid()]283284calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, fstar3_L, fstar3_R, u,285MeshT, have_nonconservative_terms, equations,286volume_flux_fv, dg, element, cache)287288# Calculate FV volume integral contribution289for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)290for v in eachvariable(equations)291du[v, i, j, k, element] += (alpha *292(inverse_weights[i] *293(fstar1_L[v, i + 1, j, k] -294fstar1_R[v, i, j, k]) +295inverse_weights[j] *296(fstar2_L[v, i, j + 1, k] -297fstar2_R[v, i, j, k]) +298inverse_weights[k] *299(fstar3_L[v, i, j, k + 1] -300fstar3_R[v, i, j, k])))301end302end303304return nothing305end306307@inline function fvO2_kernel!(du, u,308MeshT::Type{<:Union{TreeMesh{3}, StructuredMesh{3},309P4estMesh{3},310T8codeMesh{3}}},311have_nonconservative_terms, equations,312volume_flux_fv, dg::DGSEM, cache, element,313sc_interface_coords, reconstruction_mode, slope_limiter,314cons2recon, recon2cons,315alpha = true)316@unpack fstar1_L_threaded, fstar1_R_threaded,317fstar2_L_threaded, fstar2_R_threaded,318fstar3_L_threaded, fstar3_R_threaded = cache319@unpack inverse_weights = dg.basis # Plays role of inverse DG-subcell sizes320321# Calculate FV two-point fluxes322fstar1_L = fstar1_L_threaded[Threads.threadid()]323fstar2_L = fstar2_L_threaded[Threads.threadid()]324fstar3_L = fstar3_L_threaded[Threads.threadid()]325fstar1_R = fstar1_R_threaded[Threads.threadid()]326fstar2_R = fstar2_R_threaded[Threads.threadid()]327fstar3_R = fstar3_R_threaded[Threads.threadid()]328329calcflux_fvO2!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, fstar3_L, fstar3_R, u,330MeshT, have_nonconservative_terms, equations,331volume_flux_fv, dg, element, cache,332sc_interface_coords, reconstruction_mode, slope_limiter,333cons2recon, recon2cons)334335# Calculate FV volume integral contribution336for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)337for v in eachvariable(equations)338du[v, i, j, k, element] += (alpha *339(inverse_weights[i] *340(fstar1_L[v, i + 1, j, k] -341fstar1_R[v, i, j, k]) +342inverse_weights[j] *343(fstar2_L[v, i, j + 1, k] -344fstar2_R[v, i, j, k]) +345inverse_weights[k] *346(fstar3_L[v, i, j, k + 1] -347fstar3_R[v, i, j, k])))348end349end350351return nothing352end353354# Compute the normal flux for the FV method on cartesian subcells, see355# Hennemann, Rueda-Ramírez, Hindenlang, Gassner (2020)356# "A provably entropy stable subcell shock capturing approach for high order split form DG for the compressible Euler equations"357# [arXiv: 2008.12044v2](https://arxiv.org/pdf/2008.12044)358@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R,359fstar3_L, fstar3_R, u,360::Type{<:TreeMesh{3}}, have_nonconservative_terms::False,361equations,362volume_flux_fv, dg::DGSEM, element, cache)363for k in eachnode(dg), j in eachnode(dg), i in 2:nnodes(dg)364u_ll = get_node_vars(u, equations, dg, i - 1, j, k, element)365u_rr = get_node_vars(u, equations, dg, i, j, k, element)366flux = volume_flux_fv(u_ll, u_rr, 1, equations) # orientation 1: x direction367set_node_vars!(fstar1_L, flux, equations, dg, i, j, k)368set_node_vars!(fstar1_R, flux, equations, dg, i, j, k)369end370371for k in eachnode(dg), j in 2:nnodes(dg), i in eachnode(dg)372u_ll = get_node_vars(u, equations, dg, i, j - 1, k, element)373u_rr = get_node_vars(u, equations, dg, i, j, k, element)374flux = volume_flux_fv(u_ll, u_rr, 2, equations) # orientation 2: y direction375set_node_vars!(fstar2_L, flux, equations, dg, i, j, k)376set_node_vars!(fstar2_R, flux, equations, dg, i, j, k)377end378379for k in 2:nnodes(dg), j in eachnode(dg), i in eachnode(dg)380u_ll = get_node_vars(u, equations, dg, i, j, k - 1, element)381u_rr = get_node_vars(u, equations, dg, i, j, k, element)382flux = volume_flux_fv(u_ll, u_rr, 3, equations) # orientation 3: z direction383set_node_vars!(fstar3_L, flux, equations, dg, i, j, k)384set_node_vars!(fstar3_R, flux, equations, dg, i, j, k)385end386387return nothing388end389390@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R,391fstar3_L, fstar3_R, u,392::Type{<:TreeMesh{3}},393have_nonconservative_terms::True, equations,394volume_flux_fv, dg::DGSEM, element, cache)395volume_flux, nonconservative_flux = volume_flux_fv396397for k in eachnode(dg), j in eachnode(dg), i in 2:nnodes(dg)398u_ll = get_node_vars(u, equations, dg, i - 1, j, k, element)399u_rr = get_node_vars(u, equations, dg, i, j, k, element)400401# Compute conservative part402flux = volume_flux(u_ll, u_rr, 1, equations) # orientation 1: x direction403404# Compute nonconservative part405# Note the factor 0.5 necessary for the nonconservative fluxes based on406# the interpretation of global SBP operators coupled discontinuously via407# central fluxes/SATs408flux_L = flux + 0.5f0 * nonconservative_flux(u_ll, u_rr, 1, equations)409flux_R = flux + 0.5f0 * nonconservative_flux(u_rr, u_ll, 1, equations)410411set_node_vars!(fstar1_L, flux_L, equations, dg, i, j, k)412set_node_vars!(fstar1_R, flux_R, equations, dg, i, j, k)413end414415for k in eachnode(dg), j in 2:nnodes(dg), i in eachnode(dg)416u_ll = get_node_vars(u, equations, dg, i, j - 1, k, element)417u_rr = get_node_vars(u, equations, dg, i, j, k, element)418419# Compute conservative part420flux = volume_flux(u_ll, u_rr, 2, equations) # orientation 2: y direction421422# Compute nonconservative part423# Note the factor 0.5 necessary for the nonconservative fluxes based on424# the interpretation of global SBP operators coupled discontinuously via425# central fluxes/SATs426flux_L = flux + 0.5f0 * nonconservative_flux(u_ll, u_rr, 2, equations)427flux_R = flux + 0.5f0 * nonconservative_flux(u_rr, u_ll, 2, equations)428429set_node_vars!(fstar2_L, flux_L, equations, dg, i, j, k)430set_node_vars!(fstar2_R, flux_R, equations, dg, i, j, k)431end432433for k in 2:nnodes(dg), j in eachnode(dg), i in eachnode(dg)434u_ll = get_node_vars(u, equations, dg, i, j, k - 1, element)435u_rr = get_node_vars(u, equations, dg, i, j, k, element)436437# Compute conservative part438flux = volume_flux(u_ll, u_rr, 3, equations) # orientation 3: z direction439440# Compute nonconservative part441# Note the factor 0.5 necessary for the nonconservative fluxes based on442# the interpretation of global SBP operators coupled discontinuously via443# central fluxes/SATs444flux_L = flux + 0.5f0 * nonconservative_flux(u_ll, u_rr, 3, equations)445flux_R = flux + 0.5f0 * nonconservative_flux(u_rr, u_ll, 3, equations)446447set_node_vars!(fstar3_L, flux_L, equations, dg, i, j, k)448set_node_vars!(fstar3_R, flux_R, equations, dg, i, j, k)449end450451return nothing452end453454@inline function calcflux_fvO2!(fstar1_L, fstar1_R, fstar2_L, fstar2_R,455fstar3_L, fstar3_R, u,456::Type{<:TreeMesh{3}},457have_nonconservative_terms::False,458equations,459volume_flux_fv, dg::DGSEM, element, cache,460sc_interface_coords, reconstruction_mode, slope_limiter,461cons2recon, recon2cons)462for k in eachnode(dg), j in eachnode(dg), i in 2:nnodes(dg)463u_ll = cons2recon(get_node_vars(u, equations, dg, max(1, i - 2), j, k, element),464equations)465u_lr = cons2recon(get_node_vars(u, equations, dg, i - 1, j, k, element),466equations)467u_rl = cons2recon(get_node_vars(u, equations, dg, i, j, k, element),468equations)469470u_rr = cons2recon(get_node_vars(u, equations, dg, min(nnodes(dg), i + 1), j, k,471element), equations)472473u_l, u_r = reconstruction_mode(u_ll, u_lr, u_rl, u_rr,474sc_interface_coords, i,475slope_limiter, dg)476477flux = volume_flux_fv(recon2cons(u_l, equations), recon2cons(u_r, equations),4781, equations) # orientation 1: x direction479480set_node_vars!(fstar1_L, flux, equations, dg, i, j, k)481set_node_vars!(fstar1_R, flux, equations, dg, i, j, k)482end483484for k in eachnode(dg), j in 2:nnodes(dg), i in eachnode(dg)485u_ll = cons2recon(get_node_vars(u, equations, dg, i, max(1, j - 2), k, element),486equations)487u_lr = cons2recon(get_node_vars(u, equations, dg, i, j - 1, k, element),488equations)489u_rl = cons2recon(get_node_vars(u, equations, dg, i, j, k, element),490equations)491u_rr = cons2recon(get_node_vars(u, equations, dg, i, min(nnodes(dg), j + 1), k,492element), equations)493494u_l, u_r = reconstruction_mode(u_ll, u_lr, u_rl, u_rr,495sc_interface_coords, j,496slope_limiter, dg)497498flux = volume_flux_fv(recon2cons(u_l, equations), recon2cons(u_r, equations),4992, equations) # orientation 2: y direction500501set_node_vars!(fstar2_L, flux, equations, dg, i, j, k)502set_node_vars!(fstar2_R, flux, equations, dg, i, j, k)503end504505for k in 2:nnodes(dg), j in eachnode(dg), i in eachnode(dg)506u_ll = cons2recon(get_node_vars(u, equations, dg, i, j, max(1, k - 2), element),507equations)508u_lr = cons2recon(get_node_vars(u, equations, dg, i, j, k - 1, element),509equations)510u_rl = cons2recon(get_node_vars(u, equations, dg, i, j, k, element),511equations)512u_rr = cons2recon(get_node_vars(u, equations, dg, i, j, min(nnodes(dg), k + 1),513element), equations)514515u_l, u_r = reconstruction_mode(u_ll, u_lr, u_rl, u_rr,516sc_interface_coords, k,517slope_limiter, dg)518519flux = volume_flux_fv(recon2cons(u_l, equations), recon2cons(u_r, equations),5203, equations) # orientation 3: z direction521522set_node_vars!(fstar3_L, flux, equations, dg, i, j, k)523set_node_vars!(fstar3_R, flux, equations, dg, i, j, k)524end525526return nothing527end528529function prolong2interfaces!(backend::Nothing, cache, u, mesh::TreeMesh{3}, equations,530dg::DG)531@unpack interfaces = cache532@unpack orientations, neighbor_ids = interfaces533interfaces_u = interfaces.u534535@threaded for interface in eachinterface(dg, cache)536left_element = neighbor_ids[1, interface]537right_element = neighbor_ids[2, interface]538539if orientations[interface] == 1540# interface in x-direction541for k in eachnode(dg), j in eachnode(dg), v in eachvariable(equations)542interfaces_u[1, v, j, k, interface] = u[v, nnodes(dg), j, k,543left_element]544interfaces_u[2, v, j, k, interface] = u[v, 1, j, k,545right_element]546end547elseif orientations[interface] == 2548# interface in y-direction549for k in eachnode(dg), i in eachnode(dg), v in eachvariable(equations)550interfaces_u[1, v, i, k, interface] = u[v, i, nnodes(dg), k,551left_element]552interfaces_u[2, v, i, k, interface] = u[v, i, 1, k,553right_element]554end555else # if orientations[interface] == 3556# interface in z-direction557for j in eachnode(dg), i in eachnode(dg), v in eachvariable(equations)558interfaces_u[1, v, i, j, interface] = u[v, i, j, nnodes(dg),559left_element]560interfaces_u[2, v, i, j, interface] = u[v, i, j, 1, right_element]561end562end563end564565return nothing566end567568function calc_interface_flux!(backend::Nothing, surface_flux_values,569mesh::TreeMesh{3},570have_nonconservative_terms::False, equations,571surface_integral, dg::DG, cache)572@unpack surface_flux = surface_integral573@unpack u, neighbor_ids, orientations = cache.interfaces574575@threaded for interface in eachinterface(dg, cache)576# Get neighboring elements577left_id = neighbor_ids[1, interface]578right_id = neighbor_ids[2, interface]579580# Determine interface direction with respect to elements:581# orientation = 1: left -> 2, right -> 1582# orientation = 2: left -> 4, right -> 3583# orientation = 3: left -> 6, right -> 5584left_direction = 2 * orientations[interface]585right_direction = 2 * orientations[interface] - 1586587for j in eachnode(dg), i in eachnode(dg)588# Call pointwise Riemann solver589u_ll, u_rr = get_surface_node_vars(u, equations, dg, i, j, interface)590flux = surface_flux(u_ll, u_rr, orientations[interface], equations)591592# Copy flux to left and right element storage593for v in eachvariable(equations)594surface_flux_values[v, i, j, left_direction, left_id] = flux[v]595surface_flux_values[v, i, j, right_direction, right_id] = flux[v]596end597end598end599600return nothing601end602603function calc_interface_flux!(backend::Nothing, surface_flux_values,604mesh::TreeMesh{3},605have_nonconservative_terms::True, equations,606surface_integral, dg::DG, cache)607surface_flux, nonconservative_flux = surface_integral.surface_flux608@unpack u, neighbor_ids, orientations = cache.interfaces609610@threaded for interface in eachinterface(dg, cache)611# Get neighboring elements612left_id = neighbor_ids[1, interface]613right_id = neighbor_ids[2, interface]614615# Determine interface direction with respect to elements:616# orientation = 1: left -> 2, right -> 1617# orientation = 2: left -> 4, right -> 3618# orientation = 3: left -> 6, right -> 5619left_direction = 2 * orientations[interface]620right_direction = 2 * orientations[interface] - 1621622for j in eachnode(dg), i in eachnode(dg)623# Call pointwise Riemann solver624orientation = orientations[interface]625u_ll, u_rr = get_surface_node_vars(u, equations, dg, i, j, interface)626flux = surface_flux(u_ll, u_rr, orientation, equations)627628# Compute both nonconservative fluxes629noncons_left = nonconservative_flux(u_ll, u_rr, orientation, equations)630noncons_right = nonconservative_flux(u_rr, u_ll, orientation, equations)631632# Copy flux to left and right element storage633for v in eachvariable(equations)634# Note the factor 0.5 necessary for the nonconservative fluxes based on635# the interpretation of global SBP operators coupled discontinuously via636# central fluxes/SATs637surface_flux_values[v, i, j, left_direction, left_id] = flux[v] +6380.5f0 *639noncons_left[v]640surface_flux_values[v, i, j, right_direction, right_id] = flux[v] +6410.5f0 *642noncons_right[v]643end644end645end646647return nothing648end649650function prolong2boundaries!(cache, u,651mesh::TreeMesh{3}, equations, dg::DG)652@unpack boundaries = cache653@unpack orientations, neighbor_sides = boundaries654655@threaded for boundary in eachboundary(dg, cache)656element = boundaries.neighbor_ids[boundary]657658if orientations[boundary] == 1659# boundary in x-direction660if neighbor_sides[boundary] == 1661# element in -x direction of boundary662for k in eachnode(dg), j in eachnode(dg), v in eachvariable(equations)663boundaries.u[1, v, j, k, boundary] = u[v, nnodes(dg), j, k, element]664end665else # Element in +x direction of boundary666for k in eachnode(dg), j in eachnode(dg), v in eachvariable(equations)667boundaries.u[2, v, j, k, boundary] = u[v, 1, j, k, element]668end669end670elseif orientations[boundary] == 2671# boundary in y-direction672if neighbor_sides[boundary] == 1673# element in -y direction of boundary674for k in eachnode(dg), i in eachnode(dg), v in eachvariable(equations)675boundaries.u[1, v, i, k, boundary] = u[v, i, nnodes(dg), k, element]676end677else678# element in +y direction of boundary679for k in eachnode(dg), i in eachnode(dg), v in eachvariable(equations)680boundaries.u[2, v, i, k, boundary] = u[v, i, 1, k, element]681end682end683else #if orientations[boundary] == 3684# boundary in z-direction685if neighbor_sides[boundary] == 1686# element in -z direction of boundary687for j in eachnode(dg), i in eachnode(dg), v in eachvariable(equations)688boundaries.u[1, v, i, j, boundary] = u[v, i, j, nnodes(dg), element]689end690else691# element in +z direction of boundary692for j in eachnode(dg), i in eachnode(dg), v in eachvariable(equations)693boundaries.u[2, v, i, j, boundary] = u[v, i, j, 1, element]694end695end696end697end698699return nothing700end701702function calc_boundary_flux!(cache, t, boundary_conditions::NamedTuple,703mesh::TreeMesh{3}, equations, surface_integral, dg::DG)704@unpack surface_flux_values = cache.elements705@unpack n_boundaries_per_direction = cache.boundaries706707# Calculate indices708lasts = accumulate(+, n_boundaries_per_direction)709firsts = lasts - n_boundaries_per_direction .+ 1710711# Calc boundary fluxes in each direction712calc_boundary_flux_by_direction!(surface_flux_values, t, boundary_conditions[1],713equations, surface_integral, dg, cache,7141, firsts[1], lasts[1])715calc_boundary_flux_by_direction!(surface_flux_values, t, boundary_conditions[2],716equations, surface_integral, dg, cache,7172, firsts[2], lasts[2])718calc_boundary_flux_by_direction!(surface_flux_values, t, boundary_conditions[3],719equations, surface_integral, dg, cache,7203, firsts[3], lasts[3])721calc_boundary_flux_by_direction!(surface_flux_values, t, boundary_conditions[4],722equations, surface_integral, dg, cache,7234, firsts[4], lasts[4])724calc_boundary_flux_by_direction!(surface_flux_values, t, boundary_conditions[5],725equations, surface_integral, dg, cache,7265, firsts[5], lasts[5])727calc_boundary_flux_by_direction!(surface_flux_values, t, boundary_conditions[6],728equations, surface_integral, dg, cache,7296, firsts[6], lasts[6])730731return nothing732end733734function calc_boundary_flux_by_direction!(surface_flux_values::AbstractArray{<:Any, 5},735t,736boundary_condition, equations,737surface_integral, dg::DG, cache,738direction, first_boundary, last_boundary)739@unpack surface_flux = surface_integral740@unpack u, neighbor_ids, neighbor_sides, node_coordinates, orientations = cache.boundaries741742@threaded for boundary in first_boundary:last_boundary743# Get neighboring element744neighbor = neighbor_ids[boundary]745746for j in eachnode(dg), i in eachnode(dg)747# Get boundary flux748u_ll, u_rr = get_surface_node_vars(u, equations, dg, i, j, boundary)749if neighbor_sides[boundary] == 1 # Element is on the left, boundary on the right750u_inner = u_ll751else # Element is on the right, boundary on the left752u_inner = u_rr753end754x = get_node_coords(node_coordinates, equations, dg, i, j, boundary)755flux = boundary_condition(u_inner, orientations[boundary], direction, x, t,756surface_flux, equations)757758# Copy flux to left and right element storage759for v in eachvariable(equations)760surface_flux_values[v, i, j, direction, neighbor] = flux[v]761end762end763end764765return nothing766end767768function prolong2mortars!(cache, u,769mesh::TreeMesh{3}, equations,770mortar_l2::LobattoLegendreMortarL2,771dg::DGSEM)772# temporary buffer for projections773@unpack fstar_tmp1_threaded = cache774775@threaded for mortar in eachmortar(dg, cache)776fstar_tmp1 = fstar_tmp1_threaded[Threads.threadid()]777778lower_left_element = cache.mortars.neighbor_ids[1, mortar]779lower_right_element = cache.mortars.neighbor_ids[2, mortar]780upper_left_element = cache.mortars.neighbor_ids[3, mortar]781upper_right_element = cache.mortars.neighbor_ids[4, mortar]782large_element = cache.mortars.neighbor_ids[5, mortar]783784# Copy solution small to small785if cache.mortars.large_sides[mortar] == 1 # -> small elements on right side786if cache.mortars.orientations[mortar] == 1787# L2 mortars in x-direction788for k in eachnode(dg), j in eachnode(dg)789for v in eachvariable(equations)790cache.mortars.u_upper_left[2, v, j, k, mortar] = u[v, 1, j, k,791upper_left_element]792cache.mortars.u_upper_right[2, v, j, k, mortar] = u[v, 1, j, k,793upper_right_element]794cache.mortars.u_lower_left[2, v, j, k, mortar] = u[v, 1, j, k,795lower_left_element]796cache.mortars.u_lower_right[2, v, j, k, mortar] = u[v, 1, j, k,797lower_right_element]798end799end800elseif cache.mortars.orientations[mortar] == 2801# L2 mortars in y-direction802for k in eachnode(dg), i in eachnode(dg)803for v in eachvariable(equations)804cache.mortars.u_upper_left[2, v, i, k, mortar] = u[v, i, 1, k,805upper_left_element]806cache.mortars.u_upper_right[2, v, i, k, mortar] = u[v, i, 1, k,807upper_right_element]808cache.mortars.u_lower_left[2, v, i, k, mortar] = u[v, i, 1, k,809lower_left_element]810cache.mortars.u_lower_right[2, v, i, k, mortar] = u[v, i, 1, k,811lower_right_element]812end813end814else # orientations[mortar] == 3815# L2 mortars in z-direction816for j in eachnode(dg), i in eachnode(dg)817for v in eachvariable(equations)818cache.mortars.u_upper_left[2, v, i, j, mortar] = u[v, i, j, 1,819upper_left_element]820cache.mortars.u_upper_right[2, v, i, j, mortar] = u[v, i, j, 1,821upper_right_element]822cache.mortars.u_lower_left[2, v, i, j, mortar] = u[v, i, j, 1,823lower_left_element]824cache.mortars.u_lower_right[2, v, i, j, mortar] = u[v, i, j, 1,825lower_right_element]826end827end828end829else # large_sides[mortar] == 2 -> small elements on left side830if cache.mortars.orientations[mortar] == 1831# L2 mortars in x-direction832for k in eachnode(dg), j in eachnode(dg)833for v in eachvariable(equations)834cache.mortars.u_upper_left[1, v, j, k, mortar] = u[v,835nnodes(dg),836j, k,837upper_left_element]838cache.mortars.u_upper_right[1, v, j, k, mortar] = u[v,839nnodes(dg),840j, k,841upper_right_element]842cache.mortars.u_lower_left[1, v, j, k, mortar] = u[v,843nnodes(dg),844j, k,845lower_left_element]846cache.mortars.u_lower_right[1, v, j, k, mortar] = u[v,847nnodes(dg),848j, k,849lower_right_element]850end851end852elseif cache.mortars.orientations[mortar] == 2853# L2 mortars in y-direction854for k in eachnode(dg), i in eachnode(dg)855for v in eachvariable(equations)856cache.mortars.u_upper_left[1, v, i, k, mortar] = u[v, i,857nnodes(dg),858k,859upper_left_element]860cache.mortars.u_upper_right[1, v, i, k, mortar] = u[v, i,861nnodes(dg),862k,863upper_right_element]864cache.mortars.u_lower_left[1, v, i, k, mortar] = u[v, i,865nnodes(dg),866k,867lower_left_element]868cache.mortars.u_lower_right[1, v, i, k, mortar] = u[v, i,869nnodes(dg),870k,871lower_right_element]872end873end874else # if cache.mortars.orientations[mortar] == 3875# L2 mortars in z-direction876for j in eachnode(dg), i in eachnode(dg)877for v in eachvariable(equations)878cache.mortars.u_upper_left[1, v, i, j, mortar] = u[v, i, j,879nnodes(dg),880upper_left_element]881cache.mortars.u_upper_right[1, v, i, j, mortar] = u[v, i, j,882nnodes(dg),883upper_right_element]884cache.mortars.u_lower_left[1, v, i, j, mortar] = u[v, i, j,885nnodes(dg),886lower_left_element]887cache.mortars.u_lower_right[1, v, i, j, mortar] = u[v, i, j,888nnodes(dg),889lower_right_element]890end891end892end893end894895# Interpolate large element face data to small interface locations896if cache.mortars.large_sides[mortar] == 1 # -> large element on left side897leftright = 1898if cache.mortars.orientations[mortar] == 1899# L2 mortars in x-direction900u_large = view(u, :, nnodes(dg), :, :, large_element)901element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,902mortar, u_large, fstar_tmp1)903elseif cache.mortars.orientations[mortar] == 2904# L2 mortars in y-direction905u_large = view(u, :, :, nnodes(dg), :, large_element)906element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,907mortar, u_large, fstar_tmp1)908else # cache.mortars.orientations[mortar] == 3909# L2 mortars in z-direction910u_large = view(u, :, :, :, nnodes(dg), large_element)911element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,912mortar, u_large, fstar_tmp1)913end914else # large_sides[mortar] == 2 -> large element on right side915leftright = 2916if cache.mortars.orientations[mortar] == 1917# L2 mortars in x-direction918u_large = view(u, :, 1, :, :, large_element)919element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,920mortar, u_large, fstar_tmp1)921elseif cache.mortars.orientations[mortar] == 2922# L2 mortars in y-direction923u_large = view(u, :, :, 1, :, large_element)924element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,925mortar, u_large, fstar_tmp1)926else # cache.mortars.orientations[mortar] == 3927# L2 mortars in z-direction928u_large = view(u, :, :, :, 1, large_element)929element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,930mortar, u_large, fstar_tmp1)931end932end933end934935return nothing936end937938@inline function element_solutions_to_mortars!(mortars,939mortar_l2::LobattoLegendreMortarL2,940leftright, mortar,941u_large::AbstractArray{<:Any, 3},942fstar_tmp1)943multiply_dimensionwise!(view(mortars.u_upper_left, leftright, :, :, :, mortar),944mortar_l2.forward_lower, mortar_l2.forward_upper, u_large,945fstar_tmp1)946multiply_dimensionwise!(view(mortars.u_upper_right, leftright, :, :, :, mortar),947mortar_l2.forward_upper, mortar_l2.forward_upper, u_large,948fstar_tmp1)949multiply_dimensionwise!(view(mortars.u_lower_left, leftright, :, :, :, mortar),950mortar_l2.forward_lower, mortar_l2.forward_lower, u_large,951fstar_tmp1)952multiply_dimensionwise!(view(mortars.u_lower_right, leftright, :, :, :, mortar),953mortar_l2.forward_upper, mortar_l2.forward_lower, u_large,954fstar_tmp1)955return nothing956end957958function calc_mortar_flux!(surface_flux_values,959mesh::TreeMesh{3},960have_nonconservative_terms::False, equations,961mortar_l2::LobattoLegendreMortarL2,962surface_integral, dg::DG, cache)963@unpack surface_flux = surface_integral964@unpack u_lower_left, u_lower_right, u_upper_left, u_upper_right, orientations = cache.mortars965@unpack (fstar_primary_upper_left_threaded, fstar_primary_upper_right_threaded,966fstar_primary_lower_left_threaded, fstar_primary_lower_right_threaded,967fstar_secondary_upper_left_threaded, fstar_secondary_upper_right_threaded,968fstar_secondary_lower_left_threaded, fstar_secondary_lower_right_threaded,969fstar_tmp1_threaded) = cache970971@threaded for mortar in eachmortar(dg, cache)972# Choose thread-specific pre-allocated container973fstar_primary_upper_left = fstar_primary_upper_left_threaded[Threads.threadid()]974fstar_primary_upper_right = fstar_primary_upper_right_threaded[Threads.threadid()]975fstar_primary_lower_left = fstar_primary_lower_left_threaded[Threads.threadid()]976fstar_primary_lower_right = fstar_primary_lower_right_threaded[Threads.threadid()]977fstar_secondary_upper_left = fstar_secondary_upper_left_threaded[Threads.threadid()]978fstar_secondary_upper_right = fstar_secondary_upper_right_threaded[Threads.threadid()]979fstar_secondary_lower_left = fstar_secondary_lower_left_threaded[Threads.threadid()]980fstar_secondary_lower_right = fstar_secondary_lower_right_threaded[Threads.threadid()]981fstar_tmp1 = fstar_tmp1_threaded[Threads.threadid()]982983# Calculate fluxes984orientation = orientations[mortar]985calc_fstar!(fstar_primary_upper_left, equations, surface_flux, dg,986u_upper_left, mortar, orientation)987calc_fstar!(fstar_primary_upper_right, equations, surface_flux, dg,988u_upper_right, mortar, orientation)989calc_fstar!(fstar_primary_lower_left, equations, surface_flux, dg,990u_lower_left, mortar, orientation)991calc_fstar!(fstar_primary_lower_right, equations, surface_flux, dg,992u_lower_right, mortar, orientation)993calc_fstar!(fstar_secondary_upper_left, equations, surface_flux, dg,994u_upper_left, mortar, orientation)995calc_fstar!(fstar_secondary_upper_right, equations, surface_flux, dg,996u_upper_right, mortar, orientation)997calc_fstar!(fstar_secondary_lower_left, equations, surface_flux, dg,998u_lower_left, mortar, orientation)999calc_fstar!(fstar_secondary_lower_right, equations, surface_flux, dg,1000u_lower_right, mortar, orientation)10011002mortar_fluxes_to_elements!(surface_flux_values,1003mesh, equations, mortar_l2, dg, cache, mortar,1004fstar_primary_upper_left, fstar_primary_upper_right,1005fstar_primary_lower_left, fstar_primary_lower_right,1006fstar_secondary_upper_left,1007fstar_secondary_upper_right,1008fstar_secondary_lower_left,1009fstar_secondary_lower_right,1010fstar_tmp1)1011end10121013return nothing1014end10151016function calc_mortar_flux!(surface_flux_values,1017mesh::TreeMesh{3},1018have_nonconservative_terms::True, equations,1019mortar_l2::LobattoLegendreMortarL2,1020surface_integral, dg::DG, cache)1021surface_flux, nonconservative_flux = surface_integral.surface_flux1022@unpack u_lower_left, u_lower_right, u_upper_left, u_upper_right, orientations, large_sides = cache.mortars1023@unpack (fstar_primary_upper_left_threaded, fstar_primary_upper_right_threaded,1024fstar_primary_lower_left_threaded, fstar_primary_lower_right_threaded,1025fstar_secondary_upper_left_threaded, fstar_secondary_upper_right_threaded,1026fstar_secondary_lower_left_threaded, fstar_secondary_lower_right_threaded,1027fstar_tmp1_threaded) = cache10281029@threaded for mortar in eachmortar(dg, cache)1030# Choose thread-specific pre-allocated container1031fstar_primary_upper_left = fstar_primary_upper_left_threaded[Threads.threadid()]1032fstar_primary_upper_right = fstar_primary_upper_right_threaded[Threads.threadid()]1033fstar_primary_lower_left = fstar_primary_lower_left_threaded[Threads.threadid()]1034fstar_primary_lower_right = fstar_primary_lower_right_threaded[Threads.threadid()]1035fstar_secondary_upper_left = fstar_secondary_upper_left_threaded[Threads.threadid()]1036fstar_secondary_upper_right = fstar_secondary_upper_right_threaded[Threads.threadid()]1037fstar_secondary_lower_left = fstar_secondary_lower_left_threaded[Threads.threadid()]1038fstar_secondary_lower_right = fstar_secondary_lower_right_threaded[Threads.threadid()]1039fstar_tmp1 = fstar_tmp1_threaded[Threads.threadid()]10401041# Calculate fluxes1042orientation = orientations[mortar]1043calc_fstar!(fstar_primary_upper_left, equations, surface_flux, dg,1044u_upper_left, mortar, orientation)1045calc_fstar!(fstar_primary_upper_right, equations, surface_flux, dg,1046u_upper_right, mortar, orientation)1047calc_fstar!(fstar_primary_lower_left, equations, surface_flux, dg,1048u_lower_left, mortar, orientation)1049calc_fstar!(fstar_primary_lower_right, equations, surface_flux, dg,1050u_lower_right, mortar, orientation)1051calc_fstar!(fstar_secondary_upper_left, equations, surface_flux, dg,1052u_upper_left, mortar, orientation)1053calc_fstar!(fstar_secondary_upper_right, equations, surface_flux, dg,1054u_upper_right, mortar, orientation)1055calc_fstar!(fstar_secondary_lower_left, equations, surface_flux, dg,1056u_lower_left, mortar, orientation)1057calc_fstar!(fstar_secondary_lower_right, equations, surface_flux, dg,1058u_lower_right, mortar, orientation)10591060# Add nonconservative fluxes.1061# These need to be adapted on the geometry (left/right) since the order of1062# the arguments matters, based on the global SBP operator interpretation.1063# The same interpretation (global SBP operators coupled discontinuously via1064# central fluxes/SATs) explains why we need the factor 0.5.1065# Alternatively, you can also follow the argumentation of Bohm et al. 20181066# ("nonconservative diamond flux")1067if large_sides[mortar] == 1 # -> small elements on right side1068for j in eachnode(dg), i in eachnode(dg)1069# Pull the left and right solutions1070u_upper_left_ll, u_upper_left_rr = get_surface_node_vars(u_upper_left,1071equations, dg,1072i, j, mortar)1073u_upper_right_ll, u_upper_right_rr = get_surface_node_vars(u_upper_right,1074equations,1075dg, i, j,1076mortar)1077u_lower_left_ll, u_lower_left_rr = get_surface_node_vars(u_lower_left,1078equations, dg,1079i, j, mortar)1080u_lower_right_ll, u_lower_right_rr = get_surface_node_vars(u_lower_right,1081equations,1082dg, i, j,1083mortar)1084# Call pointwise nonconservative term1085noncons_primary_upper_left = nonconservative_flux(u_upper_left_ll,1086u_upper_left_rr,1087orientation,1088equations)1089noncons_primary_upper_right = nonconservative_flux(u_upper_right_ll,1090u_upper_right_rr,1091orientation,1092equations)1093noncons_primary_lower_left = nonconservative_flux(u_lower_left_ll,1094u_lower_left_rr,1095orientation,1096equations)1097noncons_primary_lower_right = nonconservative_flux(u_lower_right_ll,1098u_lower_right_rr,1099orientation,1100equations)1101noncons_secondary_upper_left = nonconservative_flux(u_upper_left_rr,1102u_upper_left_ll,1103orientation,1104equations)1105noncons_secondary_upper_right = nonconservative_flux(u_upper_right_rr,1106u_upper_right_ll,1107orientation,1108equations)1109noncons_secondary_lower_left = nonconservative_flux(u_lower_left_rr,1110u_lower_left_ll,1111orientation,1112equations)1113noncons_secondary_lower_right = nonconservative_flux(u_lower_right_rr,1114u_lower_right_ll,1115orientation,1116equations)1117# Add to primary and secondary temporary storage1118multiply_add_to_node_vars!(fstar_primary_upper_left, 0.5f0,1119noncons_primary_upper_left,1120equations, dg, i, j)1121multiply_add_to_node_vars!(fstar_primary_upper_right, 0.5f0,1122noncons_primary_upper_right,1123equations, dg, i, j)1124multiply_add_to_node_vars!(fstar_primary_lower_left, 0.5f0,1125noncons_primary_lower_left,1126equations, dg, i, j)1127multiply_add_to_node_vars!(fstar_primary_lower_right, 0.5f0,1128noncons_primary_lower_right,1129equations, dg, i, j)1130multiply_add_to_node_vars!(fstar_secondary_upper_left, 0.5f0,1131noncons_secondary_upper_left,1132equations, dg, i, j)1133multiply_add_to_node_vars!(fstar_secondary_upper_right, 0.5f0,1134noncons_secondary_upper_right,1135equations, dg, i, j)1136multiply_add_to_node_vars!(fstar_secondary_lower_left, 0.5f0,1137noncons_secondary_lower_left,1138equations, dg, i, j)1139multiply_add_to_node_vars!(fstar_secondary_lower_right, 0.5f0,1140noncons_secondary_lower_right,1141equations, dg, i, j)1142end1143else # large_sides[mortar] == 2 -> small elements on the left1144for j in eachnode(dg), i in eachnode(dg)1145# Pull the left and right solutions1146u_upper_left_ll, u_upper_left_rr = get_surface_node_vars(u_upper_left,1147equations, dg,1148i, j, mortar)1149u_upper_right_ll, u_upper_right_rr = get_surface_node_vars(u_upper_right,1150equations,1151dg, i, j,1152mortar)1153u_lower_left_ll, u_lower_left_rr = get_surface_node_vars(u_lower_left,1154equations, dg,1155i, j, mortar)1156u_lower_right_ll, u_lower_right_rr = get_surface_node_vars(u_lower_right,1157equations,1158dg, i, j,1159mortar)1160# Call pointwise nonconservative term1161noncons_primary_upper_left = nonconservative_flux(u_upper_left_rr,1162u_upper_left_ll,1163orientation,1164equations)1165noncons_primary_upper_right = nonconservative_flux(u_upper_right_rr,1166u_upper_right_ll,1167orientation,1168equations)1169noncons_primary_lower_left = nonconservative_flux(u_lower_left_rr,1170u_lower_left_ll,1171orientation,1172equations)1173noncons_primary_lower_right = nonconservative_flux(u_lower_right_rr,1174u_lower_right_ll,1175orientation,1176equations)1177noncons_secondary_upper_left = nonconservative_flux(u_upper_left_ll,1178u_upper_left_rr,1179orientation,1180equations)1181noncons_secondary_upper_right = nonconservative_flux(u_upper_right_ll,1182u_upper_right_rr,1183orientation,1184equations)1185noncons_secondary_lower_left = nonconservative_flux(u_lower_left_ll,1186u_lower_left_rr,1187orientation,1188equations)1189noncons_secondary_lower_right = nonconservative_flux(u_lower_right_ll,1190u_lower_right_rr,1191orientation,1192equations)1193# Add to primary and secondary temporary storage1194multiply_add_to_node_vars!(fstar_primary_upper_left, 0.5f0,1195noncons_primary_upper_left,1196equations, dg, i, j)1197multiply_add_to_node_vars!(fstar_primary_upper_right, 0.5f0,1198noncons_primary_upper_right,1199equations, dg, i, j)1200multiply_add_to_node_vars!(fstar_primary_lower_left, 0.5f0,1201noncons_primary_lower_left,1202equations, dg, i, j)1203multiply_add_to_node_vars!(fstar_primary_lower_right, 0.5f0,1204noncons_primary_lower_right,1205equations, dg, i, j)1206multiply_add_to_node_vars!(fstar_secondary_upper_left, 0.5f0,1207noncons_secondary_upper_left,1208equations, dg, i, j)1209multiply_add_to_node_vars!(fstar_secondary_upper_right, 0.5f0,1210noncons_secondary_upper_right,1211equations, dg, i, j)1212multiply_add_to_node_vars!(fstar_secondary_lower_left, 0.5f0,1213noncons_secondary_lower_left,1214equations, dg, i, j)1215multiply_add_to_node_vars!(fstar_secondary_lower_right, 0.5f0,1216noncons_secondary_lower_right,1217equations, dg, i, j)1218end1219end12201221mortar_fluxes_to_elements!(surface_flux_values,1222mesh, equations, mortar_l2, dg, cache, mortar,1223fstar_primary_upper_left, fstar_primary_upper_right,1224fstar_primary_lower_left, fstar_primary_lower_right,1225fstar_secondary_upper_left,1226fstar_secondary_upper_right,1227fstar_secondary_lower_left,1228fstar_secondary_lower_right,1229fstar_tmp1)1230end12311232return nothing1233end12341235@inline function calc_fstar!(destination::AbstractArray{<:Any, 3}, equations,1236surface_flux, dg::DGSEM,1237u_interfaces, interface, orientation)1238for j in eachnode(dg), i in eachnode(dg)1239# Call pointwise two-point numerical flux function1240u_ll, u_rr = get_surface_node_vars(u_interfaces, equations, dg, i, j, interface)1241flux = surface_flux(u_ll, u_rr, orientation, equations)12421243# Copy flux to left and right element storage1244set_node_vars!(destination, flux, equations, dg, i, j)1245end12461247return nothing1248end12491250@inline function mortar_fluxes_to_elements!(surface_flux_values,1251mesh::TreeMesh{3}, equations,1252mortar_l2::LobattoLegendreMortarL2,1253dg::DGSEM, cache,1254mortar,1255fstar_primary_upper_left,1256fstar_primary_upper_right,1257fstar_primary_lower_left,1258fstar_primary_lower_right,1259fstar_secondary_upper_left,1260fstar_secondary_upper_right,1261fstar_secondary_lower_left,1262fstar_secondary_lower_right,1263fstar_tmp1)1264lower_left_element = cache.mortars.neighbor_ids[1, mortar]1265lower_right_element = cache.mortars.neighbor_ids[2, mortar]1266upper_left_element = cache.mortars.neighbor_ids[3, mortar]1267upper_right_element = cache.mortars.neighbor_ids[4, mortar]1268large_element = cache.mortars.neighbor_ids[5, mortar]12691270# Copy flux small to small1271if cache.mortars.large_sides[mortar] == 1 # -> small elements on right side1272if cache.mortars.orientations[mortar] == 11273# L2 mortars in x-direction1274direction = 11275elseif cache.mortars.orientations[mortar] == 21276# L2 mortars in y-direction1277direction = 31278else # if cache.mortars.orientations[mortar] == 31279# L2 mortars in z-direction1280direction = 51281end1282else # large_sides[mortar] == 2 -> small elements on left side1283if cache.mortars.orientations[mortar] == 11284# L2 mortars in x-direction1285direction = 21286elseif cache.mortars.orientations[mortar] == 21287# L2 mortars in y-direction1288direction = 41289else # if cache.mortars.orientations[mortar] == 31290# L2 mortars in z-direction1291direction = 61292end1293end1294surface_flux_values[:, :, :, direction, upper_left_element] .= fstar_primary_upper_left1295surface_flux_values[:, :, :, direction, upper_right_element] .= fstar_primary_upper_right1296surface_flux_values[:, :, :, direction, lower_left_element] .= fstar_primary_lower_left1297surface_flux_values[:, :, :, direction, lower_right_element] .= fstar_primary_lower_right12981299# Project small fluxes to large element1300if cache.mortars.large_sides[mortar] == 1 # -> small elements on right side1301if cache.mortars.orientations[mortar] == 11302# L2 mortars in x-direction1303direction = 21304elseif cache.mortars.orientations[mortar] == 21305# L2 mortars in y-direction1306direction = 41307else # if cache.mortars.orientations[mortar] == 31308# L2 mortars in z-direction1309direction = 61310end1311else # large_sides[mortar] == 2 -> small elements on left side1312if cache.mortars.orientations[mortar] == 11313# L2 mortars in x-direction1314direction = 11315elseif cache.mortars.orientations[mortar] == 21316# L2 mortars in y-direction1317direction = 31318else # if cache.mortars.orientations[mortar] == 31319# L2 mortars in z-direction1320direction = 51321end1322end13231324multiply_dimensionwise!(view(surface_flux_values, :, :, :, direction,1325large_element),1326mortar_l2.reverse_lower, mortar_l2.reverse_upper,1327fstar_secondary_upper_left, fstar_tmp1)1328add_multiply_dimensionwise!(view(surface_flux_values, :, :, :, direction,1329large_element),1330mortar_l2.reverse_upper, mortar_l2.reverse_upper,1331fstar_secondary_upper_right, fstar_tmp1)1332add_multiply_dimensionwise!(view(surface_flux_values, :, :, :, direction,1333large_element),1334mortar_l2.reverse_lower, mortar_l2.reverse_lower,1335fstar_secondary_lower_left, fstar_tmp1)1336add_multiply_dimensionwise!(view(surface_flux_values, :, :, :, direction,1337large_element),1338mortar_l2.reverse_upper, mortar_l2.reverse_lower,1339fstar_secondary_lower_right, fstar_tmp1)13401341return nothing1342end13431344function calc_surface_integral!(backend::Nothing, du, u,1345mesh::Union{TreeMesh{3}, StructuredMesh{3}},1346equations, surface_integral::SurfaceIntegralWeakForm,1347dg::DGSEM, cache)1348@unpack inverse_weights = dg.basis1349@unpack surface_flux_values = cache.elements13501351# This computes the **negative** surface integral contribution,1352# i.e., M^{-1} * boundary_interpolation^T (which is for Gauss-Lobatto DGSEM just M^{-1} * B)1353# and the missing "-" is taken care of by `apply_jacobian!`.1354#1355# We also use explicit assignments instead of `+=` and `-=` to let `@muladd`1356# turn these into FMAs (see comment at the top of the file).1357factor = inverse_weights[1] # For LGL basis: Identical to weighted boundary interpolation at x = ±11358@threaded for element in eachelement(dg, cache)1359for m in eachnode(dg), l in eachnode(dg)1360for v in eachvariable(equations)1361# surface at -x1362du[v, 1, l, m, element] = (du[v, 1, l, m, element] -1363surface_flux_values[v, l, m, 1,1364element] *1365factor)13661367# surface at +x1368du[v, nnodes(dg), l, m, element] = (du[v, nnodes(dg), l, m, element] +1369surface_flux_values[v, l, m, 2,1370element] *1371factor)13721373# surface at -y1374du[v, l, 1, m, element] = (du[v, l, 1, m, element] -1375surface_flux_values[v, l, m, 3,1376element] *1377factor)13781379# surface at +y1380du[v, l, nnodes(dg), m, element] = (du[v, l, nnodes(dg), m, element] +1381surface_flux_values[v, l, m, 4,1382element] *1383factor)13841385# surface at -z1386du[v, l, m, 1, element] = (du[v, l, m, 1, element] -1387surface_flux_values[v, l, m, 5,1388element] *1389factor)13901391# surface at +z1392du[v, l, m, nnodes(dg), element] = (du[v, l, m, nnodes(dg), element] +1393surface_flux_values[v, l, m, 6,1394element] *1395factor)1396end1397end1398end13991400return nothing1401end14021403function apply_jacobian!(backend::Nothing, du, mesh::TreeMesh{3},1404equations, dg::DG, cache)1405@unpack inverse_jacobian = cache.elements14061407@threaded for element in eachelement(dg, cache)1408# Negative sign included to account for the negated surface and volume terms,1409# see e.g. the computation of `derivative_hat` in the basis setup and1410# the comment in `calc_surface_integral!`.1411factor = -inverse_jacobian[element]14121413for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)1414for v in eachvariable(equations)1415du[v, i, j, k, element] *= factor1416end1417end1418end14191420return nothing1421end14221423# Need dimension specific version to avoid error at dispatching1424function calc_sources!(du, u, t, source_terms::Nothing,1425equations::AbstractEquations{3}, dg::DG, cache)1426return nothing1427end14281429function calc_sources!(du, u, t, source_terms,1430equations::AbstractEquations{3}, dg::DG, cache)1431@unpack node_coordinates = cache.elements14321433@threaded for element in eachelement(dg, cache)1434for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)1435u_local = get_node_vars(u, equations, dg, i, j, k, element)1436x_local = get_node_coords(node_coordinates, equations, dg,1437i, j, k, element)1438du_local = source_terms(u_local, x_local, t, equations)1439add_to_node_vars!(du, du_local, equations, dg, i, j, k, element)1440end1441end14421443return nothing1444end1445end # @muladd144614471448