Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/solvers/dgmulti/flux_differencing.jl
5590 views
1
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
2
# Since these FMAs can increase the performance of many numerical algorithms,
3
# we need to opt-in explicitly.
4
# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
5
@muladd begin
6
#! format: noindent
7
8
# Return the contravariant basis vector corresponding to the Cartesian
9
# coordinate direction `orientation` in a given `element` of the `mesh`.
10
# The contravariant basis vectors have entries `dx_i / dxhat_j` where
11
# j ∈ {1, ..., NDIMS}. Here, `x_i` and `xhat_j` are the ith physical coordinate
12
# and jth reference coordinate, respectively. These are geometric terms which
13
# appear when using the chain rule to compute physical derivatives as a linear
14
# combination of reference derivatives.
15
@inline function get_contravariant_vector(element, orientation,
16
mesh::DGMultiMesh{NDIMS}, cache) where {NDIMS}
17
# note that rstxyzJ = [rxJ, sxJ, txJ; ryJ syJ tyJ; rzJ szJ tzJ], so that this will return
18
# SVector{2}(rxJ[1, element], ryJ[1, element]) in 2D.
19
20
# assumes geometric terms are constant on each element
21
dxidxhatj = mesh.md.rstxyzJ
22
return SVector{NDIMS}(getindex.(dxidxhatj[:, orientation], 1, element))
23
end
24
25
@inline function get_contravariant_vector(element, orientation,
26
mesh::DGMultiMesh{NDIMS, NonAffine},
27
cache) where {NDIMS}
28
# note that rstxyzJ = [rxJ, sxJ, txJ; ryJ syJ tyJ; rzJ szJ tzJ]
29
30
# assumes geometric terms vary spatially over each element
31
(; dxidxhatj) = cache
32
return SVector{NDIMS}(view.(dxidxhatj[:, orientation], :, element))
33
end
34
35
# For Affine meshes, `get_contravariant_vector` returns an SVector of scalars (constant over the
36
# element). The normal direction is the same for all node pairs.
37
@inline get_normal_direction(normal_directions::AbstractVector, i, j) = normal_directions
38
39
# For NonAffine meshes, `get_contravariant_vector` returns an SVector of per-node arrays.
40
# We average the normals at nodes i and j for provably entropy-stable de-aliasing of geometric terms.
41
@inline function get_normal_direction(normal_directions::AbstractVector{<:AbstractVector},
42
i, j)
43
return 0.5f0 * (getindex.(normal_directions, i) + getindex.(normal_directions, j))
44
end
45
46
# use hybridized SBP operators for general flux differencing schemes.
47
function compute_flux_differencing_SBP_matrices(dg::DGMulti)
48
return compute_flux_differencing_SBP_matrices(dg, has_sparse_operators(dg))
49
end
50
51
function compute_flux_differencing_SBP_matrices(dg::DGMulti, sparse_operators)
52
rd = dg.basis
53
Qrst_hybridized, VhP, Ph = StartUpDG.hybridized_SBP_operators(rd)
54
Qrst_skew = map(A -> 0.5 * (A - A'), Qrst_hybridized)
55
if sparse_operators == true
56
Qrst_skew = map(Qi -> droptol!(sparse(Qi'), 100 * eps(eltype(Qi)))', Qrst_skew)
57
end
58
return Qrst_skew, VhP, Ph
59
end
60
61
# use traditional multidimensional SBP operators for SBP approximation types.
62
function compute_flux_differencing_SBP_matrices(dg::DGMultiFluxDiffSBP,
63
sparse_operators)
64
rd = dg.basis
65
@unpack M, Drst, Pq = rd
66
Qrst = map(D -> M * D, Drst)
67
Qrst_skew = map(A -> 0.5 * (A - A'), Qrst)
68
if sparse_operators == true
69
Qrst_skew = map(Qi -> droptol!(sparse(Qi'), 100 * eps(eltype(Qi)))', Qrst_skew)
70
end
71
return Qrst_skew
72
end
73
74
# Build element-to-element connectivity from face-to-face connectivity.
75
# Used for smoothing of shock capturing blending parameters (see `apply_smoothing!`).
76
#
77
# Here, `mesh.md.FToF` is a `(num_faces_per_element × num_elements)` array where
78
# `FToF[f, e]` stores the global face index of the neighbor of local face `f` on
79
# element `e`.
80
#
81
# Global face indices are laid out as
82
#
83
# global_face_index = (element_index - 1) * num_faces + local_face_index,
84
#
85
# so that the element index can be recovered by integer division:
86
#
87
# element_index = (global_face_index - 1) ÷ num_faces + 1.
88
#
89
# For a non-periodic boundary face, `FToF[f, e]` points back to face `f` of element
90
# `e` itself, so boundary elements are listed as their own neighbor.
91
function build_element_to_element_connectivity(mesh::DGMultiMesh, dg::DGMulti)
92
face_to_face_connectivity = mesh.md.FToF
93
element_to_element_connectivity = similar(face_to_face_connectivity)
94
for e in axes(face_to_face_connectivity, 2)
95
for f in axes(face_to_face_connectivity, 1)
96
neighbor_face_index = face_to_face_connectivity[f, e]
97
98
# Reverse-engineer element index from face index. Assumes all elements
99
# have the same number of faces.
100
neighbor_element_index = ((neighbor_face_index - 1) ÷ dg.basis.num_faces) +
101
1
102
element_to_element_connectivity[f, e] = neighbor_element_index
103
end
104
end
105
return element_to_element_connectivity
106
end
107
108
# For flux differencing SBP-type approximations, store solutions in Matrix{SVector{nvars}}.
109
# This results in a slight speedup for `calc_volume_integral!`.
110
function allocate_nested_array(uEltype, nvars, array_dimensions, dg::DGMultiFluxDiffSBP)
111
return zeros(SVector{nvars, uEltype}, array_dimensions...)
112
end
113
114
function create_cache(mesh::DGMultiMesh, equations, dg::DGMultiFluxDiffSBP,
115
RealT, uEltype)
116
rd = dg.basis
117
md = mesh.md
118
119
# for use with flux differencing schemes
120
Qrst_skew = compute_flux_differencing_SBP_matrices(dg)
121
122
lift_scalings = rd.wf ./ rd.wq[rd.Fmask] # lift scalings for diag-norm SBP operators
123
124
nvars = nvariables(equations)
125
# Use an array of SVectors (chunks of `nvars` are contiguous in memory) to speed up flux differencing
126
du_local_threaded = [zeros(SVector{nvars, uEltype}, rd.Nq)
127
for _ in 1:Threads.maxthreadid()]
128
129
solution_container = initialize_dgmulti_solution_container(mesh, equations, dg,
130
uEltype)
131
132
return (; md, Qrst_skew, dxidxhatj = md.rstxyzJ,
133
invJ = inv.(md.J), lift_scalings, inv_wq = inv.(rd.wq),
134
solution_container, du_local_threaded)
135
end
136
137
# most general create_cache: works for `DGMultiFluxDiff{<:Polynomial}`
138
function create_cache(mesh::DGMultiMesh, equations, dg::DGMultiFluxDiff, RealT, uEltype)
139
rd = dg.basis
140
@unpack md = mesh
141
142
Qrst_skew, VhP, Ph = compute_flux_differencing_SBP_matrices(dg)
143
144
# temp storage for entropy variables at volume quad points
145
nvars = nvariables(equations)
146
entropy_var_values = allocate_nested_array(uEltype, nvars, (rd.Nq, md.num_elements),
147
dg)
148
149
# storage for all quadrature points (concatenated volume / face quadrature points)
150
num_quad_points_total = rd.Nq + rd.Nfq
151
entropy_projected_u_values = allocate_nested_array(uEltype, nvars,
152
(num_quad_points_total,
153
md.num_elements), dg)
154
projected_entropy_var_values = allocate_nested_array(uEltype, nvars,
155
(num_quad_points_total,
156
md.num_elements), dg)
157
158
# For this specific solver, `prolong2interfaces` will not be used anymore.
159
# Instead, this step is also performed in `entropy_projection!`. Thus, we set
160
# `u_face_values` as a `view` into `entropy_projected_u_values`. We do not do
161
# the same for `u_values` since we will use that with LoopVectorization, which
162
# cannot handle such views as of v0.12.66, the latest version at the time of writing.
163
u_values = allocate_nested_array(uEltype, nvars, size(md.xq), dg)
164
u_face_values = view(entropy_projected_u_values, (rd.Nq + 1):num_quad_points_total,
165
:)
166
flux_face_values = similar(u_face_values)
167
168
# local storage for interface fluxes, rhs, and source
169
local_values_threaded = [allocate_nested_array(uEltype, nvars, (rd.Nq,), dg)
170
for _ in 1:Threads.maxthreadid()]
171
172
# Use an array of SVectors (chunks of `nvars` are contiguous in memory) to speed up flux differencing
173
# The result is then transferred to `rhs_local`, a thread-local element of
174
# `rhs_local_threaded::StructArray{<:SVector}` before projecting it and storing it into `du`.
175
du_local_threaded = [zeros(SVector{nvars, uEltype}, num_quad_points_total)
176
for _ in 1:Threads.maxthreadid()]
177
rhs_local_threaded = [allocate_nested_array(uEltype, nvars,
178
(num_quad_points_total,), dg)
179
for _ in 1:Threads.maxthreadid()]
180
181
# interpolate geometric terms to both quadrature and face values for curved meshes
182
(; Vq, Vf) = dg.basis
183
interpolated_geometric_terms = map(x -> [Vq; Vf] * x, mesh.md.rstxyzJ)
184
J = Vq * md.J
185
186
solution_container = DGMultiSolutionContainer(u_values, u_face_values,
187
flux_face_values,
188
local_values_threaded)
189
190
return (; md, Qrst_skew, VhP, Ph,
191
invJ = inv.(J), dxidxhatj = interpolated_geometric_terms,
192
entropy_var_values, projected_entropy_var_values,
193
entropy_projected_u_values,
194
solution_container, du_local_threaded, rhs_local_threaded)
195
end
196
197
# TODO: DGMulti. Address hard-coding of `entropy2cons!` and `cons2entropy!` for this function.
198
function entropy_projection!(cache, u, mesh::DGMultiMesh, equations, dg::DGMulti)
199
rd = dg.basis
200
@unpack Vq = rd
201
@unpack VhP, entropy_var_values = cache
202
@unpack projected_entropy_var_values, entropy_projected_u_values = cache
203
(; u_values) = cache.solution_container
204
205
apply_to_each_field(mul_by!(Vq), u_values, u)
206
207
cons2entropy!(entropy_var_values, u_values, equations)
208
209
# "VhP" fuses the projection "P" with interpolation to volume and face quadrature "Vh"
210
apply_to_each_field(mul_by!(VhP), projected_entropy_var_values, entropy_var_values)
211
212
entropy2cons!(entropy_projected_u_values, projected_entropy_var_values, equations)
213
return nothing
214
end
215
216
@inline function cons2entropy!(entropy_var_values::StructArray,
217
u_values::StructArray,
218
equations)
219
@threaded for i in eachindex(u_values)
220
entropy_var_values[i] = cons2entropy(u_values[i], equations)
221
end
222
end
223
224
@inline function entropy2cons!(entropy_projected_u_values::StructArray,
225
projected_entropy_var_values::StructArray,
226
equations)
227
@threaded for i in eachindex(projected_entropy_var_values)
228
entropy_projected_u_values[i] = entropy2cons(projected_entropy_var_values[i],
229
equations)
230
end
231
end
232
233
# Trait-like system to dispatch based on whether or not the SBP operators are sparse.
234
# Designed to be extendable to include specialized `approximation_types` too.
235
@inline function has_sparse_operators(dg::DGMultiFluxDiff)
236
rd = dg.basis
237
return has_sparse_operators(rd.element_type, rd.approximation_type)
238
end
239
240
# General fallback for DGMulti solvers:
241
# Polynomial-based solvers use hybridized SBP operators, which have blocks scaled by outward
242
# normal components. This implies that operators for different coordinate directions have
243
# different sparsity patterns. We default to using sum factorization (which is faster when
244
# operators are sparse) for all `DGMulti` / `StartUpDG.jl` approximation types.
245
@inline has_sparse_operators(element_type, approx_type) = True()
246
247
# For traditional SBP operators on triangles, the operators are fully dense. We avoid using
248
# sum factorization here, which is slower for fully dense matrices.
249
@inline function has_sparse_operators(::Union{Line, Tri, Tet},
250
approx_type::AT) where {AT <: SBP}
251
return False()
252
end
253
254
# SBP/GaussSBP operators on quads/hexes use tensor-product operators. Thus, sum factorization is
255
# more efficient and we use the sparsity structure.
256
@inline function has_sparse_operators(::Union{Quad, Hex},
257
approx_type::AT) where {AT <: SBP}
258
return True()
259
end
260
@inline has_sparse_operators(::Union{Quad, Hex}, approx_type::GaussSBP) = True()
261
262
# FD SBP methods have sparse operators
263
@inline function has_sparse_operators(::Union{Line, Quad, Hex},
264
approx_type::AbstractDerivativeOperator)
265
return True()
266
end
267
268
function calc_volume_integral!(du, u, mesh::DGMultiMesh,
269
have_nonconservative_terms, equations,
270
volume_integral, dg::DGMultiFluxDiff, cache,
271
alpha = true)
272
# No interpolation performed for general volume integral.
273
# Instead, an element-wise entropy projection (`entropy_projection!`) is performed before, see
274
# `rhs!` for `DGMultiFluxDiff`, which populates `entropy_projected_u_values`
275
@threaded for element in eachelement(mesh, dg, cache)
276
volume_integral_kernel!(du, u, element, mesh,
277
have_nonconservative_terms, equations,
278
volume_integral, dg, cache, alpha)
279
end
280
281
return nothing
282
end
283
284
# Computes flux differencing contribution over a single element by looping over node pairs (i, j).
285
# The physical normal direction for each pair is n_ij = geometric_matrix * ref_entries,
286
# where ref_entries[d] = Qrst_skew[d][i,j].
287
# This fuses the NDIMS per-dimension flux
288
# evaluations of the old dimension-by-dimension loop into a single evaluation per pair.
289
# Essentially, instead of calculating
290
# volume_flux(u_i, u_j, 1, equations) * Qx[i, j] + volume_flux(u_i, u_j, 2, equations) * Qy[i, j] + ...
291
# where Qx[i, j] = dr/dx * Qr[i, j] + ds/dx * Qs[i, j], we can expand out and evaluate
292
# volume_flux(u_i, u_j, [dr/dx, dr/dy] * Qr[i, j], equations) +
293
# volume_flux(u_i, u_j, [ds/dx, ds/dy] * Qs[i, j], equations)
294
# which is slightly faster.
295
#
296
# For dense operators (SBP on Line/Tri/Tet), we do not use this sum factorization trick.
297
@inline function local_flux_differencing!(du_local, u_local, element_index,
298
have_nonconservative_terms::False,
299
volume_flux,
300
has_sparse_operators::False, mesh,
301
equations, dg, cache)
302
@unpack Qrst_skew = cache
303
NDIMS = ndims(mesh)
304
row_ids = axes(first(Qrst_skew), 1)
305
geometric_matrix = get_contravariant_matrix(element_index, mesh, cache)
306
for i in row_ids
307
u_i = u_local[i]
308
for j in row_ids
309
# We use the symmetry of the volume flux and the anti-symmetry
310
# of the derivative operator to save half of the volume flux
311
# computations.
312
if j > i
313
u_j = u_local[j]
314
ref_entries = SVector(ntuple(d -> Qrst_skew[d][i, j], Val(NDIMS)))
315
normal_direction = geometric_matrix * ref_entries
316
AF_ij = 2 * volume_flux(u_i, u_j, normal_direction, equations)
317
du_local[i] = du_local[i] + AF_ij
318
du_local[j] = du_local[j] - AF_ij # Due to skew-symmetry
319
end
320
end
321
end
322
end
323
324
@inline function local_flux_differencing!(du_local, u_local, element_index,
325
have_nonconservative_terms::True, volume_flux,
326
has_sparse_operators::False, mesh,
327
equations, dg, cache)
328
@unpack Qrst_skew = cache
329
NDIMS = ndims(mesh)
330
flux_conservative, flux_nonconservative = volume_flux
331
row_ids = axes(first(Qrst_skew), 1)
332
geometric_matrix = get_contravariant_matrix(element_index, mesh, cache)
333
for i in row_ids
334
u_i = u_local[i]
335
for j in row_ids
336
ref_entries = SVector(ntuple(d -> Qrst_skew[d][i, j], Val(NDIMS)))
337
normal_direction = geometric_matrix * ref_entries
338
# We use the symmetry of the volume flux and the anti-symmetry
339
# of the derivative operator to save half of the volume flux
340
# computations.
341
if j > i
342
u_j = u_local[j]
343
AF_ij = 2 * flux_conservative(u_i, u_j, normal_direction, equations)
344
du_local[i] = du_local[i] + AF_ij
345
du_local[j] = du_local[j] - AF_ij # Due to skew-symmetry
346
end
347
# Non-conservative terms use the full (non-symmetric) loop.
348
# The 0.5f0 factor on the normal direction is necessary for the nonconservative
349
# fluxes based on the interpretation of global SBP operators.
350
# See also `calc_interface_flux!` with `have_nonconservative_terms::True`
351
# in src/solvers/dgsem_tree/dg_1d.jl
352
f_nc = flux_nonconservative(u_i, u_local[j], 0.5f0 * normal_direction,
353
equations)
354
du_local[i] = du_local[i] + 2 * f_nc
355
end
356
end
357
end
358
359
# When the operators are sparse, we use the sum-factorization approach to
360
# computing flux differencing. Each dimension has its own sparse operator with
361
# its own sparsity pattern (e.g., tensor-product structure on Quad/Hex elements),
362
# so we loop per-dimension. For each nonzero entry A[i,j] we evaluate the flux once
363
# and exploit skew-symmetry to accumulate both the (i,j) and (j,i) contributions.
364
@inline function local_flux_differencing!(du_local, u_local, element_index,
365
have_nonconservative_terms::False,
366
volume_flux,
367
has_sparse_operators::True, mesh,
368
equations, dg, cache)
369
@unpack Qrst_skew = cache
370
for dim in eachdim(mesh)
371
normal_directions = get_contravariant_vector(element_index, dim, mesh, cache)
372
Q_skew = Qrst_skew[dim]
373
A_base, row_ids, rows, vals = sparse_operator_data(Q_skew)
374
for i in row_ids
375
u_i = u_local[i]
376
du_i = du_local[i]
377
for id in nzrange(A_base, i)
378
j = rows[id]
379
# This routine computes only the upper-triangular part of the hadamard sum (A .* F).
380
# We avoid computing the lower-triangular part, and instead accumulate those contributions
381
# while computing the upper-triangular part (using the fact that A is skew-symmetric and F
382
# is symmetric).
383
if j > i
384
u_j = u_local[j]
385
A_ij = vals[id]
386
normal_direction_ij = get_normal_direction(normal_directions, i, j)
387
AF_ij = 2 * A_ij *
388
volume_flux(u_i, u_j, normal_direction_ij, equations)
389
du_i = du_i + AF_ij
390
du_local[j] = du_local[j] - AF_ij # Due to skew-symmetry
391
end
392
end
393
du_local[i] = du_i
394
end
395
end
396
end
397
398
@inline function local_flux_differencing!(du_local, u_local, element_index,
399
have_nonconservative_terms::True, volume_flux,
400
has_sparse_operators::True, mesh,
401
equations, dg, cache)
402
@unpack Qrst_skew = cache
403
flux_conservative, flux_nonconservative = volume_flux
404
for dim in eachdim(mesh)
405
normal_directions = get_contravariant_vector(element_index, dim, mesh, cache)
406
Q_skew = Qrst_skew[dim]
407
A_base, row_ids, rows, vals = sparse_operator_data(Q_skew)
408
for i in row_ids
409
u_i = u_local[i]
410
du_i = du_local[i]
411
for id in nzrange(A_base, i)
412
j = rows[id]
413
A_ij = vals[id]
414
u_j = u_local[j]
415
normal_direction_ij = get_normal_direction(normal_directions, i, j)
416
# Conservative part: exploit skew-symmetry (calculate upper triangular part only).
417
if j > i
418
AF_ij = 2 * A_ij *
419
flux_conservative(u_i, u_j, normal_direction_ij, equations)
420
du_i = du_i + AF_ij
421
du_local[j] = du_local[j] - AF_ij # Due to skew-symmetry
422
end
423
# Non-conservative terms use the full (non-symmetric) loop.
424
# The 0.5f0 factor on the normal direction is necessary for the nonconservative
425
# fluxes based on the interpretation of global SBP operators.
426
# See also `calc_interface_flux!` with `have_nonconservative_terms::True`
427
# in src/solvers/dgsem_tree/dg_1d.jl
428
f_nc = flux_nonconservative(u_i, u_j, 0.5f0 * normal_direction_ij,
429
equations)
430
du_i = du_i + 2 * A_ij * f_nc
431
end
432
du_local[i] = du_i
433
end
434
end
435
end
436
437
# calculates volume integral for <:Polynomial approximation types. We
438
# do not assume any additional structure (such as collocated volume or
439
# face nodes, tensor product structure, etc) in `DGMulti`.
440
@inline function volume_integral_kernel!(du, u, element, mesh::DGMultiMesh,
441
have_nonconservative_terms, equations,
442
volume_integral::VolumeIntegralFluxDifferencing,
443
dg::DGMultiFluxDiff, cache, alpha = true)
444
@unpack entropy_projected_u_values, Ph = cache
445
@unpack du_local_threaded, rhs_local_threaded = cache
446
447
du_local = du_local_threaded[Threads.threadid()]
448
fill!(du_local, zero(eltype(du_local)))
449
u_local = view(entropy_projected_u_values, :, element)
450
451
local_flux_differencing!(du_local, u_local, element,
452
have_nonconservative_terms,
453
volume_integral.volume_flux,
454
has_sparse_operators(dg),
455
mesh, equations, dg, cache)
456
457
# convert du_local::Vector{<:SVector} to StructArray{<:SVector} for faster
458
# apply_to_each_field performance.
459
rhs_local = rhs_local_threaded[Threads.threadid()]
460
for i in Base.OneTo(length(du_local))
461
rhs_local[i] = du_local[i]
462
end
463
apply_to_each_field(mul_by_accum!(Ph, alpha), view(du, :, element), rhs_local)
464
465
return nothing
466
end
467
468
@inline function volume_integral_kernel!(du, u, element, mesh::DGMultiMesh,
469
have_nonconservative_terms, equations,
470
volume_integral::VolumeIntegralFluxDifferencing,
471
dg::DGMultiFluxDiffSBP, cache,
472
alpha = true)
473
@unpack du_local_threaded, inv_wq = cache
474
475
du_local = du_local_threaded[Threads.threadid()]
476
fill!(du_local, zero(eltype(du_local)))
477
u_local = view(u, :, element)
478
479
local_flux_differencing!(du_local, u_local, element,
480
have_nonconservative_terms,
481
volume_integral.volume_flux,
482
has_sparse_operators(dg),
483
mesh, equations, dg, cache)
484
485
for i in each_quad_node(mesh, dg, cache)
486
du[i, element] = du[i, element] + alpha * du_local[i] * inv_wq[i]
487
end
488
489
return nothing
490
end
491
492
# Specialize since `u_values` isn't computed for DGMultiFluxDiffSBP solvers.
493
function calc_sources!(du, u, t, source_terms,
494
mesh, equations, dg::DGMultiFluxDiffSBP, cache)
495
md = mesh.md
496
497
@threaded for e in eachelement(mesh, dg, cache)
498
for i in each_quad_node(mesh, dg, cache)
499
du[i, e] += source_terms(u[i, e], SVector(getindex.(md.xyzq, i, e)), t,
500
equations)
501
end
502
end
503
end
504
505
# Specializes on Polynomial (e.g., modal) DG methods with a flux differencing volume integral, e.g.,
506
# an entropy conservative/stable discretization. For modal DG schemes, an extra `entropy_projection!`
507
# is required (see https://doi.org/10.1016/j.jcp.2018.02.033, Section 4.3).
508
# Also called by DGMultiFluxDiff{<:GaussSBP} solvers.
509
function rhs!(du, u, t, mesh, equations, boundary_conditions::BC,
510
source_terms::Source, dg::DGMultiFluxDiff, cache) where {Source, BC}
511
@trixi_timeit timer() "reset ∂u/∂t" set_zero!(du, dg, cache)
512
513
# this function evaluates the solution at volume and face quadrature points (which was previously
514
# done in `prolong2interfaces` and `calc_volume_integral`)
515
@trixi_timeit timer() "entropy_projection!" begin
516
entropy_projection!(cache, u, mesh, equations, dg)
517
end
518
519
@trixi_timeit timer() "volume integral" begin
520
calc_volume_integral!(du, u, mesh, have_nonconservative_terms(equations),
521
equations,
522
dg.volume_integral, dg, cache)
523
end
524
525
# the following functions are the same as in VolumeIntegralWeakForm, and can be reused from dg.jl
526
@trixi_timeit timer() "interface flux" begin
527
calc_interface_flux!(cache, dg.surface_integral, mesh,
528
have_nonconservative_terms(equations), equations, dg)
529
end
530
531
@trixi_timeit timer() "boundary flux" begin
532
calc_boundary_flux!(cache, t, boundary_conditions, mesh,
533
have_nonconservative_terms(equations), equations, dg)
534
end
535
536
@trixi_timeit timer() "surface integral" begin
537
calc_surface_integral!(du, u, mesh, equations,
538
dg.surface_integral, dg, cache)
539
end
540
541
@trixi_timeit timer() "Jacobian" invert_jacobian!(du, mesh, equations, dg, cache)
542
543
@trixi_timeit timer() "source terms" begin
544
calc_sources!(du, u, t, source_terms, mesh, equations, dg, cache)
545
end
546
547
return nothing
548
end
549
550
# Specializes on SBP (e.g., nodal/collocation) DG methods with a flux differencing volume
551
# integral, e.g., an entropy conservative/stable discretization. The implementation of `rhs!`
552
# for such schemes is very similar to the implementation of `rhs!` for standard DG methods,
553
# but specializes `calc_volume_integral`.
554
function rhs!(du, u, t, mesh, equations,
555
boundary_conditions::BC, source_terms::Source,
556
dg::DGMultiFluxDiffSBP, cache) where {BC, Source}
557
@trixi_timeit timer() "reset ∂u/∂t" set_zero!(du, dg, cache)
558
559
@trixi_timeit timer() "volume integral" calc_volume_integral!(du, u, mesh,
560
have_nonconservative_terms(equations),
561
equations,
562
dg.volume_integral,
563
dg, cache)
564
565
@trixi_timeit timer() "prolong2interfaces" prolong2interfaces!(cache, u, mesh,
566
equations, dg)
567
568
@trixi_timeit timer() "interface flux" calc_interface_flux!(cache,
569
dg.surface_integral,
570
mesh,
571
have_nonconservative_terms(equations),
572
equations, dg)
573
574
@trixi_timeit timer() "boundary flux" calc_boundary_flux!(cache, t,
575
boundary_conditions, mesh,
576
have_nonconservative_terms(equations),
577
equations, dg)
578
579
@trixi_timeit timer() "surface integral" calc_surface_integral!(du, u, mesh,
580
equations,
581
dg.surface_integral,
582
dg, cache)
583
584
@trixi_timeit timer() "Jacobian" invert_jacobian!(du, mesh, equations, dg, cache)
585
586
@trixi_timeit timer() "source terms" calc_sources!(du, u, t, source_terms, mesh,
587
equations, dg, cache)
588
589
return nothing
590
end
591
end # @muladd
592
593