Path: blob/main/src/solvers/dgsem_structured/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: noindent6function create_cache(mesh::Union{StructuredMesh{3},7P4estMesh{3}, T8codeMesh{3}},8equations,9volume_integral::AbstractVolumeIntegralSubcell,10dg::DG, cache_containers, uEltype)11fstar1_L_threaded, fstar1_R_threaded,12fstar2_L_threaded, fstar2_R_threaded,13fstar3_L_threaded, fstar3_R_threaded = create_f_threaded(mesh, equations, dg,14uEltype)1516normal_vectors = NormalVectorContainer3D(mesh, dg, cache_containers)1718cache_subcell_limiting = create_cache_subcell_limiting(mesh, equations,19volume_integral, dg,20cache_containers, uEltype)2122return (; fstar1_L_threaded, fstar1_R_threaded,23fstar2_L_threaded, fstar2_R_threaded,24fstar3_L_threaded, fstar3_R_threaded,25normal_vectors, cache_subcell_limiting...)26end2728#=29`weak_form_kernel!` is only implemented for conserved terms as30non-conservative terms should always be discretized in conjunction with a flux-splitting scheme,31see `flux_differencing_kernel!`.32This treatment is required to achieve, e.g., entropy-stability or well-balancedness.33See also https://github.com/trixi-framework/Trixi.jl/issues/1671#issuecomment-176564406434=#35@inline function weak_form_kernel!(du, u,36element,37::Type{<:Union{StructuredMesh{3}, P4estMesh{3},38T8codeMesh{3}}},39have_nonconservative_terms::False, equations,40dg::DGSEM, cache, alpha = true)41# true * [some floating point value] == [exactly the same floating point value]42# This can (hopefully) be optimized away due to constant propagation.43@unpack derivative_hat = dg.basis44@unpack contravariant_vectors = cache.elements4546for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)47u_node = get_node_vars(u, equations, dg, i, j, k, element)4849flux1 = flux(u_node, 1, equations)50flux2 = flux(u_node, 2, equations)51flux3 = flux(u_node, 3, equations)5253# Compute the contravariant flux by taking the scalar product of the54# first contravariant vector Ja^1 and the flux vector55Ja11, Ja12, Ja13 = get_contravariant_vector(1, contravariant_vectors,56i, j, k, element)57contravariant_flux1 = Ja11 * flux1 + Ja12 * flux2 + Ja13 * flux358for ii in eachnode(dg)59multiply_add_to_node_vars!(du, alpha * derivative_hat[ii, i],60contravariant_flux1, equations, dg,61ii, j, k, element)62end6364# Compute the contravariant flux by taking the scalar product of the65# second contravariant vector Ja^2 and the flux vector66Ja21, Ja22, Ja23 = get_contravariant_vector(2, contravariant_vectors,67i, j, k, element)68contravariant_flux2 = Ja21 * flux1 + Ja22 * flux2 + Ja23 * flux369for jj in eachnode(dg)70multiply_add_to_node_vars!(du, alpha * derivative_hat[jj, j],71contravariant_flux2, equations, dg,72i, jj, k, element)73end7475# Compute the contravariant flux by taking the scalar product of the76# third contravariant vector Ja^3 and the flux vector77Ja31, Ja32, Ja33 = get_contravariant_vector(3, contravariant_vectors,78i, j, k, element)79contravariant_flux3 = Ja31 * flux1 + Ja32 * flux2 + Ja33 * flux380for kk in eachnode(dg)81multiply_add_to_node_vars!(du, alpha * derivative_hat[kk, k],82contravariant_flux3, equations, dg,83i, j, kk, element)84end85end8687return nothing88end8990# flux differencing volume integral on curvilinear hexahedral elements. Averaging of the91# mapping terms, stored in `contravariant_vectors`, is peeled apart from the evaluation of92# the physical fluxes in each Cartesian direction93@inline function flux_differencing_kernel!(du, u, element,94::Type{<:Union{StructuredMesh{3},95P4estMesh{3},96T8codeMesh{3}}},97have_nonconservative_terms::False, equations,98volume_flux, dg::DGSEM, cache, alpha = true)99# true * [some floating point value] == [exactly the same floating point value]100# This can (hopefully) be optimized away due to constant propagation.101@unpack derivative_split = dg.basis102@unpack contravariant_vectors = cache.elements103104# Calculate volume integral in one element105for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)106u_node = get_node_vars(u, equations, dg, i, j, k, element)107108# pull the contravariant vectors in each coordinate direction109Ja1_node = get_contravariant_vector(1, contravariant_vectors, i, j, k, element)110Ja2_node = get_contravariant_vector(2, contravariant_vectors, i, j, k, element)111Ja3_node = get_contravariant_vector(3, contravariant_vectors, i, j, k, element)112113# All diagonal entries of `derivative_split` are zero. Thus, we can skip114# the computation of the diagonal terms. In addition, we use the symmetry115# of the `volume_flux` to save half of the possible two-point flux116# computations.117118# x direction119for ii in (i + 1):nnodes(dg)120u_node_ii = get_node_vars(u, equations, dg, ii, j, k, element)121# pull the contravariant vectors and compute the average122Ja1_node_ii = get_contravariant_vector(1, contravariant_vectors,123ii, j, k, element)124# average mapping terms in first coordinate direction,125# used as normal vector in the flux computation126Ja1_avg = 0.5f0 * (Ja1_node + Ja1_node_ii)127# compute the contravariant sharp flux in the direction of the128# averaged contravariant vector129fluxtilde1 = volume_flux(u_node, u_node_ii, Ja1_avg, equations)130multiply_add_to_node_vars!(du, alpha * derivative_split[i, ii], fluxtilde1,131equations, dg, i, j, k, element)132multiply_add_to_node_vars!(du, alpha * derivative_split[ii, i], fluxtilde1,133equations, dg, ii, j, k, element)134end135136# y direction137for jj in (j + 1):nnodes(dg)138u_node_jj = get_node_vars(u, equations, dg, i, jj, k, element)139# pull the contravariant vectors and compute the average140Ja2_node_jj = get_contravariant_vector(2, contravariant_vectors,141i, jj, k, element)142# average mapping terms in second coordinate direction,143# used as normal vector in the flux computation144Ja2_avg = 0.5f0 * (Ja2_node + Ja2_node_jj)145# compute the contravariant sharp flux in the direction of the146# averaged contravariant vector147fluxtilde2 = volume_flux(u_node, u_node_jj, Ja2_avg, equations)148multiply_add_to_node_vars!(du, alpha * derivative_split[j, jj], fluxtilde2,149equations, dg, i, j, k, element)150multiply_add_to_node_vars!(du, alpha * derivative_split[jj, j], fluxtilde2,151equations, dg, i, jj, k, element)152end153154# z direction155for kk in (k + 1):nnodes(dg)156u_node_kk = get_node_vars(u, equations, dg, i, j, kk, element)157# pull the contravariant vectors and compute the average158Ja3_node_kk = get_contravariant_vector(3, contravariant_vectors,159i, j, kk, element)160# average mapping terms in third coordinate direction,161# used as normal vector in the flux computation162Ja3_avg = 0.5f0 * (Ja3_node + Ja3_node_kk)163# compute the contravariant sharp flux in the direction of the164# averaged contravariant vector165fluxtilde3 = volume_flux(u_node, u_node_kk, Ja3_avg, equations)166multiply_add_to_node_vars!(du, alpha * derivative_split[k, kk], fluxtilde3,167equations, dg, i, j, k, element)168multiply_add_to_node_vars!(du, alpha * derivative_split[kk, k], fluxtilde3,169equations, dg, i, j, kk, element)170end171end172173return nothing174end175176@inline function flux_differencing_kernel!(du, u, element,177MeshT::Type{<:Union{StructuredMesh{3},178P4estMesh{3},179T8codeMesh{3}}},180have_nonconservative_terms::True, equations,181volume_flux, dg::DGSEM, cache, alpha = true)182flux_differencing_kernel!(du, u, element, MeshT, have_nonconservative_terms,183combine_conservative_and_nonconservative_fluxes(volume_flux,184equations),185equations, volume_flux, dg, cache, alpha)186187return nothing188end189190@inline function flux_differencing_kernel!(du, u, element,191MeshT::Type{<:Union{StructuredMesh{3},192P4estMesh{3},193T8codeMesh{3}}},194have_nonconservative_terms::True,195combine_conservative_and_nonconservative_fluxes::False,196equations,197volume_flux, dg::DGSEM, cache, alpha = true)198@unpack derivative_split = dg.basis199@unpack contravariant_vectors = cache.elements200symmetric_flux, nonconservative_flux = volume_flux201202# Apply the symmetric flux as usual203flux_differencing_kernel!(du, u, element, MeshT, False(), equations, symmetric_flux,204dg, cache, alpha)205206# Calculate the remaining volume terms using the nonsymmetric generalized flux207for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)208u_node = get_node_vars(u, equations, dg, i, j, k, element)209210# pull the contravariant vectors in each coordinate direction211Ja1_node = get_contravariant_vector(1, contravariant_vectors, i, j, k, element)212Ja2_node = get_contravariant_vector(2, contravariant_vectors, i, j, k, element)213Ja3_node = get_contravariant_vector(3, contravariant_vectors, i, j, k, element)214215# The diagonal terms are zero since the diagonal of `derivative_split`216# is zero. We ignore this for now.217# In general, nonconservative fluxes can depend on both the contravariant218# vectors (normal direction) at the current node and the averaged ones.219# Thus, we need to pass both to the nonconservative flux.220221# x direction222integral_contribution = zero(u_node)223for ii in eachnode(dg)224u_node_ii = get_node_vars(u, equations, dg, ii, j, k, element)225# pull the contravariant vectors and compute the average226Ja1_node_ii = get_contravariant_vector(1, contravariant_vectors,227ii, j, k, element)228# average mapping terms in first coordinate direction,229# used as normal vector in the flux computation230Ja1_avg = 0.5f0 * (Ja1_node + Ja1_node_ii)231# compute the contravariant nonconservative flux in the direction of the232# averaged contravariant vector233fluxtilde1 = nonconservative_flux(u_node, u_node_ii, Ja1_avg,234equations)235integral_contribution = integral_contribution +236derivative_split[i, ii] * fluxtilde1237end238239# y direction240for jj in eachnode(dg)241u_node_jj = get_node_vars(u, equations, dg, i, jj, k, element)242# pull the contravariant vectors and compute the average243Ja2_node_jj = get_contravariant_vector(2, contravariant_vectors,244i, jj, k, element)245# average mapping terms in second coordinate direction,246# used as normal vector in the flux computation247Ja2_avg = 0.5f0 * (Ja2_node + Ja2_node_jj)248# compute the contravariant nonconservative flux in the direction of the249# averaged contravariant vector250fluxtilde2 = nonconservative_flux(u_node, u_node_jj, Ja2_avg,251equations)252integral_contribution = integral_contribution +253derivative_split[j, jj] * fluxtilde2254end255256# z direction257for kk in eachnode(dg)258u_node_kk = get_node_vars(u, equations, dg, i, j, kk, element)259# pull the contravariant vectors and compute the average260Ja3_node_kk = get_contravariant_vector(3, contravariant_vectors,261i, j, kk, element)262# average mapping terms in third coordinate direction,263# used as normal vector in the flux computation264Ja3_avg = 0.5f0 * (Ja3_node + Ja3_node_kk)265# compute the contravariant nonconservative flux in the direction of the266# averaged contravariant vector267fluxtilde3 = nonconservative_flux(u_node, u_node_kk, Ja3_avg,268equations)269integral_contribution = integral_contribution +270derivative_split[k, kk] * fluxtilde3271end272273# The factor 0.5 cancels the factor 2 in the flux differencing form274multiply_add_to_node_vars!(du, alpha * 0.5f0, integral_contribution, equations,275dg, i, j, k, element)276end277278return nothing279end280281@inline function flux_differencing_kernel!(du, u, element,282::Type{<:Union{StructuredMesh{3},283P4estMesh{3},284T8codeMesh{3}}},285have_nonconservative_terms::True,286combine_conservative_and_nonconservative_fluxes::True,287equations,288volume_flux, dg::DGSEM, cache, alpha = true)289@unpack derivative_split = dg.basis290@unpack contravariant_vectors = cache.elements291292# Calculate volume integral in one element293for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)294u_node = get_node_vars(u, equations, dg, i, j, k, element)295296# pull the contravariant vectors in each coordinate direction297Ja1_node = get_contravariant_vector(1, contravariant_vectors, i, j, k, element)298Ja2_node = get_contravariant_vector(2, contravariant_vectors, i, j, k, element)299Ja3_node = get_contravariant_vector(3, contravariant_vectors, i, j, k, element)300301# All diagonal entries of `derivative_split` are zero. Thus, we can skip302# the computation of the diagonal terms. In addition, we use the symmetry303# of the `volume_flux` to save half of the possible two-point flux304# computations.305306# x direction307for ii in (i + 1):nnodes(dg)308u_node_ii = get_node_vars(u, equations, dg, ii, j, k, element)309# pull the contravariant vectors and compute the average310Ja1_node_ii = get_contravariant_vector(1, contravariant_vectors,311ii, j, k, element)312# average mapping terms in first coordinate direction,313# used as normal vector in the flux computation314Ja1_avg = 0.5f0 * (Ja1_node + Ja1_node_ii)315# compute the contravariant sharp flux in the direction of the316# averaged contravariant vector317fluxtilde1_left, fluxtilde1_right = volume_flux(u_node, u_node_ii, Ja1_avg,318equations)319multiply_add_to_node_vars!(du, alpha * derivative_split[i, ii],320fluxtilde1_left,321equations, dg, i, j, k, element)322multiply_add_to_node_vars!(du, alpha * derivative_split[ii, i],323fluxtilde1_right,324equations, dg, ii, j, k, element)325end326327# y direction328for jj in (j + 1):nnodes(dg)329u_node_jj = get_node_vars(u, equations, dg, i, jj, k, element)330# pull the contravariant vectors and compute the average331Ja2_node_jj = get_contravariant_vector(2, contravariant_vectors,332i, jj, k, element)333# average mapping terms in second coordinate direction,334# used as normal vector in the flux computation335Ja2_avg = 0.5f0 * (Ja2_node + Ja2_node_jj)336# compute the contravariant sharp flux in the direction of the337# averaged contravariant vector338fluxtilde2_left, fluxtilde2_right = volume_flux(u_node, u_node_jj, Ja2_avg,339equations)340multiply_add_to_node_vars!(du, alpha * derivative_split[j, jj],341fluxtilde2_left,342equations, dg, i, j, k, element)343multiply_add_to_node_vars!(du, alpha * derivative_split[jj, j],344fluxtilde2_right,345equations, dg, i, jj, k, element)346end347348# z direction349for kk in (k + 1):nnodes(dg)350u_node_kk = get_node_vars(u, equations, dg, i, j, kk, element)351# pull the contravariant vectors and compute the average352Ja3_node_kk = get_contravariant_vector(3, contravariant_vectors,353i, j, kk, element)354# average mapping terms in third coordinate direction,355# used as normal vector in the flux computation356Ja3_avg = 0.5f0 * (Ja3_node + Ja3_node_kk)357# compute the contravariant sharp flux in the direction of the358# averaged contravariant vector359fluxtilde3_left, fluxtilde3_right = volume_flux(u_node, u_node_kk, Ja3_avg,360equations)361multiply_add_to_node_vars!(du, alpha * derivative_split[k, kk],362fluxtilde3_left,363equations, dg, i, j, k, element)364multiply_add_to_node_vars!(du, alpha * derivative_split[kk, k],365fluxtilde3_right,366equations, dg, i, j, kk, element)367end368end369return nothing370end371372# Compute the normal flux for the FV method on curvilinear subcells, see373# Hennemann, Rueda-RamÃrez, Hindenlang, Gassner (2020)374# "A provably entropy stable subcell shock capturing approach for high order split form DG for the compressible Euler equations"375# [arXiv: 2008.12044v2](https://arxiv.org/pdf/2008.12044)376@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R,377fstar3_L, fstar3_R, u,378::Type{<:Union{StructuredMesh{3}, P4estMesh{3},379T8codeMesh{3}}},380have_nonconservative_terms::False,381equations, volume_flux_fv, dg::DGSEM, element, cache)382@unpack contravariant_vectors = cache.elements383@unpack weights, derivative_matrix = dg.basis384@unpack normal_vectors_1, normal_vectors_2, normal_vectors_3 = cache.normal_vectors385386for k in eachnode(dg), j in eachnode(dg), i in 2:nnodes(dg)387u_ll = get_node_vars(u, equations, dg, i - 1, j, k, element)388u_rr = get_node_vars(u, equations, dg, i, j, k, element)389390# Fetch precomputed freestream-preserving normal vector391# We access i - 1 here since the normal vector for i = 1 is not used and stored392normal_direction = get_normal_vector(normal_vectors_1,393i - 1, j, k, element)394395# Compute the contravariant flux396contravariant_flux = volume_flux_fv(u_ll, u_rr, normal_direction, equations)397398set_node_vars!(fstar1_L, contravariant_flux, equations, dg, i, j, k)399set_node_vars!(fstar1_R, contravariant_flux, equations, dg, i, j, k)400end401402for k in eachnode(dg), j in 2:nnodes(dg), i in eachnode(dg)403u_ll = get_node_vars(u, equations, dg, i, j - 1, k, element)404u_rr = get_node_vars(u, equations, dg, i, j, k, element)405406# Fetch precomputed freestream-preserving normal vector407# We access j - 1 here since the normal vector for j = 1 is not used and stored408normal_direction = get_normal_vector(normal_vectors_2,409i, j - 1, k, element)410411# Compute the contravariant flux412contravariant_flux = volume_flux_fv(u_ll, u_rr, normal_direction, equations)413414set_node_vars!(fstar2_L, contravariant_flux, equations, dg, i, j, k)415set_node_vars!(fstar2_R, contravariant_flux, equations, dg, i, j, k)416end417418for k in 2:nnodes(dg), j in eachnode(dg), i in eachnode(dg)419u_ll = get_node_vars(u, equations, dg, i, j, k - 1, element)420u_rr = get_node_vars(u, equations, dg, i, j, k, element)421422# Fetch precomputed freestream-preserving normal vector423# We access k - 1 here since the normal vector for k = 1 is not used and stored424normal_direction = get_normal_vector(normal_vectors_3,425i, j, k - 1, element)426427# Compute the contravariant flux428contravariant_flux = volume_flux_fv(u_ll, u_rr, normal_direction, equations)429430set_node_vars!(fstar3_L, contravariant_flux, equations, dg, i, j, k)431set_node_vars!(fstar3_R, contravariant_flux, equations, dg, i, j, k)432end433434return nothing435end436437@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R,438fstar3_L, fstar3_R, u,439::Type{<:Union{StructuredMesh{3}, P4estMesh{3},440T8codeMesh{3}}},441have_nonconservative_terms::True,442equations, volume_flux_fv, dg::DGSEM, element, cache)443@unpack contravariant_vectors = cache.elements444@unpack weights, derivative_matrix = dg.basis445@unpack normal_vectors_1, normal_vectors_2, normal_vectors_3 = cache.normal_vectors446447volume_flux, nonconservative_flux = volume_flux_fv448449for k in eachnode(dg), j in eachnode(dg), i in 2:nnodes(dg)450u_ll = get_node_vars(u, equations, dg, i - 1, j, k, element)451u_rr = get_node_vars(u, equations, dg, i, j, k, element)452453# Fetch precomputed freestream-preserving normal vector454# We access i - 1 here since the normal vector for i = 1 is not used and stored455normal_direction = get_normal_vector(normal_vectors_1,456i - 1, j, k, element)457458# Compute the contravariant conservative flux459ftilde = volume_flux(u_ll, u_rr, normal_direction, equations)460461# Compute and add in the nonconservative part462# Note the factor 0.5 necessary for the nonconservative fluxes based on463# the interpretation of global SBP operators coupled discontinuously via464# central fluxes/SATs465ftilde_L = ftilde +4660.5f0 *467nonconservative_flux(u_ll, u_rr, normal_direction, equations)468ftilde_R = ftilde +4690.5f0 *470nonconservative_flux(u_rr, u_ll, normal_direction, equations)471472set_node_vars!(fstar1_L, ftilde_L, equations, dg, i, j, k)473set_node_vars!(fstar1_R, ftilde_R, equations, dg, i, j, k)474end475476for k in eachnode(dg), j in 2:nnodes(dg), i in eachnode(dg)477u_ll = get_node_vars(u, equations, dg, i, j - 1, k, element)478u_rr = get_node_vars(u, equations, dg, i, j, k, element)479480# Fetch precomputed freestream-preserving normal vector481# We access j - 1 here since the normal vector for j = 1 is not used and stored482normal_direction = get_normal_vector(normal_vectors_2,483i, j - 1, k, element)484485# Compute the contravariant conservative flux486ftilde = volume_flux(u_ll, u_rr, normal_direction, equations)487488# Compute and add in the nonconservative part489# Note the factor 0.5 necessary for the nonconservative fluxes based on490# the interpretation of global SBP operators coupled discontinuously via491# central fluxes/SATs492ftilde_L = ftilde +4930.5f0 *494nonconservative_flux(u_ll, u_rr, normal_direction, equations)495ftilde_R = ftilde +4960.5f0 *497nonconservative_flux(u_rr, u_ll, normal_direction, equations)498499set_node_vars!(fstar2_L, ftilde_L, equations, dg, i, j, k)500set_node_vars!(fstar2_R, ftilde_R, equations, dg, i, j, k)501end502503for k in 2:nnodes(dg), j in eachnode(dg), i in eachnode(dg)504u_ll = get_node_vars(u, equations, dg, i, j, k - 1, element)505u_rr = get_node_vars(u, equations, dg, i, j, k, element)506507# Fetch precomputed freestream-preserving normal vector508# We access k - 1 here since the normal vector for k = 1 is not used and stored509normal_direction = get_normal_vector(normal_vectors_3,510i, j, k - 1, element)511512# Compute the contravariant conservative flux513ftilde = volume_flux(u_ll, u_rr, normal_direction, equations)514515# Compute and add in the nonconservative part516# Note the factor 0.5 necessary for the nonconservative fluxes based on517# the interpretation of global SBP operators coupled discontinuously via518# central fluxes/SATs519ftilde_L = ftilde +5200.5f0 *521nonconservative_flux(u_ll, u_rr, normal_direction, equations)522ftilde_R = ftilde +5230.5f0 *524nonconservative_flux(u_rr, u_ll, normal_direction, equations)525526set_node_vars!(fstar3_L, ftilde_L, equations, dg, i, j, k)527set_node_vars!(fstar3_R, ftilde_R, equations, dg, i, j, k)528end529530return nothing531end532533@inline function calcflux_fvO2!(fstar1_L, fstar1_R, fstar2_L, fstar2_R,534fstar3_L, fstar3_R, u,535::Type{<:Union{StructuredMesh{3}, P4estMesh{3},536T8codeMesh{3}}},537have_nonconservative_terms::False, equations,538volume_flux_fv, dg::DGSEM, element, cache,539sc_interface_coords, reconstruction_mode, slope_limiter,540cons2recon, recon2cons)541@unpack normal_vectors_1, normal_vectors_2, normal_vectors_3 = cache.normal_vectors542543for k in eachnode(dg), j in eachnode(dg), i in 2:nnodes(dg)544u_ll = cons2recon(get_node_vars(u, equations, dg, max(1, i - 2), j, k,545element), equations)546u_lr = cons2recon(get_node_vars(u, equations, dg, i - 1, j, k,547element), equations)548u_rl = cons2recon(get_node_vars(u, equations, dg, i, j, k,549element), equations)550u_rr = cons2recon(get_node_vars(u, equations, dg, min(nnodes(dg), i + 1), j, k,551element), equations)552553u_l, u_r = reconstruction_mode(u_ll, u_lr, u_rl, u_rr,554sc_interface_coords, i,555slope_limiter, dg)556557normal_direction = get_normal_vector(normal_vectors_1, i - 1, j, k, element)558559contravariant_flux = volume_flux_fv(recon2cons(u_l, equations),560recon2cons(u_r, equations),561normal_direction, equations)562563set_node_vars!(fstar1_L, contravariant_flux, equations, dg, i, j, k)564set_node_vars!(fstar1_R, contravariant_flux, equations, dg, i, j, k)565end566567for k in eachnode(dg), j in 2:nnodes(dg), i in eachnode(dg)568u_ll = cons2recon(get_node_vars(u, equations, dg, i, max(1, j - 2), k,569element), equations)570u_lr = cons2recon(get_node_vars(u, equations, dg, i, j - 1, k,571element), equations)572u_rl = cons2recon(get_node_vars(u, equations, dg, i, j, k,573element), equations)574u_rr = cons2recon(get_node_vars(u, equations, dg, i, min(nnodes(dg), j + 1), k,575element), equations)576577u_l, u_r = reconstruction_mode(u_ll, u_lr, u_rl, u_rr,578sc_interface_coords, j,579slope_limiter, dg)580581normal_direction = get_normal_vector(normal_vectors_2, i, j - 1, k, element)582583contravariant_flux = volume_flux_fv(recon2cons(u_l, equations),584recon2cons(u_r, equations),585normal_direction, equations)586587set_node_vars!(fstar2_L, contravariant_flux, equations, dg, i, j, k)588set_node_vars!(fstar2_R, contravariant_flux, equations, dg, i, j, k)589end590591for k in 2:nnodes(dg), j in eachnode(dg), i in eachnode(dg)592u_ll = cons2recon(get_node_vars(u, equations, dg, i, j, max(1, k - 2),593element), equations)594u_lr = cons2recon(get_node_vars(u, equations, dg, i, j, k - 1,595element), equations)596u_rl = cons2recon(get_node_vars(u, equations, dg, i, j, k,597element), equations)598u_rr = cons2recon(get_node_vars(u, equations, dg, i, j, min(nnodes(dg), k + 1),599element), equations)600601u_l, u_r = reconstruction_mode(u_ll, u_lr, u_rl, u_rr,602sc_interface_coords, k,603slope_limiter, dg)604605normal_direction = get_normal_vector(normal_vectors_3, i, j, k - 1, element)606607contravariant_flux = volume_flux_fv(recon2cons(u_l, equations),608recon2cons(u_r, equations),609normal_direction, equations)610611set_node_vars!(fstar3_L, contravariant_flux, equations, dg, i, j, k)612set_node_vars!(fstar3_R, contravariant_flux, equations, dg, i, j, k)613end614615return nothing616end617618function prolong2interfaces!(cache, u, mesh::StructuredMesh{3}, equations, dg::DG)619@unpack interfaces_u = cache.elements620621@threaded for element in eachelement(dg, cache)622for j in eachnode(dg), i in eachnode(dg)623# Negative x-direction (direction 1, left/negative x face)624# Face nodes (i, j) correspond to (y, z) directions625for v in eachvariable(equations)626interfaces_u[v, i, j, 1, element] = u[v, 1, i, j, element]627end628# Positive x-direction (direction 2, right/positive x face)629for v in eachvariable(equations)630interfaces_u[v, i, j, 2, element] = u[v, nnodes(dg), i, j, element]631end632# Negative y-direction (direction 3, bottom/negative y face)633# Face nodes (i, j) correspond to (x, z) directions634for v in eachvariable(equations)635interfaces_u[v, i, j, 3, element] = u[v, i, 1, j, element]636end637# Positive y-direction (direction 4, top/positive y face)638for v in eachvariable(equations)639interfaces_u[v, i, j, 4, element] = u[v, i, nnodes(dg), j, element]640end641# Negative z-direction (direction 5, back/negative z face)642# Face nodes (i, j) correspond to (x, y) directions643for v in eachvariable(equations)644interfaces_u[v, i, j, 5, element] = u[v, i, j, 1, element]645end646# Positive z-direction (direction 6, front/positive z face)647for v in eachvariable(equations)648interfaces_u[v, i, j, 6, element] = u[v, i, j, nnodes(dg), element]649end650end651end652653return nothing654end655656function calc_interface_flux!(surface_flux_values, mesh::StructuredMesh{3},657have_nonconservative_terms, # can be True/False658equations, surface_integral, dg::DG, cache)659@unpack elements = cache660661@threaded for element in eachelement(dg, cache)662# Interfaces in negative directions663# Faster version of "for orientation in (1, 2, 3)"664665# Interfaces in x-direction (`orientation` = 1)666calc_interface_flux!(elements.surface_flux_values,667elements.left_neighbors[1, element],668element, 1, mesh,669have_nonconservative_terms, equations,670surface_integral, dg, cache)671672# Interfaces in y-direction (`orientation` = 2)673calc_interface_flux!(elements.surface_flux_values,674elements.left_neighbors[2, element],675element, 2, mesh,676have_nonconservative_terms, equations,677surface_integral, dg, cache)678679# Interfaces in z-direction (`orientation` = 3)680calc_interface_flux!(elements.surface_flux_values,681elements.left_neighbors[3, element],682element, 3, mesh,683have_nonconservative_terms, equations,684surface_integral, dg, cache)685end686687return nothing688end689690@inline function calc_interface_flux!(surface_flux_values, left_element, right_element,691orientation,692mesh::StructuredMesh{3},693have_nonconservative_terms::False, equations,694surface_integral, dg::DG, cache)695# This is slow for LSA, but for some reason faster for Euler (see #519)696if left_element <= 0 # left_element = 0 at boundaries697return surface_flux_values698end699700@unpack surface_flux = surface_integral701@unpack interfaces_u, contravariant_vectors, inverse_jacobian = cache.elements702703right_direction = 2 * orientation704left_direction = right_direction - 1705706for j in eachnode(dg), i in eachnode(dg)707u_ll = get_node_vars(interfaces_u, equations, dg, i, j, right_direction,708left_element)709u_rr = get_node_vars(interfaces_u, equations, dg, i, j, left_direction,710right_element)711712if orientation == 1713# If the mapping is orientation-reversing, the contravariant vectors' orientation714# is reversed as well. The normal vector must be oriented in the direction715# from `left_element` to `right_element`, or the numerical flux will be computed716# incorrectly (downwind direction).717sign_jacobian = sign(inverse_jacobian[1, i, j, right_element])718719# First contravariant vector Ja^1 as SVector720normal_direction = sign_jacobian *721get_contravariant_vector(1, contravariant_vectors,7221, i, j, right_element)723elseif orientation == 2724# See above725sign_jacobian = sign(inverse_jacobian[i, 1, j, right_element])726727# Second contravariant vector Ja^2 as SVector728normal_direction = sign_jacobian *729get_contravariant_vector(2, contravariant_vectors,730i, 1, j, right_element)731else # orientation == 3732# See above733sign_jacobian = sign(inverse_jacobian[i, j, 1, right_element])734735# Third contravariant vector Ja^3 as SVector736normal_direction = sign_jacobian *737get_contravariant_vector(3, contravariant_vectors,738i, j, 1, right_element)739end740741# If the mapping is orientation-reversing, the normal vector will be reversed (see above).742# However, the flux now has the wrong sign, since we need the physical flux in normal direction.743flux = sign_jacobian * surface_flux(u_ll, u_rr, normal_direction, equations)744745for v in eachvariable(equations)746surface_flux_values[v, i, j, right_direction, left_element] = flux[v]747surface_flux_values[v, i, j, left_direction, right_element] = flux[v]748end749end750751return nothing752end753754@inline function calc_interface_flux!(surface_flux_values, left_element, right_element,755orientation,756mesh::StructuredMesh{3},757have_nonconservative_terms::True, equations,758surface_integral, dg::DG, cache)759# See comment on `calc_interface_flux!` with `have_nonconservative_terms::False`760if left_element <= 0 # left_element = 0 at boundaries761return surface_flux_values762end763764surface_flux, nonconservative_flux = surface_integral.surface_flux765@unpack interfaces_u, contravariant_vectors, inverse_jacobian = cache.elements766767right_direction = 2 * orientation768left_direction = right_direction - 1769770for j in eachnode(dg), i in eachnode(dg)771u_ll = get_node_vars(interfaces_u, equations, dg, i, j, right_direction,772left_element)773u_rr = get_node_vars(interfaces_u, equations, dg, i, j, left_direction,774right_element)775776if orientation == 1777# If the mapping is orientation-reversing, the contravariant vectors' orientation778# is reversed as well. The normal vector must be oriented in the direction779# from `left_element` to `right_element`, or the numerical flux will be computed780# incorrectly (downwind direction).781sign_jacobian = sign(inverse_jacobian[1, i, j, right_element])782783# First contravariant vector Ja^1 as SVector784normal_direction = sign_jacobian *785get_contravariant_vector(1, contravariant_vectors,7861, i, j, right_element)787elseif orientation == 2788# See above789sign_jacobian = sign(inverse_jacobian[i, 1, j, right_element])790791# Second contravariant vector Ja^2 as SVector792normal_direction = sign_jacobian *793get_contravariant_vector(2, contravariant_vectors,794i, 1, j, right_element)795else # orientation == 3796# See above797sign_jacobian = sign(inverse_jacobian[i, j, 1, right_element])798799# Third contravariant vector Ja^3 as SVector800normal_direction = sign_jacobian *801get_contravariant_vector(3, contravariant_vectors,802i, j, 1, right_element)803end804805# If the mapping is orientation-reversing, the normal vector will be reversed (see above).806# However, the flux now has the wrong sign, since we need the physical flux in normal direction.807flux = sign_jacobian * surface_flux(u_ll, u_rr, normal_direction, equations)808809# Compute both nonconservative fluxes810# Scale with sign_jacobian to ensure that the normal_direction matches that811# from the flux above812noncons_left = sign_jacobian *813nonconservative_flux(u_ll, u_rr, normal_direction, equations)814noncons_right = sign_jacobian *815nonconservative_flux(u_rr, u_ll, normal_direction, equations)816817for v in eachvariable(equations)818# Note the factor 0.5 necessary for the nonconservative fluxes based on819# the interpretation of global SBP operators coupled discontinuously via820# central fluxes/SATs821surface_flux_values[v, i, j, right_direction, left_element] = flux[v] +8220.5f0 *823noncons_left[v]824surface_flux_values[v, i, j, left_direction, right_element] = flux[v] +8250.5f0 *826noncons_right[v]827end828end829830return nothing831end832833function calc_boundary_flux!(cache, t, boundary_conditions::NamedTuple,834mesh::StructuredMesh{3}, equations, surface_integral,835dg::DG)836@unpack surface_flux_values = cache.elements837linear_indices = LinearIndices(size(mesh))838839for cell_z in axes(mesh, 3), cell_y in axes(mesh, 2)840# Negative x-direction841direction = 1842element = linear_indices[begin, cell_y, cell_z]843844for k in eachnode(dg), j in eachnode(dg)845calc_boundary_flux_by_direction!(surface_flux_values, t, 1,846boundary_conditions[direction],847mesh,848have_nonconservative_terms(equations),849equations, surface_integral, dg,850cache,851direction, (1, j, k), (j, k), element)852end853854# Positive x-direction855direction = 2856element = linear_indices[end, cell_y, cell_z]857858for k in eachnode(dg), j in eachnode(dg)859calc_boundary_flux_by_direction!(surface_flux_values, t, 1,860boundary_conditions[direction],861mesh,862have_nonconservative_terms(equations),863equations, surface_integral, dg,864cache,865direction, (nnodes(dg), j, k), (j, k),866element)867end868end869870for cell_z in axes(mesh, 3), cell_x in axes(mesh, 1)871# Negative y-direction872direction = 3873element = linear_indices[cell_x, begin, cell_z]874875for k in eachnode(dg), i in eachnode(dg)876calc_boundary_flux_by_direction!(surface_flux_values, t, 2,877boundary_conditions[direction],878mesh,879have_nonconservative_terms(equations),880equations, surface_integral, dg,881cache,882direction, (i, 1, k), (i, k), element)883end884885# Positive y-direction886direction = 4887element = linear_indices[cell_x, end, cell_z]888889for k in eachnode(dg), i in eachnode(dg)890calc_boundary_flux_by_direction!(surface_flux_values, t, 2,891boundary_conditions[direction],892mesh,893have_nonconservative_terms(equations),894equations, surface_integral, dg,895cache,896direction, (i, nnodes(dg), k), (i, k),897element)898end899end900901for cell_y in axes(mesh, 2), cell_x in axes(mesh, 1)902# Negative z-direction903direction = 5904element = linear_indices[cell_x, cell_y, begin]905906for j in eachnode(dg), i in eachnode(dg)907calc_boundary_flux_by_direction!(surface_flux_values, t, 3,908boundary_conditions[direction],909mesh,910have_nonconservative_terms(equations),911equations, surface_integral, dg,912cache,913direction, (i, j, 1), (i, j), element)914end915916# Positive z-direction917direction = 6918element = linear_indices[cell_x, cell_y, end]919920for j in eachnode(dg), i in eachnode(dg)921calc_boundary_flux_by_direction!(surface_flux_values, t, 3,922boundary_conditions[direction],923mesh,924have_nonconservative_terms(equations),925equations, surface_integral, dg,926cache,927direction, (i, j, nnodes(dg)), (i, j),928element)929end930end931932return nothing933end934935function apply_jacobian!(backend::Nothing, du,936mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}},937equations, dg::DG, cache)938@unpack inverse_jacobian = cache.elements939@threaded for element in eachelement(dg, cache)940apply_jacobian_per_element!(du, typeof(mesh), equations, dg, inverse_jacobian,941element)942end943return nothing944end945946function apply_jacobian!(backend::Backend, du,947mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}},948equations, dg::DG, cache)949@unpack inverse_jacobian = cache.elements950951kernel! = apply_jacobian_KAkernel!(backend)952kernel!(du, typeof(mesh), equations, dg, inverse_jacobian,953ndrange = nelements(cache.elements))954return nothing955end956957@kernel function apply_jacobian_KAkernel!(du, MeshT, equations, dg::DG,958inverse_jacobian)959element = @index(Global)960apply_jacobian_per_element!(du, MeshT, equations, dg, inverse_jacobian, element)961end962963@inline function apply_jacobian_per_element!(du,964::Type{<:Union{StructuredMesh{3},965P4estMesh{3},966T8codeMesh{3}}},967equations, dg, inverse_jacobian, element)968for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)969# Negative sign included to account for the negated surface and volume terms,970# see e.g. the computation of `derivative_hat` in the basis setup and971# the comment in `calc_surface_integral!`.972factor = -inverse_jacobian[i, j, k, element]973974for v in eachvariable(equations)975du[v, i, j, k, element] *= factor976end977end978return nothing979end980end # @muladd981982983