Path: blob/main/src/solvers/dgmulti/flux_differencing_gauss_sbp.jl
5591 views
1# ========= GaussSBP approximation types ============2# Note: we define type aliases outside of the @muladd block to avoid Revise breaking when code3# inside the @muladd block is edited. See https://github.com/trixi-framework/Trixi.jl/issues/8014# for more details.56# `GaussSBP` is a type alias for a StartUpDG type (e.g., Gauss nodes on quads/hexes)7const GaussSBP = Polynomial{Gauss}89function tensor_product_quadrature(element_type::Line, r1D, w1D)10return r1D, w1D11end1213function tensor_product_quadrature(element_type::Quad, r1D, w1D)14sq, rq = vec.(StartUpDG.NodesAndModes.meshgrid(r1D))15ws, wr = vec.(StartUpDG.NodesAndModes.meshgrid(w1D))16wq = wr .* ws17return rq, sq, wq18end1920function tensor_product_quadrature(element_type::Hex, r1D, w1D)21rq, sq, tq = vec.(StartUpDG.NodesAndModes.meshgrid(r1D, r1D, r1D))22wr, ws, wt = vec.(StartUpDG.NodesAndModes.meshgrid(w1D, w1D, w1D))23wq = wr .* ws .* wt24return rq, sq, tq, wq25end2627# type parameters for `TensorProductFaceOperator`.28abstract type AbstractGaussOperator end29struct Interpolation <: AbstractGaussOperator end30# - `Projection{ScaleByFaceWeights = False()}` corresponds to the operator `projection_matrix_gauss_to_face = M \ Vf'`,31# which is used in `VolumeIntegralFluxDifferencing`.32# - `Projection{ScaleByFaceWeights = True()}` corresponds to the quadrature-based lifting33# operator `LIFT = M \ (Vf' * diagm(rd.wf))`, which is used in `SurfaceIntegralWeakForm`34struct Projection{ScaleByFaceWeights} <: AbstractGaussOperator end3536# used to dispatch for different Gauss interpolation operators37abstract type AbstractTensorProductGaussOperator end3839# TensorProductGaussFaceOperator{Tmat, Ti}40#41# Data for performing tensor product interpolation from volume nodes to face nodes.42struct TensorProductGaussFaceOperator{NDIMS, OperatorType <: AbstractGaussOperator,43Tmat, Tweights, Tfweights, Tindices} <:44AbstractTensorProductGaussOperator45interp_matrix_gauss_to_face_1d::Tmat46inv_volume_weights_1d::Tweights47face_weights::Tfweights48face_indices_tensor_product::Tindices49nnodes_1d::Int50nfaces::Int51end5253function TensorProductGaussFaceOperator(operator::AbstractGaussOperator,54dg::DGMulti{1, Line, GaussSBP})55rd = dg.basis5657rq1D, wq1D = StartUpDG.gauss_quad(0, 0, polydeg(dg))58interp_matrix_gauss_to_face_1d = polynomial_interpolation_matrix(rq1D, [-1; 1])5960nnodes_1d = length(rq1D)61face_indices_tensor_product = nothing # not needed in 1D; we fall back to mul!6263num_faces = 26465T_op = typeof(operator)66Tm = typeof(interp_matrix_gauss_to_face_1d)67Tw = typeof(inv.(wq1D))68Tf = typeof(rd.wf)69Ti = typeof(face_indices_tensor_product)70return TensorProductGaussFaceOperator{1, T_op, Tm, Tw, Tf, Ti}(interp_matrix_gauss_to_face_1d,71inv.(wq1D), rd.wf,72face_indices_tensor_product,73nnodes_1d, num_faces)74end7576# constructor for a 2D operator77function TensorProductGaussFaceOperator(operator::AbstractGaussOperator,78dg::DGMulti{2, Quad, GaussSBP})79rd = dg.basis8081rq1D, wq1D = StartUpDG.gauss_quad(0, 0, polydeg(dg))82interp_matrix_gauss_to_face_1d = polynomial_interpolation_matrix(rq1D, [-1; 1])8384nnodes_1d = length(rq1D)8586# Permutation of indices in a tensor product form87num_faces = StartUpDG.num_faces(rd.element_type)88indices = reshape(1:length(rd.rf), nnodes_1d, num_faces)89face_indices_tensor_product = zeros(Int, 2, nnodes_1d, ndims(rd.element_type))90for i in 1:nnodes_1d # loop over nodes in one face91face_indices_tensor_product[:, i, 1] .= indices[i, 1:2]92face_indices_tensor_product[:, i, 2] .= indices[i, 3:4]93end9495T_op = typeof(operator)96Tm = typeof(interp_matrix_gauss_to_face_1d)97Tw = typeof(inv.(wq1D))98Tf = typeof(rd.wf)99Ti = typeof(face_indices_tensor_product)100return TensorProductGaussFaceOperator{2, T_op, Tm, Tw, Tf, Ti}(interp_matrix_gauss_to_face_1d,101inv.(wq1D), rd.wf,102face_indices_tensor_product,103nnodes_1d, num_faces)104end105106# constructor for a 3D operator107function TensorProductGaussFaceOperator(operator::AbstractGaussOperator,108dg::DGMulti{3, Hex, GaussSBP})109rd = dg.basis110111rq1D, wq1D = StartUpDG.gauss_quad(0, 0, polydeg(dg))112interp_matrix_gauss_to_face_1d = polynomial_interpolation_matrix(rq1D, [-1; 1])113114nnodes_1d = length(rq1D)115116# Permutation of indices in a tensor product form117num_faces = StartUpDG.num_faces(rd.element_type)118indices = reshape(1:length(rd.rf), nnodes_1d, nnodes_1d, num_faces)119face_indices_tensor_product = zeros(Int, 2, nnodes_1d, nnodes_1d,120ndims(rd.element_type))121for j in 1:nnodes_1d, i in 1:nnodes_1d # loop over nodes in one face122face_indices_tensor_product[:, i, j, 1] .= indices[i, j, 1:2]123face_indices_tensor_product[:, i, j, 2] .= indices[i, j, 3:4]124face_indices_tensor_product[:, i, j, 3] .= indices[i, j, 5:6]125end126127T_op = typeof(operator)128Tm = typeof(interp_matrix_gauss_to_face_1d)129Tw = typeof(inv.(wq1D))130Tf = typeof(rd.wf)131Ti = typeof(face_indices_tensor_product)132return TensorProductGaussFaceOperator{3, T_op, Tm, Tw, Tf, Ti}(interp_matrix_gauss_to_face_1d,133inv.(wq1D), rd.wf,134face_indices_tensor_product,135nnodes_1d, num_faces)136end137138# specialize behavior of `mul_by!(A)` where `A isa TensorProductGaussFaceOperator)`139@inline function mul_by!(A::AbstractTensorProductGaussOperator)140return (out, x) -> tensor_product_gauss_face_operator!(out, A, x)141end142143@inline function tensor_product_gauss_face_operator!(out::AbstractMatrix,144A::AbstractTensorProductGaussOperator,145x::AbstractMatrix)146@threaded for col in Base.OneTo(size(out, 2))147tensor_product_gauss_face_operator!(view(out, :, col), A, view(x, :, col))148end149end150151@inline function tensor_product_gauss_face_operator!(out::AbstractVector,152A::TensorProductGaussFaceOperator{1,153Interpolation},154x::AbstractVector)155return mul!(out, A.interp_matrix_gauss_to_face_1d, x)156end157158@inline function tensor_product_gauss_face_operator!(out::AbstractVector,159A::TensorProductGaussFaceOperator{1,160<:Projection},161x::AbstractVector)162mul!(out, A.interp_matrix_gauss_to_face_1d', x)163@. out *= A.inv_volume_weights_1d164end165166# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).167# Since these FMAs can increase the performance of many numerical algorithms,168# we need to opt-in explicitly.169# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.170@muladd begin171#! format: noindent172173#! format: off174# Interpolates values from volume Gauss nodes to face nodes on one element.175@inline function tensor_product_gauss_face_operator!(out::AbstractVector,176A::TensorProductGaussFaceOperator{2, Interpolation},177x_in::AbstractVector)178#! format: on179(; interp_matrix_gauss_to_face_1d, face_indices_tensor_product) = A180(; nnodes_1d) = A181182fill!(out, zero(eltype(out)))183184# for 2D GaussSBP nodes, the indexing is first in x, then in y185x = reshape(x_in, nnodes_1d, nnodes_1d)186187# interpolation in the x-direction188@turbo for i in Base.OneTo(nnodes_1d) # loop over nodes in a face189index_left = face_indices_tensor_product[1, i, 1]190index_right = face_indices_tensor_product[2, i, 1]191for jj in Base.OneTo(nnodes_1d) # loop over "line" of volume nodes192out[index_left] = out[index_left] +193interp_matrix_gauss_to_face_1d[1, jj] * x[jj, i]194out[index_right] = out[index_right] +195interp_matrix_gauss_to_face_1d[2, jj] * x[jj, i]196end197end198199# interpolation in the y-direction200@turbo for i in Base.OneTo(nnodes_1d) # loop over nodes in a face201index_left = face_indices_tensor_product[1, i, 2]202index_right = face_indices_tensor_product[2, i, 2]203for jj in Base.OneTo(nnodes_1d) # loop over "line" of volume nodes204out[index_left] = out[index_left] +205interp_matrix_gauss_to_face_1d[1, jj] * x[i, jj]206out[index_right] = out[index_right] +207interp_matrix_gauss_to_face_1d[2, jj] * x[i, jj]208end209end210211return nothing212end213214# Interpolates values from volume Gauss nodes to face nodes on one element.215#! format: off216@inline function tensor_product_gauss_face_operator!(out::AbstractVector,217A::TensorProductGaussFaceOperator{3, Interpolation},218x::AbstractVector)219#! format: on220(; interp_matrix_gauss_to_face_1d, face_indices_tensor_product) = A221(; nnodes_1d) = A222223fill!(out, zero(eltype(out)))224225# for 3D GaussSBP nodes, the indexing is first in y, then x, then z.226x = reshape(x, nnodes_1d, nnodes_1d, nnodes_1d)227228# interpolation in the y-direction229@turbo for j in Base.OneTo(nnodes_1d), i in Base.OneTo(nnodes_1d) # loop over nodes in a face230index_left = face_indices_tensor_product[1, i, j, 2]231index_right = face_indices_tensor_product[2, i, j, 2]232for jj in Base.OneTo(nnodes_1d) # loop over "line" of volume nodes233out[index_left] = out[index_left] +234interp_matrix_gauss_to_face_1d[1, jj] * x[jj, i, j]235out[index_right] = out[index_right] +236interp_matrix_gauss_to_face_1d[2, jj] * x[jj, i, j]237end238end239240# interpolation in the x-direction241@turbo for j in Base.OneTo(nnodes_1d), i in Base.OneTo(nnodes_1d) # loop over nodes in a face242index_left = face_indices_tensor_product[1, i, j, 1]243index_right = face_indices_tensor_product[2, i, j, 1]244for jj in Base.OneTo(nnodes_1d) # loop over "line" of volume nodes245out[index_left] = out[index_left] +246interp_matrix_gauss_to_face_1d[1, jj] * x[i, jj, j]247out[index_right] = out[index_right] +248interp_matrix_gauss_to_face_1d[2, jj] * x[i, jj, j]249end250end251252# interpolation in the z-direction253@turbo for i in Base.OneTo(nnodes_1d), j in Base.OneTo(nnodes_1d) # loop over nodes in a face254index_left = face_indices_tensor_product[1, i, j, 3]255index_right = face_indices_tensor_product[2, i, j, 3]256for jj in Base.OneTo(nnodes_1d) # loop over "line" of volume nodes257# The ordering (i,j) -> (j,i) needs to be reversed for this last face.258# This is due to way we define face nodes for Hex() types in StartUpDG.jl.259out[index_left] = out[index_left] +260interp_matrix_gauss_to_face_1d[1, jj] * x[j, i, jj]261out[index_right] = out[index_right] +262interp_matrix_gauss_to_face_1d[2, jj] * x[j, i, jj]263end264end265266return nothing267end268269# Projects face node values to volume Gauss nodes on one element.270#! format: off271@inline function tensor_product_gauss_face_operator!(out_vec::AbstractVector,272A::TensorProductGaussFaceOperator{2, Projection{ApplyFaceWeights}},273x::AbstractVector) where {ApplyFaceWeights}274#! format: on275(; interp_matrix_gauss_to_face_1d, face_indices_tensor_product) = A276(; inv_volume_weights_1d, nnodes_1d) = A277278fill!(out_vec, zero(eltype(out_vec)))279280# As of Julia 1.9, Base.ReshapedArray does not produce allocations when setting values.281# Thus, Base.ReshapedArray should be used if you are setting values in the array.282# `reshape` is fine if you are only accessing values.283# Note that, for 2D GaussSBP nodes, the indexing is first in x, then y284out = Base.ReshapedArray(out_vec, (nnodes_1d, nnodes_1d), ())285286if ApplyFaceWeights == true287@turbo for i in eachindex(x)288x[i] = x[i] * A.face_weights[i]289end290end291292# interpolation in the x-direction293@turbo for i in Base.OneTo(nnodes_1d) # loop over face nodes294index_left = face_indices_tensor_product[1, i, 1]295index_right = face_indices_tensor_product[2, i, 1]296for jj in Base.OneTo(nnodes_1d) # loop over a line of volume nodes297out[jj, i] = out[jj, i] +298interp_matrix_gauss_to_face_1d[1, jj] * x[index_left]299out[jj, i] = out[jj, i] +300interp_matrix_gauss_to_face_1d[2, jj] * x[index_right]301end302end303304# interpolation in the y-direction305@turbo for i in Base.OneTo(nnodes_1d)306index_left = face_indices_tensor_product[1, i, 2]307index_right = face_indices_tensor_product[2, i, 2]308# loop over a line of volume nodes309for jj in Base.OneTo(nnodes_1d)310out[i, jj] = out[i, jj] +311interp_matrix_gauss_to_face_1d[1, jj] * x[index_left]312out[i, jj] = out[i, jj] +313interp_matrix_gauss_to_face_1d[2, jj] * x[index_right]314end315end316317# apply inv(M)318@turbo for j in Base.OneTo(nnodes_1d), i in Base.OneTo(nnodes_1d)319out[i, j] = out[i, j] * inv_volume_weights_1d[i] * inv_volume_weights_1d[j]320end321322return nothing323end324325# Interpolates values from volume Gauss nodes to face nodes on one element.326#! format: off327@inline function tensor_product_gauss_face_operator!(out_vec::AbstractVector,328A::TensorProductGaussFaceOperator{3, Projection{ApplyFaceWeights}},329x::AbstractVector) where {ApplyFaceWeights}330#! format: on331@unpack interp_matrix_gauss_to_face_1d, face_indices_tensor_product = A332@unpack inv_volume_weights_1d, nnodes_1d, nfaces = A333334fill!(out_vec, zero(eltype(out_vec)))335336# As of Julia 1.9, Base.ReshapedArray does not produce allocations when setting values.337# Thus, Base.ReshapedArray should be used if you are setting values in the array.338# `reshape` is fine if you are only accessing values.339# Note that, for 3D GaussSBP nodes, the indexing is first in y, then x, then z.340out = Base.ReshapedArray(out_vec, (nnodes_1d, nnodes_1d, nnodes_1d), ())341342if ApplyFaceWeights == true343@turbo for i in eachindex(x)344x[i] = x[i] * A.face_weights[i]345end346end347348# interpolation in the y-direction349@turbo for j in Base.OneTo(nnodes_1d), i in Base.OneTo(nnodes_1d) # loop over nodes in a face350index_left = face_indices_tensor_product[1, i, j, 2]351index_right = face_indices_tensor_product[2, i, j, 2]352for jj in Base.OneTo(nnodes_1d) # loop over "line" of volume nodes353out[jj, i, j] = out[jj, i, j] +354interp_matrix_gauss_to_face_1d[1, jj] * x[index_left]355out[jj, i, j] = out[jj, i, j] +356interp_matrix_gauss_to_face_1d[2, jj] * x[index_right]357end358end359360# interpolation in the x-direction361@turbo for j in Base.OneTo(nnodes_1d), i in Base.OneTo(nnodes_1d) # loop over nodes in a face362index_left = face_indices_tensor_product[1, i, j, 1]363index_right = face_indices_tensor_product[2, i, j, 1]364for jj in Base.OneTo(nnodes_1d) # loop over "line" of volume nodes365out[i, jj, j] = out[i, jj, j] +366interp_matrix_gauss_to_face_1d[1, jj] * x[index_left]367out[i, jj, j] = out[i, jj, j] +368interp_matrix_gauss_to_face_1d[2, jj] * x[index_right]369end370end371372# interpolation in the z-direction373@turbo for i in Base.OneTo(nnodes_1d), j in Base.OneTo(nnodes_1d) # loop over nodes in a face374index_left = face_indices_tensor_product[1, i, j, 3]375index_right = face_indices_tensor_product[2, i, j, 3]376for jj in Base.OneTo(nnodes_1d) # loop over "line" of volume nodes377# The ordering (i,j) -> (j,i) needs to be reversed for this last face.378# This is due to way we define face nodes for Hex() types in StartUpDG.jl.379out[j, i, jj] = out[j, i, jj] +380interp_matrix_gauss_to_face_1d[1, jj] * x[index_left]381out[j, i, jj] = out[j, i, jj] +382interp_matrix_gauss_to_face_1d[2, jj] * x[index_right]383end384end385386# apply inv(M)387@turbo for k in Base.OneTo(nnodes_1d), j in Base.OneTo(nnodes_1d),388i in Base.OneTo(nnodes_1d)389390out[i, j, k] = out[i, j, k] * inv_volume_weights_1d[i] *391inv_volume_weights_1d[j] * inv_volume_weights_1d[k]392end393394return nothing395end396397# For now, this is mostly the same as `create_cache` for DGMultiFluxDiff{<:Polynomial}.398# In the future, we may modify it so that we can specialize additional parts of GaussSBP() solvers.399function create_cache(mesh::DGMultiMesh, equations,400dg::DGMultiFluxDiff{<:GaussSBP, <:Union{Line, Quad, Hex}}, RealT,401uEltype)402403# call general Polynomial flux differencing constructor404cache = invoke(create_cache,405Tuple{typeof(mesh), typeof(equations),406DGMultiFluxDiff, typeof(RealT), typeof(uEltype)},407mesh, equations, dg, RealT, uEltype)408409rd = dg.basis410@unpack md = mesh411412# for change of basis prior to the volume integral and entropy projection413r1D, _ = StartUpDG.gauss_lobatto_quad(0, 0, polydeg(dg))414rq1D, _ = StartUpDG.gauss_quad(0, 0, polydeg(dg))415interp_matrix_lobatto_to_gauss_1D = polynomial_interpolation_matrix(r1D, rq1D)416interp_matrix_gauss_to_lobatto_1D = polynomial_interpolation_matrix(rq1D, r1D)417NDIMS = ndims(rd.element_type)418interp_matrix_lobatto_to_gauss = SimpleKronecker(NDIMS,419interp_matrix_lobatto_to_gauss_1D,420uEltype)421interp_matrix_gauss_to_lobatto = SimpleKronecker(NDIMS,422interp_matrix_gauss_to_lobatto_1D,423uEltype)424inv_gauss_weights = inv.(rd.wq)425426# specialized operators to perform tensor product interpolation to faces for Gauss nodes427interp_matrix_gauss_to_face = TensorProductGaussFaceOperator(Interpolation(), dg)428projection_matrix_gauss_to_face = TensorProductGaussFaceOperator(Projection{False()}(),429dg)430431# `LIFT` matrix for Gauss nodes - this is equivalent to `projection_matrix_gauss_to_face` scaled by `diagm(rd.wf)`,432# where `rd.wf` are Gauss node face quadrature weights.433gauss_LIFT = TensorProductGaussFaceOperator(Projection{True()}(), dg)434435nvars = nvariables(equations)436rhs_volume_local_threaded = [allocate_nested_array(uEltype, nvars, (rd.Nq,), dg)437for _ in 1:Threads.maxthreadid()]438gauss_volume_local_threaded = [allocate_nested_array(uEltype, nvars, (rd.Nq,), dg)439for _ in 1:Threads.maxthreadid()]440441return (; cache..., projection_matrix_gauss_to_face, gauss_LIFT, inv_gauss_weights,442rhs_volume_local_threaded, gauss_volume_local_threaded,443interp_matrix_lobatto_to_gauss, interp_matrix_gauss_to_lobatto,444interp_matrix_gauss_to_face,445create_cache(mesh, equations, dg.volume_integral, dg, RealT, uEltype)...) # add cache specialized on the volume integral446end447448# by default, return an empty tuple for volume integral caches449create_cache(mesh, equations, volume_integral, dg, RealT, uEltype) = NamedTuple()450451# TODO: DGMulti. Address hard-coding of `entropy2cons!` and `cons2entropy!` for this function.452function entropy_projection!(cache, u, mesh::DGMultiMesh, equations,453dg::DGMultiFluxDiff{<:GaussSBP})454rd = dg.basis455@unpack Vq = rd456@unpack VhP, entropy_var_values = cache457@unpack projected_entropy_var_values, entropy_projected_u_values = cache458@unpack interp_matrix_lobatto_to_gauss, interp_matrix_gauss_to_face = cache459(; u_values) = cache.solution_container460461@threaded for e in eachelement(mesh, dg, cache)462apply_to_each_field(mul_by!(interp_matrix_lobatto_to_gauss),463view(u_values, :, e), view(u, :, e))464end465466# transform quadrature values to entropy variables467cons2entropy!(entropy_var_values, u_values, equations)468469volume_indices = Base.OneTo(rd.Nq)470face_indices = (rd.Nq + 1):(rd.Nq + rd.Nfq)471472# Interpolate volume Gauss nodes to Gauss face nodes (note the layout of473# `projected_entropy_var_values = [vol pts; face pts]`).474entropy_var_face_values = view(projected_entropy_var_values, face_indices, :)475apply_to_each_field(mul_by!(interp_matrix_gauss_to_face), entropy_var_face_values,476entropy_var_values)477478# directly copy over volume values (no entropy projection required)479entropy_projected_volume_values = view(entropy_projected_u_values, volume_indices,480:)481@threaded for i in eachindex(u_values)482entropy_projected_volume_values[i] = u_values[i]483end484485# transform entropy to conservative variables on face values486entropy_projected_face_values = view(entropy_projected_u_values, face_indices, :)487entropy2cons!(entropy_projected_face_values, entropy_var_face_values, equations)488489return nothing490end491492# Assumes cache.solution_container.flux_face_values is already computed.493# Enables tensor product evaluation of `LIFT isa TensorProductGaussFaceOperator`.494function calc_surface_integral!(du, u, mesh::DGMultiMesh, equations,495surface_integral::SurfaceIntegralWeakForm,496dg::DGMultiFluxDiff{<:GaussSBP}, cache)497(; gauss_LIFT, gauss_volume_local_threaded) = cache498499@threaded for e in eachelement(mesh, dg, cache)500501# applies LIFT matrix, output is stored at Gauss nodes502gauss_volume_local = gauss_volume_local_threaded[Threads.threadid()]503apply_to_each_field(mul_by!(gauss_LIFT), gauss_volume_local,504view(cache.solution_container.flux_face_values, :, e))505506for i in eachindex(gauss_volume_local)507du[i, e] = du[i, e] + gauss_volume_local[i]508end509end510511return nothing512end513514function project_rhs_to_gauss_nodes!(du, rhs_local, element, mesh::DGMultiMesh,515dg::DGMulti, cache, alpha = true)516517# Here, we exploit that under a Gauss nodal basis the structure of the projection518# matrix `Ph = [diagm(1 ./ wq), projection_matrix_gauss_to_face]` such that519# `Ph * [u; uf] = (u ./ wq) + projection_matrix_gauss_to_face * uf`.520volume_indices = Base.OneTo(dg.basis.Nq)521face_indices = (dg.basis.Nq + 1):(dg.basis.Nq + dg.basis.Nfq)522local_volume_flux = view(rhs_local, volume_indices)523local_face_flux = view(rhs_local, face_indices)524525# initialize rhs_volume_local = projection_matrix_gauss_to_face * local_face_flux526rhs_volume_local = cache.rhs_volume_local_threaded[Threads.threadid()]527apply_to_each_field(mul_by!(cache.projection_matrix_gauss_to_face),528rhs_volume_local, local_face_flux)529530# accumulate volume contributions at Gauss nodes531for i in eachindex(rhs_volume_local)532du_local = rhs_volume_local[i] +533local_volume_flux[i] * cache.inv_gauss_weights[i]534du[i, element] = du[i, element] + alpha * du_local535end536537return nothing538end539540function volume_integral_kernel!(du, u, element, mesh::DGMultiMesh,541have_nonconservative_terms, equations,542volume_integral::VolumeIntegralFluxDifferencing,543dg::DGMultiFluxDiff{<:GaussSBP}, cache, alpha = true)544(; volume_flux) = volume_integral545546du_local = cache.du_local_threaded[Threads.threadid()]547fill!(du_local, zero(eltype(du_local)))548u_local = view(cache.entropy_projected_u_values, :, element)549550local_flux_differencing!(du_local, u_local, element,551have_nonconservative_terms,552volume_flux, has_sparse_operators(dg),553mesh, equations, dg, cache)554555# convert `du_local::Vector{<:SVector}` to `rhs_local::StructArray{<:SVector}`556# for faster performance when using `apply_to_each_field`.557rhs_local = cache.rhs_local_threaded[Threads.threadid()]558for i in Base.OneTo(length(du_local))559rhs_local[i] = du_local[i]560end561562return project_rhs_to_gauss_nodes!(du, rhs_local, element, mesh, dg, cache, alpha)563end564565# interpolate back to Lobatto nodes after applying the inverse Jacobian at Gauss points566function invert_jacobian_and_interpolate!(du, mesh::DGMultiMesh, equations,567dg::DGMultiFluxDiff{<:GaussSBP}, cache;568scaling = -1)569(; interp_matrix_gauss_to_lobatto, rhs_volume_local_threaded, invJ) = cache570571@threaded for e in eachelement(mesh, dg, cache)572rhs_volume_local = rhs_volume_local_threaded[Threads.threadid()]573574# At this point, `rhs_volume_local` should still be stored at Gauss points.575# We scale it by the inverse Jacobian before transforming back to Lobatto.576for i in eachindex(rhs_volume_local)577rhs_volume_local[i] = du[i, e] * invJ[i, e] * scaling578end579580# Interpolate result back to Lobatto nodes for ease of analysis, visualization581apply_to_each_field(mul_by!(interp_matrix_gauss_to_lobatto),582view(du, :, e), rhs_volume_local)583end584585return nothing586end587588# Specialize RHS so that we can call `invert_jacobian_and_interpolate!` instead of just `invert_jacobian!`,589# since `invert_jacobian!` is also used in other places (e.g., parabolic terms).590function rhs!(du, u, t, mesh, equations, boundary_conditions::BC,591source_terms::Source, dg::DGMultiFluxDiff{<:GaussSBP},592cache) where {Source, BC}593@trixi_timeit timer() "reset ∂u/∂t" set_zero!(du, dg, cache)594595# this function evaluates the solution at volume and face quadrature points (which was previously596# done in `prolong2interfaces` and `calc_volume_integral`)597@trixi_timeit timer() "entropy_projection!" begin598entropy_projection!(cache, u, mesh, equations, dg)599end600601# `du` is stored at Gauss nodes here602@trixi_timeit timer() "volume integral" begin603calc_volume_integral!(du, u, mesh,604have_nonconservative_terms(equations), equations,605dg.volume_integral, dg, cache)606end607608# the following functions are the same as in VolumeIntegralWeakForm, and can be reused from dg.jl609@trixi_timeit timer() "interface flux" begin610calc_interface_flux!(cache, dg.surface_integral, mesh,611have_nonconservative_terms(equations), equations, dg)612end613614@trixi_timeit timer() "boundary flux" begin615calc_boundary_flux!(cache, t, boundary_conditions, mesh,616have_nonconservative_terms(equations), equations, dg)617end618619# `du` is stored at Gauss nodes here620@trixi_timeit timer() "surface integral" begin621calc_surface_integral!(du, u, mesh, equations,622dg.surface_integral, dg, cache)623end624625# invert Jacobian and map `du` from Gauss to Lobatto nodes626@trixi_timeit timer() "Jacobian" begin627invert_jacobian_and_interpolate!(du, mesh, equations, dg, cache)628end629630@trixi_timeit timer() "source terms" begin631calc_sources!(du, u, t, source_terms, mesh, equations, dg, cache)632end633634return nothing635end636end # @muladd637638639