Path: blob/main/src/solvers/dgsem_structured/dg_2d.jl
5590 views
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).1# Since these FMAs can increase the performance of many numerical algorithms,2# we need to opt-in explicitly.3# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.4@muladd begin5#! format: noindent67function create_cache(mesh::Union{StructuredMesh{2}, UnstructuredMesh2D,8P4estMesh{2}, T8codeMesh{2}}, equations,9volume_integral::AbstractVolumeIntegralSubcell,10dg::DG, cache_containers, uEltype)11fstar1_L_threaded, fstar1_R_threaded,12fstar2_L_threaded, fstar2_R_threaded = create_f_threaded(mesh, equations, dg,13uEltype)1415normal_vectors = NormalVectorContainer2D(mesh, dg, cache_containers)1617cache_subcell_limiting = create_cache_subcell_limiting(mesh, equations,18volume_integral, dg,19cache_containers, uEltype)2021return (; fstar1_L_threaded, fstar1_R_threaded,22fstar2_L_threaded, fstar2_R_threaded,23normal_vectors, cache_subcell_limiting...)24end2526#=27`weak_form_kernel!` is only implemented for conserved terms as28non-conservative terms should always be discretized in conjunction with a flux-splitting scheme,29see `flux_differencing_kernel!`.30This treatment is required to achieve, e.g., entropy-stability or well-balancedness.31See also https://github.com/trixi-framework/Trixi.jl/issues/1671#issuecomment-176564406432=#33@inline function weak_form_kernel!(du, u,34element,35::Type{<:Union{StructuredMesh{2},36StructuredMeshView{2},37UnstructuredMesh2D, P4estMesh{2},38P4estMeshView{2}, T8codeMesh{2}}},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 j in eachnode(dg), i in eachnode(dg)47u_node = get_node_vars(u, equations, dg, i, j, element)4849flux1 = flux(u_node, 1, equations)50flux2 = flux(u_node, 2, equations)5152# Compute the contravariant flux by taking the scalar product of the53# first contravariant vector Ja^1 and the flux vector54Ja11, Ja12 = get_contravariant_vector(1, contravariant_vectors, i, j, element)55contravariant_flux1 = Ja11 * flux1 + Ja12 * flux256for ii in eachnode(dg)57multiply_add_to_node_vars!(du, alpha * derivative_hat[ii, i],58contravariant_flux1, equations, dg,59ii, j, element)60end6162# Compute the contravariant flux by taking the scalar product of the63# second contravariant vector Ja^2 and the flux vector64Ja21, Ja22 = get_contravariant_vector(2, contravariant_vectors, i, j, element)65contravariant_flux2 = Ja21 * flux1 + Ja22 * flux266for jj in eachnode(dg)67multiply_add_to_node_vars!(du, alpha * derivative_hat[jj, j],68contravariant_flux2, equations, dg,69i, jj, element)70end71end7273return nothing74end7576@inline function flux_differencing_kernel!(du, u, element,77::Type{<:Union{StructuredMesh{2},78StructuredMeshView{2},79UnstructuredMesh2D,80P4estMesh{2},81T8codeMesh{2}}},82have_nonconservative_terms::False, equations,83volume_flux, dg::DGSEM, cache, alpha = true)84@unpack derivative_split = dg.basis85@unpack contravariant_vectors = cache.elements8687# Calculate volume integral in one element88for j in eachnode(dg), i in eachnode(dg)89u_node = get_node_vars(u, equations, dg, i, j, element)9091# pull the contravariant vectors in each coordinate direction92Ja1_node = get_contravariant_vector(1, contravariant_vectors, i, j, element)93Ja2_node = get_contravariant_vector(2, contravariant_vectors, i, j, element)9495# All diagonal entries of `derivative_split` are zero. Thus, we can skip96# the computation of the diagonal terms. In addition, we use the symmetry97# of the `volume_flux` to save half of the possible two-point flux98# computations.99100# x direction101for ii in (i + 1):nnodes(dg)102u_node_ii = get_node_vars(u, equations, dg, ii, j, element)103# pull the contravariant vectors and compute the average104Ja1_node_ii = get_contravariant_vector(1, contravariant_vectors,105ii, j, element)106# average mapping terms in first coordinate direction,107# used as normal vector in the flux computation108Ja1_avg = 0.5f0 * (Ja1_node + Ja1_node_ii)109# compute the contravariant sharp flux in the direction of the110# averaged contravariant vector111fluxtilde1 = volume_flux(u_node, u_node_ii, Ja1_avg, equations)112multiply_add_to_node_vars!(du, alpha * derivative_split[i, ii], fluxtilde1,113equations, dg, i, j, element)114multiply_add_to_node_vars!(du, alpha * derivative_split[ii, i], fluxtilde1,115equations, dg, ii, j, element)116end117118# y direction119for jj in (j + 1):nnodes(dg)120u_node_jj = get_node_vars(u, equations, dg, i, jj, element)121# pull the contravariant vectors and compute the average122Ja2_node_jj = get_contravariant_vector(2, contravariant_vectors,123i, jj, element)124# average mapping terms in second coordinate direction,125# used as normal vector in the flux computation126Ja2_avg = 0.5f0 * (Ja2_node + Ja2_node_jj)127# compute the contravariant sharp flux in the direction of the128# averaged contravariant vector129fluxtilde2 = volume_flux(u_node, u_node_jj, Ja2_avg, equations)130multiply_add_to_node_vars!(du, alpha * derivative_split[j, jj], fluxtilde2,131equations, dg, i, j, element)132multiply_add_to_node_vars!(du, alpha * derivative_split[jj, j], fluxtilde2,133equations, dg, i, jj, element)134end135end136137return nothing138end139140@inline function flux_differencing_kernel!(du, u, element,141MeshT::Type{<:Union{StructuredMesh{2},142StructuredMeshView{2},143UnstructuredMesh2D,144P4estMesh{2},145T8codeMesh{2}}},146have_nonconservative_terms::True, equations,147volume_flux, dg::DGSEM, cache, alpha = true)148flux_differencing_kernel!(du, u, element, MeshT, have_nonconservative_terms,149combine_conservative_and_nonconservative_fluxes(volume_flux,150equations),151equations,152volume_flux,153dg, cache, alpha)154return nothing155end156157@inline function flux_differencing_kernel!(du, u, element,158MeshT::Type{<:Union{StructuredMesh{2},159StructuredMeshView{2},160UnstructuredMesh2D,161P4estMesh{2},162T8codeMesh{2}}},163have_nonconservative_terms::True,164combine_conservative_and_nonconservative_fluxes::False,165equations,166volume_flux, dg::DGSEM, cache, alpha = true)167@unpack derivative_split = dg.basis168@unpack contravariant_vectors = cache.elements169symmetric_flux, nonconservative_flux = volume_flux170171# Apply the symmetric flux as usual172flux_differencing_kernel!(du, u, element, MeshT, False(), equations, symmetric_flux,173dg, cache, alpha)174175# Calculate the remaining volume terms using the nonsymmetric generalized flux176for j in eachnode(dg), i in eachnode(dg)177u_node = get_node_vars(u, equations, dg, i, j, element)178179# pull the contravariant vectors in each coordinate direction180Ja1_node = get_contravariant_vector(1, contravariant_vectors, i, j, element)181Ja2_node = get_contravariant_vector(2, contravariant_vectors, i, j, element)182183# The diagonal terms are zero since the diagonal of `derivative_split`184# is zero. We ignore this for now.185# In general, nonconservative fluxes can depend on both the contravariant186# vectors (normal direction) at the current node and the averaged ones.187# Thus, we need to pass both to the nonconservative flux.188189# x direction190integral_contribution = zero(u_node)191for ii in eachnode(dg)192u_node_ii = get_node_vars(u, equations, dg, ii, j, element)193# pull the contravariant vectors and compute the average194Ja1_node_ii = get_contravariant_vector(1, contravariant_vectors,195ii, j, element)196# average mapping terms in first coordinate direction,197# used as normal vector in the flux computation198Ja1_avg = 0.5f0 * (Ja1_node + Ja1_node_ii)199# Compute the contravariant nonconservative flux.200fluxtilde1 = nonconservative_flux(u_node, u_node_ii, Ja1_avg,201equations)202integral_contribution = integral_contribution +203derivative_split[i, ii] * fluxtilde1204end205206# y direction207for jj in eachnode(dg)208u_node_jj = get_node_vars(u, equations, dg, i, jj, element)209# pull the contravariant vectors and compute the average210Ja2_node_jj = get_contravariant_vector(2, contravariant_vectors,211i, jj, element)212# average mapping terms in second coordinate direction,213# used as normal vector in the flux computation214Ja2_avg = 0.5f0 * (Ja2_node + Ja2_node_jj)215# compute the contravariant nonconservative flux in the direction of the216# averaged contravariant vector217fluxtilde2 = nonconservative_flux(u_node, u_node_jj, Ja2_avg,218equations)219integral_contribution = integral_contribution +220derivative_split[j, jj] * fluxtilde2221end222223# The factor 0.5 cancels the factor 2 in the flux differencing form224multiply_add_to_node_vars!(du, alpha * 0.5f0, integral_contribution, equations,225dg, i, j, element)226end227228return nothing229end230231@inline function flux_differencing_kernel!(du, u, element,232::Type{<:Union{StructuredMesh{2},233StructuredMeshView{2},234UnstructuredMesh2D,235P4estMesh{2},236T8codeMesh{2}}},237have_nonconservative_terms::True,238combine_conservative_and_nonconservative_fluxes::True,239equations,240volume_flux, dg::DGSEM, cache, alpha = true)241@unpack derivative_split = dg.basis242@unpack contravariant_vectors = cache.elements243244# Calculate volume integral in one element245for j in eachnode(dg), i in eachnode(dg)246u_node = get_node_vars(u, equations, dg, i, j, element)247248# pull the contravariant vectors in each coordinate direction249Ja1_node = get_contravariant_vector(1, contravariant_vectors, i, j, element)250Ja2_node = get_contravariant_vector(2, contravariant_vectors, i, j, element)251252# All diagonal entries of `derivative_split` are zero. Thus, we can skip253# the computation of the diagonal terms. In addition, we use the symmetry254# of the `volume_flux` to save half of the possible two-point flux255# computations.256257# x direction258for ii in (i + 1):nnodes(dg)259u_node_ii = get_node_vars(u, equations, dg, ii, j, element)260# pull the contravariant vectors and compute the average261Ja1_node_ii = get_contravariant_vector(1, contravariant_vectors,262ii, j, element)263# average mapping terms in first coordinate direction,264# used as normal vector in the flux computation265Ja1_avg = 0.5f0 * (Ja1_node + Ja1_node_ii)266# compute the contravariant sharp flux in the direction of the267# averaged contravariant vector268fluxtilde1_left, fluxtilde1_right = volume_flux(u_node, u_node_ii, Ja1_avg,269equations)270multiply_add_to_node_vars!(du, alpha * derivative_split[i, ii],271fluxtilde1_left,272equations, dg, i, j, element)273multiply_add_to_node_vars!(du, alpha * derivative_split[ii, i],274fluxtilde1_right,275equations, dg, ii, j, element)276end277278# y direction279for jj in (j + 1):nnodes(dg)280u_node_jj = get_node_vars(u, equations, dg, i, jj, element)281# pull the contravariant vectors and compute the average282Ja2_node_jj = get_contravariant_vector(2, contravariant_vectors,283i, jj, element)284# average mapping terms in second coordinate direction,285# used as normal vector in the flux computation286Ja2_avg = 0.5f0 * (Ja2_node + Ja2_node_jj)287# compute the contravariant sharp flux in the direction of the288# averaged contravariant vector289fluxtilde2_left, fluxtilde2_right = volume_flux(u_node, u_node_jj, Ja2_avg,290equations)291multiply_add_to_node_vars!(du, alpha * derivative_split[j, jj],292fluxtilde2_left,293equations, dg, i, j, element)294multiply_add_to_node_vars!(du, alpha * derivative_split[jj, j],295fluxtilde2_right,296equations, dg, i, jj, element)297end298end299300return nothing301end302303@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, u,304::Type{<:Union{StructuredMesh{2}, StructuredMeshView{2},305UnstructuredMesh2D,306P4estMesh{2}, T8codeMesh{2}}},307have_nonconservative_terms::False, equations,308volume_flux_fv, dg::DGSEM, element, cache)309@unpack normal_vectors_1, normal_vectors_2 = cache.normal_vectors310311for j in eachnode(dg), i in 2:nnodes(dg)312u_ll = get_node_vars(u, equations, dg, i - 1, j, element)313u_rr = get_node_vars(u, equations, dg, i, j, element)314315# Fetch precomputed freestream-preserving normal vector316# We access i - 1 here since the normal vector for i = 1 is not used and stored317normal_direction = get_normal_vector(normal_vectors_1, i - 1, j, element)318319# Compute the contravariant flux320contravariant_flux = volume_flux_fv(u_ll, u_rr, normal_direction, equations)321322set_node_vars!(fstar1_L, contravariant_flux, equations, dg, i, j)323set_node_vars!(fstar1_R, contravariant_flux, equations, dg, i, j)324end325326for j in 2:nnodes(dg), i in eachnode(dg)327u_ll = get_node_vars(u, equations, dg, i, j - 1, element)328u_rr = get_node_vars(u, equations, dg, i, j, element)329330# Fetch precomputed freestream-preserving normal vector331# We access j - 1 here since the normal vector for j = 1 is not used and stored332normal_direction = get_normal_vector(normal_vectors_2, i, j - 1, element)333334# Compute the contravariant flux by taking the scalar product of the335# normal vector and the flux vector336contravariant_flux = volume_flux_fv(u_ll, u_rr, normal_direction, equations)337338set_node_vars!(fstar2_L, contravariant_flux, equations, dg, i, j)339set_node_vars!(fstar2_R, contravariant_flux, equations, dg, i, j)340end341342return nothing343end344345@inline function calcflux_fvO2!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, u,346::Type{<:Union{StructuredMesh{2}, StructuredMeshView{2},347UnstructuredMesh2D,348P4estMesh{2}, T8codeMesh{2}}},349have_nonconservative_terms::False, equations,350volume_flux_fv, dg::DGSEM, element, cache,351sc_interface_coords, reconstruction_mode, slope_limiter,352cons2recon, recon2cons)353@unpack normal_vectors_1, normal_vectors_2 = cache.normal_vectors354355# We compute FV02 fluxes at the (nnodes(dg) - 1) subcell boundaries356# See `calcflux_fvO2!` in solvers/dgsem_tree/dg_1d.jl for a schematic357358# The left subcell node values are labelled `_ll` (left-left) and `_lr` (left-right), while359# the right subcell node values are labelled `_rl` (right-left) and `_rr` (right-right).360for j in eachnode(dg), i in 2:nnodes(dg)361## Obtain unlimited values in reconstruction variables ##362363# Note: If i - 2 = 0 we do not go to neighbor element, as one would do in a finite volume scheme.364# Here, we keep it purely cell-local, thus overshoots between elements are not strictly ruled out,365# **unless** `reconstruction_mode` is set to `reconstruction_O2_inner`366u_ll = cons2recon(get_node_vars(u, equations, dg, max(1, i - 2), j, element),367equations)368u_lr = cons2recon(get_node_vars(u, equations, dg, i - 1, j, element),369equations)370u_rl = cons2recon(get_node_vars(u, equations, dg, i, j, element),371equations)372# Note: If i + 1 > nnodes(dg) we do not go to neighbor element, as one would do in a finite volume scheme.373# Here, we keep it purely cell-local, thus overshoots between elements are not strictly ruled out,374# **unless** `reconstruction_mode` is set to `reconstruction_O2_inner`375u_rr = cons2recon(get_node_vars(u, equations, dg, min(nnodes(dg), i + 1), j,376element), equations)377378## Reconstruct values at interfaces with limiting ##379u_l, u_r = reconstruction_mode(u_ll, u_lr, u_rl, u_rr,380sc_interface_coords, i,381slope_limiter, dg)382383# Fetch precomputed freestream-preserving normal vector384# We access i - 1 here since the normal vector for i = 1 is not used and stored385normal_direction = get_normal_vector(normal_vectors_1, i - 1, j, element)386387# Compute the contravariant flux by taking the scalar product of the388# normal vector and the flux vector.389## Convert reconstruction variables back to conservative variables ##390contravariant_flux = volume_flux_fv(recon2cons(u_l, equations),391recon2cons(u_r, equations),392normal_direction, equations)393394set_node_vars!(fstar1_L, contravariant_flux, equations, dg, i, j)395set_node_vars!(fstar1_R, contravariant_flux, equations, dg, i, j)396end397398for j in 2:nnodes(dg), i in eachnode(dg)399u_ll = cons2recon(get_node_vars(u, equations, dg, i, max(1, j - 2), element),400equations)401u_lr = cons2recon(get_node_vars(u, equations, dg, i, j - 1, element),402equations)403u_rl = cons2recon(get_node_vars(u, equations, dg, i, j, element),404equations)405u_rr = cons2recon(get_node_vars(u, equations, dg, i, min(nnodes(dg), j + 1),406element), equations)407408u_l, u_r = reconstruction_mode(u_ll, u_lr, u_rl, u_rr,409sc_interface_coords, j,410slope_limiter, dg)411412# We access j - 1 here since the normal vector for j = 1 is not used and stored413normal_direction = get_normal_vector(normal_vectors_2, i, j - 1, element)414415contravariant_flux = volume_flux_fv(recon2cons(u_l, equations),416recon2cons(u_r, equations),417normal_direction, equations)418419set_node_vars!(fstar2_L, contravariant_flux, equations, dg, i, j)420set_node_vars!(fstar2_R, contravariant_flux, equations, dg, i, j)421end422423return nothing424end425426@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, u,427::Type{<:Union{StructuredMesh{2}, StructuredMesh{2},428UnstructuredMesh2D,429P4estMesh{2}, T8codeMesh{2}}},430have_nonconservative_terms::True, equations,431volume_flux_fv, dg::DGSEM, element, cache)432@unpack normal_vectors_1, normal_vectors_2 = cache.normal_vectors433434volume_flux, nonconservative_flux = volume_flux_fv435436# Fluxes in x-direction437for j in eachnode(dg), i in 2:nnodes(dg)438u_ll = get_node_vars(u, equations, dg, i - 1, j, element)439u_rr = get_node_vars(u, equations, dg, i, j, element)440441# Fetch precomputed freestream-preserving normal vector442# We access i - 1 here since the normal vector for i = 1 is not used and stored443normal_direction = get_normal_vector(normal_vectors_1, i - 1, j, element)444445# Compute the conservative part of the contravariant flux446ftilde1 = volume_flux(u_ll, u_rr, normal_direction, equations)447448# Compute and add in the nonconservative part449# Note the factor 0.5 necessary for the nonconservative fluxes based on450# the interpretation of global SBP operators coupled discontinuously via451# central fluxes/SATs452ftilde1_L = ftilde1 +4530.5f0 *454nonconservative_flux(u_ll, u_rr, normal_direction, equations)455ftilde1_R = ftilde1 +4560.5f0 *457nonconservative_flux(u_rr, u_ll, normal_direction, equations)458459set_node_vars!(fstar1_L, ftilde1_L, equations, dg, i, j)460set_node_vars!(fstar1_R, ftilde1_R, equations, dg, i, j)461end462463# Fluxes in y-direction464for j in 2:nnodes(dg), i in eachnode(dg)465u_ll = get_node_vars(u, equations, dg, i, j - 1, element)466u_rr = get_node_vars(u, equations, dg, i, j, element)467468# Fetch precomputed freestream-preserving normal vector469# We access j - 1 here since the normal vector for j = 1 is not used and stored470normal_direction = get_normal_vector(normal_vectors_2, i, j - 1, element)471472# Compute the conservative part of the contravariant flux473ftilde2 = volume_flux(u_ll, u_rr, normal_direction, equations)474475# Compute and add in the nonconservative part476# Note the factor 0.5 necessary for the nonconservative fluxes based on477# the interpretation of global SBP operators coupled discontinuously via478# central fluxes/SATs479ftilde2_L = ftilde2 +4800.5f0 *481nonconservative_flux(u_ll, u_rr, normal_direction, equations)482ftilde2_R = ftilde2 +4830.5f0 *484nonconservative_flux(u_rr, u_ll, normal_direction, equations)485486set_node_vars!(fstar2_L, ftilde2_L, equations, dg, i, j)487set_node_vars!(fstar2_R, ftilde2_R, equations, dg, i, j)488end489490return nothing491end492493function prolong2interfaces!(cache, u,494mesh::Union{StructuredMesh{2}, StructuredMeshView{2}},495equations, dg::DG)496@unpack interfaces_u = cache.elements497498@threaded for element in eachelement(dg, cache)499for i in eachnode(dg)500# Negative x-direction (direction 1, left/negative x face)501for v in eachvariable(equations)502interfaces_u[v, i, 1, element] = u[v, 1, i, element]503end504# Positive x-direction (direction 2, right/positive x face)505for v in eachvariable(equations)506interfaces_u[v, i, 2, element] = u[v, nnodes(dg), i, element]507end508# Negative y-direction (direction 3, bottom/negative y face)509for v in eachvariable(equations)510interfaces_u[v, i, 3, element] = u[v, i, 1, element]511end512# Positive y-direction (direction 4, top/positive y face)513for v in eachvariable(equations)514interfaces_u[v, i, 4, element] = u[v, i, nnodes(dg), element]515end516end517end518519return nothing520end521522function calc_interface_flux!(surface_flux_values,523mesh::Union{StructuredMesh{2}, StructuredMeshView{2}},524have_nonconservative_terms, # can be True/False525equations, surface_integral, dg::DG, cache)526@unpack elements = cache527528@threaded for element in eachelement(dg, cache)529# Interfaces in negative directions530# Faster version of "for orientation in (1, 2)"531532# Interfaces in x-direction (`orientation` = 1)533calc_interface_flux!(elements.surface_flux_values,534elements.left_neighbors[1, element],535element, 1, mesh,536have_nonconservative_terms, equations,537surface_integral, dg, cache)538539# Interfaces in y-direction (`orientation` = 2)540calc_interface_flux!(elements.surface_flux_values,541elements.left_neighbors[2, element],542element, 2, mesh,543have_nonconservative_terms, equations,544surface_integral, dg, cache)545end546547return nothing548end549550@inline function calc_interface_flux!(surface_flux_values, left_element, right_element,551orientation,552mesh::Union{StructuredMesh{2},553StructuredMeshView{2}},554have_nonconservative_terms::False, equations,555surface_integral, dg::DG, cache)556# This is slow for LSA, but for some reason faster for Euler (see #519)557if left_element <= 0 # left_element = 0 at boundaries558return nothing559end560561@unpack surface_flux = surface_integral562@unpack interfaces_u, contravariant_vectors, inverse_jacobian = cache.elements563564right_direction = 2 * orientation565left_direction = right_direction - 1566567for i in eachnode(dg)568u_ll = get_node_vars(interfaces_u, equations, dg, i, right_direction,569left_element)570u_rr = get_node_vars(interfaces_u, equations, dg, i, left_direction,571right_element)572573if orientation == 1574# If the mapping is orientation-reversing, the contravariant vectors' orientation575# is reversed as well. The normal vector must be oriented in the direction576# from `left_element` to `right_element`, or the numerical flux will be computed577# incorrectly (downwind direction).578sign_jacobian = sign(inverse_jacobian[1, i, right_element])579580# First contravariant vector Ja^1 as SVector581normal_direction = sign_jacobian *582get_contravariant_vector(1, contravariant_vectors,5831, i, right_element)584else # orientation == 2585# See above586sign_jacobian = sign(inverse_jacobian[i, 1, right_element])587588# Second contravariant vector Ja^2 as SVector589normal_direction = sign_jacobian *590get_contravariant_vector(2, contravariant_vectors,591i, 1, right_element)592end593594# If the mapping is orientation-reversing, the normal vector will be reversed (see above).595# However, the flux now has the wrong sign, since we need the physical flux in normal direction.596flux = sign_jacobian * surface_flux(u_ll, u_rr, normal_direction, equations)597598for v in eachvariable(equations)599surface_flux_values[v, i, right_direction, left_element] = flux[v]600surface_flux_values[v, i, left_direction, right_element] = flux[v]601end602end603604return nothing605end606607@inline function calc_interface_flux!(surface_flux_values, left_element, right_element,608orientation,609mesh::Union{StructuredMesh{2},610StructuredMeshView{2}},611have_nonconservative_terms::True, equations,612surface_integral, dg::DG, cache)613# See comment on `calc_interface_flux!` with `have_nonconservative_terms::False`614if left_element <= 0 # left_element = 0 at boundaries615return nothing616end617618surface_flux, nonconservative_flux = surface_integral.surface_flux619@unpack interfaces_u, contravariant_vectors, inverse_jacobian = cache.elements620621right_direction = 2 * orientation622left_direction = right_direction - 1623624for i in eachnode(dg)625u_ll = get_node_vars(interfaces_u, equations, dg, i, right_direction,626left_element)627u_rr = get_node_vars(interfaces_u, equations, dg, i, left_direction,628right_element)629630if orientation == 1631# If the mapping is orientation-reversing, the contravariant vectors' orientation632# is reversed as well. The normal vector must be oriented in the direction633# from `left_element` to `right_element`, or the numerical flux will be computed634# incorrectly (downwind direction).635sign_jacobian = sign(inverse_jacobian[1, i, right_element])636637# First contravariant vector Ja^1 as SVector638normal_direction = sign_jacobian *639get_contravariant_vector(1, contravariant_vectors,6401, i, right_element)641else # orientation == 2642# See above643sign_jacobian = sign(inverse_jacobian[i, 1, right_element])644645# Second contravariant vector Ja^2 as SVector646normal_direction = sign_jacobian *647get_contravariant_vector(2, contravariant_vectors,648i, 1, right_element)649end650651# If the mapping is orientation-reversing, the normal vector will be reversed (see above).652# However, the flux now has the wrong sign, since we need the physical flux in normal direction.653flux = sign_jacobian * surface_flux(u_ll, u_rr, normal_direction, equations)654655# Compute both nonconservative fluxes656# Scale with sign_jacobian to ensure that the normal_direction matches that657# from the flux above658noncons_left = sign_jacobian *659nonconservative_flux(u_ll, u_rr, normal_direction, equations)660noncons_right = sign_jacobian *661nonconservative_flux(u_rr, u_ll, normal_direction, equations)662663for v in eachvariable(equations)664# Note the factor 0.5 necessary for the nonconservative fluxes based on665# the interpretation of global SBP operators coupled discontinuously via666# central fluxes/SATs667surface_flux_values[v, i, right_direction, left_element] = flux[v] +6680.5f0 *669noncons_left[v]670surface_flux_values[v, i, left_direction, right_element] = flux[v] +6710.5f0 *672noncons_right[v]673end674end675676return nothing677end678679function calc_boundary_flux!(cache, t, boundary_conditions::NamedTuple,680mesh::Union{StructuredMesh{2}, StructuredMeshView{2}},681equations, surface_integral,682dg::DG)683@unpack surface_flux_values = cache.elements684linear_indices = LinearIndices(size(mesh))685686for cell_y in axes(mesh, 2)687# Negative x-direction688direction = 1689element = linear_indices[begin, cell_y]690691for j in eachnode(dg)692calc_boundary_flux_by_direction!(surface_flux_values, t, 1,693boundary_conditions[direction],694mesh,695have_nonconservative_terms(equations),696equations, surface_integral, dg,697cache,698direction, (1, j), (j,), element)699end700701# Positive x-direction702direction = 2703element = linear_indices[end, cell_y]704705for j in eachnode(dg)706calc_boundary_flux_by_direction!(surface_flux_values, t, 1,707boundary_conditions[direction],708mesh,709have_nonconservative_terms(equations),710equations, surface_integral, dg,711cache,712direction, (nnodes(dg), j), (j,), element)713end714end715716for cell_x in axes(mesh, 1)717# Negative y-direction718direction = 3719element = linear_indices[cell_x, begin]720721for i in eachnode(dg)722calc_boundary_flux_by_direction!(surface_flux_values, t, 2,723boundary_conditions[direction],724mesh,725have_nonconservative_terms(equations),726equations, surface_integral, dg,727cache,728direction, (i, 1), (i,), element)729end730731# Positive y-direction732direction = 4733element = linear_indices[cell_x, end]734735for i in eachnode(dg)736calc_boundary_flux_by_direction!(surface_flux_values, t, 2,737boundary_conditions[direction],738mesh,739have_nonconservative_terms(equations),740equations, surface_integral, dg,741cache,742direction, (i, nnodes(dg)), (i,), element)743end744end745746return nothing747end748749function apply_jacobian!(backend::Nothing, du,750mesh::Union{StructuredMesh{2}, StructuredMeshView{2},751UnstructuredMesh2D, P4estMesh{2}, P4estMeshView{2},752T8codeMesh{2}},753equations, dg::DG, cache)754@unpack inverse_jacobian = cache.elements755@threaded for element in eachelement(dg, cache)756apply_jacobian_per_element!(du, typeof(mesh), equations, dg, inverse_jacobian,757element)758end759end760761function apply_jacobian!(backend::Backend, du,762mesh::Union{StructuredMesh{2}, StructuredMeshView{2},763UnstructuredMesh2D, P4estMesh{2}, P4estMeshView{2},764T8codeMesh{2}},765equations, dg::DG, cache)766nelements(dg, cache) == 0 && return nothing767@unpack inverse_jacobian = cache.elements768kernel! = apply_jacobian_KAkernel!(backend)769kernel!(du, typeof(mesh), equations, dg, inverse_jacobian,770ndrange = nelements(dg, cache))771end772773@kernel function apply_jacobian_KAkernel!(du,774MeshT::Type{<:Union{StructuredMesh{2},775StructuredMeshView{2},776UnstructuredMesh2D,777P4estMesh{2},778P4estMeshView{2},779T8codeMesh{2}}},780equations, dg::DG, inverse_jacobian)781element = @index(Global)782apply_jacobian_per_element!(du, MeshT, equations, dg, inverse_jacobian, element)783end784785@inline function apply_jacobian_per_element!(du,786::Type{<:Union{StructuredMesh{2},787StructuredMeshView{2},788UnstructuredMesh2D,789P4estMesh{2},790P4estMeshView{2},791T8codeMesh{2}}},792equations, dg::DG, inverse_jacobian,793element)794for j in eachnode(dg), i in eachnode(dg)795# Negative sign included to account for the negated surface and volume terms,796# see e.g. the computation of `derivative_hat` in the basis setup and797# the comment in `calc_surface_integral!`.798factor = -inverse_jacobian[i, j, element]799800for v in eachvariable(equations)801du[v, i, j, element] *= factor802end803end804return nothing805end806end # @muladd807808809