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_gauss_sbp.jl
5591 views
1
2
# ========= GaussSBP approximation types ============
3
# Note: we define type aliases outside of the @muladd block to avoid Revise breaking when code
4
# inside the @muladd block is edited. See https://github.com/trixi-framework/Trixi.jl/issues/801
5
# for more details.
6
7
# `GaussSBP` is a type alias for a StartUpDG type (e.g., Gauss nodes on quads/hexes)
8
const GaussSBP = Polynomial{Gauss}
9
10
function tensor_product_quadrature(element_type::Line, r1D, w1D)
11
return r1D, w1D
12
end
13
14
function tensor_product_quadrature(element_type::Quad, r1D, w1D)
15
sq, rq = vec.(StartUpDG.NodesAndModes.meshgrid(r1D))
16
ws, wr = vec.(StartUpDG.NodesAndModes.meshgrid(w1D))
17
wq = wr .* ws
18
return rq, sq, wq
19
end
20
21
function tensor_product_quadrature(element_type::Hex, r1D, w1D)
22
rq, sq, tq = vec.(StartUpDG.NodesAndModes.meshgrid(r1D, r1D, r1D))
23
wr, ws, wt = vec.(StartUpDG.NodesAndModes.meshgrid(w1D, w1D, w1D))
24
wq = wr .* ws .* wt
25
return rq, sq, tq, wq
26
end
27
28
# type parameters for `TensorProductFaceOperator`.
29
abstract type AbstractGaussOperator end
30
struct Interpolation <: AbstractGaussOperator end
31
# - `Projection{ScaleByFaceWeights = False()}` corresponds to the operator `projection_matrix_gauss_to_face = M \ Vf'`,
32
# which is used in `VolumeIntegralFluxDifferencing`.
33
# - `Projection{ScaleByFaceWeights = True()}` corresponds to the quadrature-based lifting
34
# operator `LIFT = M \ (Vf' * diagm(rd.wf))`, which is used in `SurfaceIntegralWeakForm`
35
struct Projection{ScaleByFaceWeights} <: AbstractGaussOperator end
36
37
# used to dispatch for different Gauss interpolation operators
38
abstract type AbstractTensorProductGaussOperator end
39
40
# TensorProductGaussFaceOperator{Tmat, Ti}
41
#
42
# Data for performing tensor product interpolation from volume nodes to face nodes.
43
struct TensorProductGaussFaceOperator{NDIMS, OperatorType <: AbstractGaussOperator,
44
Tmat, Tweights, Tfweights, Tindices} <:
45
AbstractTensorProductGaussOperator
46
interp_matrix_gauss_to_face_1d::Tmat
47
inv_volume_weights_1d::Tweights
48
face_weights::Tfweights
49
face_indices_tensor_product::Tindices
50
nnodes_1d::Int
51
nfaces::Int
52
end
53
54
function TensorProductGaussFaceOperator(operator::AbstractGaussOperator,
55
dg::DGMulti{1, Line, GaussSBP})
56
rd = dg.basis
57
58
rq1D, wq1D = StartUpDG.gauss_quad(0, 0, polydeg(dg))
59
interp_matrix_gauss_to_face_1d = polynomial_interpolation_matrix(rq1D, [-1; 1])
60
61
nnodes_1d = length(rq1D)
62
face_indices_tensor_product = nothing # not needed in 1D; we fall back to mul!
63
64
num_faces = 2
65
66
T_op = typeof(operator)
67
Tm = typeof(interp_matrix_gauss_to_face_1d)
68
Tw = typeof(inv.(wq1D))
69
Tf = typeof(rd.wf)
70
Ti = typeof(face_indices_tensor_product)
71
return TensorProductGaussFaceOperator{1, T_op, Tm, Tw, Tf, Ti}(interp_matrix_gauss_to_face_1d,
72
inv.(wq1D), rd.wf,
73
face_indices_tensor_product,
74
nnodes_1d, num_faces)
75
end
76
77
# constructor for a 2D operator
78
function TensorProductGaussFaceOperator(operator::AbstractGaussOperator,
79
dg::DGMulti{2, Quad, GaussSBP})
80
rd = dg.basis
81
82
rq1D, wq1D = StartUpDG.gauss_quad(0, 0, polydeg(dg))
83
interp_matrix_gauss_to_face_1d = polynomial_interpolation_matrix(rq1D, [-1; 1])
84
85
nnodes_1d = length(rq1D)
86
87
# Permutation of indices in a tensor product form
88
num_faces = StartUpDG.num_faces(rd.element_type)
89
indices = reshape(1:length(rd.rf), nnodes_1d, num_faces)
90
face_indices_tensor_product = zeros(Int, 2, nnodes_1d, ndims(rd.element_type))
91
for i in 1:nnodes_1d # loop over nodes in one face
92
face_indices_tensor_product[:, i, 1] .= indices[i, 1:2]
93
face_indices_tensor_product[:, i, 2] .= indices[i, 3:4]
94
end
95
96
T_op = typeof(operator)
97
Tm = typeof(interp_matrix_gauss_to_face_1d)
98
Tw = typeof(inv.(wq1D))
99
Tf = typeof(rd.wf)
100
Ti = typeof(face_indices_tensor_product)
101
return TensorProductGaussFaceOperator{2, T_op, Tm, Tw, Tf, Ti}(interp_matrix_gauss_to_face_1d,
102
inv.(wq1D), rd.wf,
103
face_indices_tensor_product,
104
nnodes_1d, num_faces)
105
end
106
107
# constructor for a 3D operator
108
function TensorProductGaussFaceOperator(operator::AbstractGaussOperator,
109
dg::DGMulti{3, Hex, GaussSBP})
110
rd = dg.basis
111
112
rq1D, wq1D = StartUpDG.gauss_quad(0, 0, polydeg(dg))
113
interp_matrix_gauss_to_face_1d = polynomial_interpolation_matrix(rq1D, [-1; 1])
114
115
nnodes_1d = length(rq1D)
116
117
# Permutation of indices in a tensor product form
118
num_faces = StartUpDG.num_faces(rd.element_type)
119
indices = reshape(1:length(rd.rf), nnodes_1d, nnodes_1d, num_faces)
120
face_indices_tensor_product = zeros(Int, 2, nnodes_1d, nnodes_1d,
121
ndims(rd.element_type))
122
for j in 1:nnodes_1d, i in 1:nnodes_1d # loop over nodes in one face
123
face_indices_tensor_product[:, i, j, 1] .= indices[i, j, 1:2]
124
face_indices_tensor_product[:, i, j, 2] .= indices[i, j, 3:4]
125
face_indices_tensor_product[:, i, j, 3] .= indices[i, j, 5:6]
126
end
127
128
T_op = typeof(operator)
129
Tm = typeof(interp_matrix_gauss_to_face_1d)
130
Tw = typeof(inv.(wq1D))
131
Tf = typeof(rd.wf)
132
Ti = typeof(face_indices_tensor_product)
133
return TensorProductGaussFaceOperator{3, T_op, Tm, Tw, Tf, Ti}(interp_matrix_gauss_to_face_1d,
134
inv.(wq1D), rd.wf,
135
face_indices_tensor_product,
136
nnodes_1d, num_faces)
137
end
138
139
# specialize behavior of `mul_by!(A)` where `A isa TensorProductGaussFaceOperator)`
140
@inline function mul_by!(A::AbstractTensorProductGaussOperator)
141
return (out, x) -> tensor_product_gauss_face_operator!(out, A, x)
142
end
143
144
@inline function tensor_product_gauss_face_operator!(out::AbstractMatrix,
145
A::AbstractTensorProductGaussOperator,
146
x::AbstractMatrix)
147
@threaded for col in Base.OneTo(size(out, 2))
148
tensor_product_gauss_face_operator!(view(out, :, col), A, view(x, :, col))
149
end
150
end
151
152
@inline function tensor_product_gauss_face_operator!(out::AbstractVector,
153
A::TensorProductGaussFaceOperator{1,
154
Interpolation},
155
x::AbstractVector)
156
return mul!(out, A.interp_matrix_gauss_to_face_1d, x)
157
end
158
159
@inline function tensor_product_gauss_face_operator!(out::AbstractVector,
160
A::TensorProductGaussFaceOperator{1,
161
<:Projection},
162
x::AbstractVector)
163
mul!(out, A.interp_matrix_gauss_to_face_1d', x)
164
@. out *= A.inv_volume_weights_1d
165
end
166
167
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
168
# Since these FMAs can increase the performance of many numerical algorithms,
169
# we need to opt-in explicitly.
170
# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
171
@muladd begin
172
#! format: noindent
173
174
#! format: off
175
# Interpolates values from volume Gauss nodes to face nodes on one element.
176
@inline function tensor_product_gauss_face_operator!(out::AbstractVector,
177
A::TensorProductGaussFaceOperator{2, Interpolation},
178
x_in::AbstractVector)
179
#! format: on
180
(; interp_matrix_gauss_to_face_1d, face_indices_tensor_product) = A
181
(; nnodes_1d) = A
182
183
fill!(out, zero(eltype(out)))
184
185
# for 2D GaussSBP nodes, the indexing is first in x, then in y
186
x = reshape(x_in, nnodes_1d, nnodes_1d)
187
188
# interpolation in the x-direction
189
@turbo for i in Base.OneTo(nnodes_1d) # loop over nodes in a face
190
index_left = face_indices_tensor_product[1, i, 1]
191
index_right = face_indices_tensor_product[2, i, 1]
192
for jj in Base.OneTo(nnodes_1d) # loop over "line" of volume nodes
193
out[index_left] = out[index_left] +
194
interp_matrix_gauss_to_face_1d[1, jj] * x[jj, i]
195
out[index_right] = out[index_right] +
196
interp_matrix_gauss_to_face_1d[2, jj] * x[jj, i]
197
end
198
end
199
200
# interpolation in the y-direction
201
@turbo for i in Base.OneTo(nnodes_1d) # loop over nodes in a face
202
index_left = face_indices_tensor_product[1, i, 2]
203
index_right = face_indices_tensor_product[2, i, 2]
204
for jj in Base.OneTo(nnodes_1d) # loop over "line" of volume nodes
205
out[index_left] = out[index_left] +
206
interp_matrix_gauss_to_face_1d[1, jj] * x[i, jj]
207
out[index_right] = out[index_right] +
208
interp_matrix_gauss_to_face_1d[2, jj] * x[i, jj]
209
end
210
end
211
212
return nothing
213
end
214
215
# Interpolates values from volume Gauss nodes to face nodes on one element.
216
#! format: off
217
@inline function tensor_product_gauss_face_operator!(out::AbstractVector,
218
A::TensorProductGaussFaceOperator{3, Interpolation},
219
x::AbstractVector)
220
#! format: on
221
(; interp_matrix_gauss_to_face_1d, face_indices_tensor_product) = A
222
(; nnodes_1d) = A
223
224
fill!(out, zero(eltype(out)))
225
226
# for 3D GaussSBP nodes, the indexing is first in y, then x, then z.
227
x = reshape(x, nnodes_1d, nnodes_1d, nnodes_1d)
228
229
# interpolation in the y-direction
230
@turbo for j in Base.OneTo(nnodes_1d), i in Base.OneTo(nnodes_1d) # loop over nodes in a face
231
index_left = face_indices_tensor_product[1, i, j, 2]
232
index_right = face_indices_tensor_product[2, i, j, 2]
233
for jj in Base.OneTo(nnodes_1d) # loop over "line" of volume nodes
234
out[index_left] = out[index_left] +
235
interp_matrix_gauss_to_face_1d[1, jj] * x[jj, i, j]
236
out[index_right] = out[index_right] +
237
interp_matrix_gauss_to_face_1d[2, jj] * x[jj, i, j]
238
end
239
end
240
241
# interpolation in the x-direction
242
@turbo for j in Base.OneTo(nnodes_1d), i in Base.OneTo(nnodes_1d) # loop over nodes in a face
243
index_left = face_indices_tensor_product[1, i, j, 1]
244
index_right = face_indices_tensor_product[2, i, j, 1]
245
for jj in Base.OneTo(nnodes_1d) # loop over "line" of volume nodes
246
out[index_left] = out[index_left] +
247
interp_matrix_gauss_to_face_1d[1, jj] * x[i, jj, j]
248
out[index_right] = out[index_right] +
249
interp_matrix_gauss_to_face_1d[2, jj] * x[i, jj, j]
250
end
251
end
252
253
# interpolation in the z-direction
254
@turbo for i in Base.OneTo(nnodes_1d), j in Base.OneTo(nnodes_1d) # loop over nodes in a face
255
index_left = face_indices_tensor_product[1, i, j, 3]
256
index_right = face_indices_tensor_product[2, i, j, 3]
257
for jj in Base.OneTo(nnodes_1d) # loop over "line" of volume nodes
258
# The ordering (i,j) -> (j,i) needs to be reversed for this last face.
259
# This is due to way we define face nodes for Hex() types in StartUpDG.jl.
260
out[index_left] = out[index_left] +
261
interp_matrix_gauss_to_face_1d[1, jj] * x[j, i, jj]
262
out[index_right] = out[index_right] +
263
interp_matrix_gauss_to_face_1d[2, jj] * x[j, i, jj]
264
end
265
end
266
267
return nothing
268
end
269
270
# Projects face node values to volume Gauss nodes on one element.
271
#! format: off
272
@inline function tensor_product_gauss_face_operator!(out_vec::AbstractVector,
273
A::TensorProductGaussFaceOperator{2, Projection{ApplyFaceWeights}},
274
x::AbstractVector) where {ApplyFaceWeights}
275
#! format: on
276
(; interp_matrix_gauss_to_face_1d, face_indices_tensor_product) = A
277
(; inv_volume_weights_1d, nnodes_1d) = A
278
279
fill!(out_vec, zero(eltype(out_vec)))
280
281
# As of Julia 1.9, Base.ReshapedArray does not produce allocations when setting values.
282
# Thus, Base.ReshapedArray should be used if you are setting values in the array.
283
# `reshape` is fine if you are only accessing values.
284
# Note that, for 2D GaussSBP nodes, the indexing is first in x, then y
285
out = Base.ReshapedArray(out_vec, (nnodes_1d, nnodes_1d), ())
286
287
if ApplyFaceWeights == true
288
@turbo for i in eachindex(x)
289
x[i] = x[i] * A.face_weights[i]
290
end
291
end
292
293
# interpolation in the x-direction
294
@turbo for i in Base.OneTo(nnodes_1d) # loop over face nodes
295
index_left = face_indices_tensor_product[1, i, 1]
296
index_right = face_indices_tensor_product[2, i, 1]
297
for jj in Base.OneTo(nnodes_1d) # loop over a line of volume nodes
298
out[jj, i] = out[jj, i] +
299
interp_matrix_gauss_to_face_1d[1, jj] * x[index_left]
300
out[jj, i] = out[jj, i] +
301
interp_matrix_gauss_to_face_1d[2, jj] * x[index_right]
302
end
303
end
304
305
# interpolation in the y-direction
306
@turbo for i in Base.OneTo(nnodes_1d)
307
index_left = face_indices_tensor_product[1, i, 2]
308
index_right = face_indices_tensor_product[2, i, 2]
309
# loop over a line of volume nodes
310
for jj in Base.OneTo(nnodes_1d)
311
out[i, jj] = out[i, jj] +
312
interp_matrix_gauss_to_face_1d[1, jj] * x[index_left]
313
out[i, jj] = out[i, jj] +
314
interp_matrix_gauss_to_face_1d[2, jj] * x[index_right]
315
end
316
end
317
318
# apply inv(M)
319
@turbo for j in Base.OneTo(nnodes_1d), i in Base.OneTo(nnodes_1d)
320
out[i, j] = out[i, j] * inv_volume_weights_1d[i] * inv_volume_weights_1d[j]
321
end
322
323
return nothing
324
end
325
326
# Interpolates values from volume Gauss nodes to face nodes on one element.
327
#! format: off
328
@inline function tensor_product_gauss_face_operator!(out_vec::AbstractVector,
329
A::TensorProductGaussFaceOperator{3, Projection{ApplyFaceWeights}},
330
x::AbstractVector) where {ApplyFaceWeights}
331
#! format: on
332
@unpack interp_matrix_gauss_to_face_1d, face_indices_tensor_product = A
333
@unpack inv_volume_weights_1d, nnodes_1d, nfaces = A
334
335
fill!(out_vec, zero(eltype(out_vec)))
336
337
# As of Julia 1.9, Base.ReshapedArray does not produce allocations when setting values.
338
# Thus, Base.ReshapedArray should be used if you are setting values in the array.
339
# `reshape` is fine if you are only accessing values.
340
# Note that, for 3D GaussSBP nodes, the indexing is first in y, then x, then z.
341
out = Base.ReshapedArray(out_vec, (nnodes_1d, nnodes_1d, nnodes_1d), ())
342
343
if ApplyFaceWeights == true
344
@turbo for i in eachindex(x)
345
x[i] = x[i] * A.face_weights[i]
346
end
347
end
348
349
# interpolation in the y-direction
350
@turbo for j in Base.OneTo(nnodes_1d), i in Base.OneTo(nnodes_1d) # loop over nodes in a face
351
index_left = face_indices_tensor_product[1, i, j, 2]
352
index_right = face_indices_tensor_product[2, i, j, 2]
353
for jj in Base.OneTo(nnodes_1d) # loop over "line" of volume nodes
354
out[jj, i, j] = out[jj, i, j] +
355
interp_matrix_gauss_to_face_1d[1, jj] * x[index_left]
356
out[jj, i, j] = out[jj, i, j] +
357
interp_matrix_gauss_to_face_1d[2, jj] * x[index_right]
358
end
359
end
360
361
# interpolation in the x-direction
362
@turbo for j in Base.OneTo(nnodes_1d), i in Base.OneTo(nnodes_1d) # loop over nodes in a face
363
index_left = face_indices_tensor_product[1, i, j, 1]
364
index_right = face_indices_tensor_product[2, i, j, 1]
365
for jj in Base.OneTo(nnodes_1d) # loop over "line" of volume nodes
366
out[i, jj, j] = out[i, jj, j] +
367
interp_matrix_gauss_to_face_1d[1, jj] * x[index_left]
368
out[i, jj, j] = out[i, jj, j] +
369
interp_matrix_gauss_to_face_1d[2, jj] * x[index_right]
370
end
371
end
372
373
# interpolation in the z-direction
374
@turbo for i in Base.OneTo(nnodes_1d), j in Base.OneTo(nnodes_1d) # loop over nodes in a face
375
index_left = face_indices_tensor_product[1, i, j, 3]
376
index_right = face_indices_tensor_product[2, i, j, 3]
377
for jj in Base.OneTo(nnodes_1d) # loop over "line" of volume nodes
378
# The ordering (i,j) -> (j,i) needs to be reversed for this last face.
379
# This is due to way we define face nodes for Hex() types in StartUpDG.jl.
380
out[j, i, jj] = out[j, i, jj] +
381
interp_matrix_gauss_to_face_1d[1, jj] * x[index_left]
382
out[j, i, jj] = out[j, i, jj] +
383
interp_matrix_gauss_to_face_1d[2, jj] * x[index_right]
384
end
385
end
386
387
# apply inv(M)
388
@turbo for k in Base.OneTo(nnodes_1d), j in Base.OneTo(nnodes_1d),
389
i in Base.OneTo(nnodes_1d)
390
391
out[i, j, k] = out[i, j, k] * inv_volume_weights_1d[i] *
392
inv_volume_weights_1d[j] * inv_volume_weights_1d[k]
393
end
394
395
return nothing
396
end
397
398
# For now, this is mostly the same as `create_cache` for DGMultiFluxDiff{<:Polynomial}.
399
# In the future, we may modify it so that we can specialize additional parts of GaussSBP() solvers.
400
function create_cache(mesh::DGMultiMesh, equations,
401
dg::DGMultiFluxDiff{<:GaussSBP, <:Union{Line, Quad, Hex}}, RealT,
402
uEltype)
403
404
# call general Polynomial flux differencing constructor
405
cache = invoke(create_cache,
406
Tuple{typeof(mesh), typeof(equations),
407
DGMultiFluxDiff, typeof(RealT), typeof(uEltype)},
408
mesh, equations, dg, RealT, uEltype)
409
410
rd = dg.basis
411
@unpack md = mesh
412
413
# for change of basis prior to the volume integral and entropy projection
414
r1D, _ = StartUpDG.gauss_lobatto_quad(0, 0, polydeg(dg))
415
rq1D, _ = StartUpDG.gauss_quad(0, 0, polydeg(dg))
416
interp_matrix_lobatto_to_gauss_1D = polynomial_interpolation_matrix(r1D, rq1D)
417
interp_matrix_gauss_to_lobatto_1D = polynomial_interpolation_matrix(rq1D, r1D)
418
NDIMS = ndims(rd.element_type)
419
interp_matrix_lobatto_to_gauss = SimpleKronecker(NDIMS,
420
interp_matrix_lobatto_to_gauss_1D,
421
uEltype)
422
interp_matrix_gauss_to_lobatto = SimpleKronecker(NDIMS,
423
interp_matrix_gauss_to_lobatto_1D,
424
uEltype)
425
inv_gauss_weights = inv.(rd.wq)
426
427
# specialized operators to perform tensor product interpolation to faces for Gauss nodes
428
interp_matrix_gauss_to_face = TensorProductGaussFaceOperator(Interpolation(), dg)
429
projection_matrix_gauss_to_face = TensorProductGaussFaceOperator(Projection{False()}(),
430
dg)
431
432
# `LIFT` matrix for Gauss nodes - this is equivalent to `projection_matrix_gauss_to_face` scaled by `diagm(rd.wf)`,
433
# where `rd.wf` are Gauss node face quadrature weights.
434
gauss_LIFT = TensorProductGaussFaceOperator(Projection{True()}(), dg)
435
436
nvars = nvariables(equations)
437
rhs_volume_local_threaded = [allocate_nested_array(uEltype, nvars, (rd.Nq,), dg)
438
for _ in 1:Threads.maxthreadid()]
439
gauss_volume_local_threaded = [allocate_nested_array(uEltype, nvars, (rd.Nq,), dg)
440
for _ in 1:Threads.maxthreadid()]
441
442
return (; cache..., projection_matrix_gauss_to_face, gauss_LIFT, inv_gauss_weights,
443
rhs_volume_local_threaded, gauss_volume_local_threaded,
444
interp_matrix_lobatto_to_gauss, interp_matrix_gauss_to_lobatto,
445
interp_matrix_gauss_to_face,
446
create_cache(mesh, equations, dg.volume_integral, dg, RealT, uEltype)...) # add cache specialized on the volume integral
447
end
448
449
# by default, return an empty tuple for volume integral caches
450
create_cache(mesh, equations, volume_integral, dg, RealT, uEltype) = NamedTuple()
451
452
# TODO: DGMulti. Address hard-coding of `entropy2cons!` and `cons2entropy!` for this function.
453
function entropy_projection!(cache, u, mesh::DGMultiMesh, equations,
454
dg::DGMultiFluxDiff{<:GaussSBP})
455
rd = dg.basis
456
@unpack Vq = rd
457
@unpack VhP, entropy_var_values = cache
458
@unpack projected_entropy_var_values, entropy_projected_u_values = cache
459
@unpack interp_matrix_lobatto_to_gauss, interp_matrix_gauss_to_face = cache
460
(; u_values) = cache.solution_container
461
462
@threaded for e in eachelement(mesh, dg, cache)
463
apply_to_each_field(mul_by!(interp_matrix_lobatto_to_gauss),
464
view(u_values, :, e), view(u, :, e))
465
end
466
467
# transform quadrature values to entropy variables
468
cons2entropy!(entropy_var_values, u_values, equations)
469
470
volume_indices = Base.OneTo(rd.Nq)
471
face_indices = (rd.Nq + 1):(rd.Nq + rd.Nfq)
472
473
# Interpolate volume Gauss nodes to Gauss face nodes (note the layout of
474
# `projected_entropy_var_values = [vol pts; face pts]`).
475
entropy_var_face_values = view(projected_entropy_var_values, face_indices, :)
476
apply_to_each_field(mul_by!(interp_matrix_gauss_to_face), entropy_var_face_values,
477
entropy_var_values)
478
479
# directly copy over volume values (no entropy projection required)
480
entropy_projected_volume_values = view(entropy_projected_u_values, volume_indices,
481
:)
482
@threaded for i in eachindex(u_values)
483
entropy_projected_volume_values[i] = u_values[i]
484
end
485
486
# transform entropy to conservative variables on face values
487
entropy_projected_face_values = view(entropy_projected_u_values, face_indices, :)
488
entropy2cons!(entropy_projected_face_values, entropy_var_face_values, equations)
489
490
return nothing
491
end
492
493
# Assumes cache.solution_container.flux_face_values is already computed.
494
# Enables tensor product evaluation of `LIFT isa TensorProductGaussFaceOperator`.
495
function calc_surface_integral!(du, u, mesh::DGMultiMesh, equations,
496
surface_integral::SurfaceIntegralWeakForm,
497
dg::DGMultiFluxDiff{<:GaussSBP}, cache)
498
(; gauss_LIFT, gauss_volume_local_threaded) = cache
499
500
@threaded for e in eachelement(mesh, dg, cache)
501
502
# applies LIFT matrix, output is stored at Gauss nodes
503
gauss_volume_local = gauss_volume_local_threaded[Threads.threadid()]
504
apply_to_each_field(mul_by!(gauss_LIFT), gauss_volume_local,
505
view(cache.solution_container.flux_face_values, :, e))
506
507
for i in eachindex(gauss_volume_local)
508
du[i, e] = du[i, e] + gauss_volume_local[i]
509
end
510
end
511
512
return nothing
513
end
514
515
function project_rhs_to_gauss_nodes!(du, rhs_local, element, mesh::DGMultiMesh,
516
dg::DGMulti, cache, alpha = true)
517
518
# Here, we exploit that under a Gauss nodal basis the structure of the projection
519
# matrix `Ph = [diagm(1 ./ wq), projection_matrix_gauss_to_face]` such that
520
# `Ph * [u; uf] = (u ./ wq) + projection_matrix_gauss_to_face * uf`.
521
volume_indices = Base.OneTo(dg.basis.Nq)
522
face_indices = (dg.basis.Nq + 1):(dg.basis.Nq + dg.basis.Nfq)
523
local_volume_flux = view(rhs_local, volume_indices)
524
local_face_flux = view(rhs_local, face_indices)
525
526
# initialize rhs_volume_local = projection_matrix_gauss_to_face * local_face_flux
527
rhs_volume_local = cache.rhs_volume_local_threaded[Threads.threadid()]
528
apply_to_each_field(mul_by!(cache.projection_matrix_gauss_to_face),
529
rhs_volume_local, local_face_flux)
530
531
# accumulate volume contributions at Gauss nodes
532
for i in eachindex(rhs_volume_local)
533
du_local = rhs_volume_local[i] +
534
local_volume_flux[i] * cache.inv_gauss_weights[i]
535
du[i, element] = du[i, element] + alpha * du_local
536
end
537
538
return nothing
539
end
540
541
function volume_integral_kernel!(du, u, element, mesh::DGMultiMesh,
542
have_nonconservative_terms, equations,
543
volume_integral::VolumeIntegralFluxDifferencing,
544
dg::DGMultiFluxDiff{<:GaussSBP}, cache, alpha = true)
545
(; volume_flux) = volume_integral
546
547
du_local = cache.du_local_threaded[Threads.threadid()]
548
fill!(du_local, zero(eltype(du_local)))
549
u_local = view(cache.entropy_projected_u_values, :, element)
550
551
local_flux_differencing!(du_local, u_local, element,
552
have_nonconservative_terms,
553
volume_flux, has_sparse_operators(dg),
554
mesh, equations, dg, cache)
555
556
# convert `du_local::Vector{<:SVector}` to `rhs_local::StructArray{<:SVector}`
557
# for faster performance when using `apply_to_each_field`.
558
rhs_local = cache.rhs_local_threaded[Threads.threadid()]
559
for i in Base.OneTo(length(du_local))
560
rhs_local[i] = du_local[i]
561
end
562
563
return project_rhs_to_gauss_nodes!(du, rhs_local, element, mesh, dg, cache, alpha)
564
end
565
566
# interpolate back to Lobatto nodes after applying the inverse Jacobian at Gauss points
567
function invert_jacobian_and_interpolate!(du, mesh::DGMultiMesh, equations,
568
dg::DGMultiFluxDiff{<:GaussSBP}, cache;
569
scaling = -1)
570
(; interp_matrix_gauss_to_lobatto, rhs_volume_local_threaded, invJ) = cache
571
572
@threaded for e in eachelement(mesh, dg, cache)
573
rhs_volume_local = rhs_volume_local_threaded[Threads.threadid()]
574
575
# At this point, `rhs_volume_local` should still be stored at Gauss points.
576
# We scale it by the inverse Jacobian before transforming back to Lobatto.
577
for i in eachindex(rhs_volume_local)
578
rhs_volume_local[i] = du[i, e] * invJ[i, e] * scaling
579
end
580
581
# Interpolate result back to Lobatto nodes for ease of analysis, visualization
582
apply_to_each_field(mul_by!(interp_matrix_gauss_to_lobatto),
583
view(du, :, e), rhs_volume_local)
584
end
585
586
return nothing
587
end
588
589
# Specialize RHS so that we can call `invert_jacobian_and_interpolate!` instead of just `invert_jacobian!`,
590
# since `invert_jacobian!` is also used in other places (e.g., parabolic terms).
591
function rhs!(du, u, t, mesh, equations, boundary_conditions::BC,
592
source_terms::Source, dg::DGMultiFluxDiff{<:GaussSBP},
593
cache) where {Source, BC}
594
@trixi_timeit timer() "reset ∂u/∂t" set_zero!(du, dg, cache)
595
596
# this function evaluates the solution at volume and face quadrature points (which was previously
597
# done in `prolong2interfaces` and `calc_volume_integral`)
598
@trixi_timeit timer() "entropy_projection!" begin
599
entropy_projection!(cache, u, mesh, equations, dg)
600
end
601
602
# `du` is stored at Gauss nodes here
603
@trixi_timeit timer() "volume integral" begin
604
calc_volume_integral!(du, u, mesh,
605
have_nonconservative_terms(equations), equations,
606
dg.volume_integral, dg, cache)
607
end
608
609
# the following functions are the same as in VolumeIntegralWeakForm, and can be reused from dg.jl
610
@trixi_timeit timer() "interface flux" begin
611
calc_interface_flux!(cache, dg.surface_integral, mesh,
612
have_nonconservative_terms(equations), equations, dg)
613
end
614
615
@trixi_timeit timer() "boundary flux" begin
616
calc_boundary_flux!(cache, t, boundary_conditions, mesh,
617
have_nonconservative_terms(equations), equations, dg)
618
end
619
620
# `du` is stored at Gauss nodes here
621
@trixi_timeit timer() "surface integral" begin
622
calc_surface_integral!(du, u, mesh, equations,
623
dg.surface_integral, dg, cache)
624
end
625
626
# invert Jacobian and map `du` from Gauss to Lobatto nodes
627
@trixi_timeit timer() "Jacobian" begin
628
invert_jacobian_and_interpolate!(du, mesh, equations, dg, cache)
629
end
630
631
@trixi_timeit timer() "source terms" begin
632
calc_sources!(du, u, t, source_terms, mesh, equations, dg, cache)
633
end
634
635
return nothing
636
end
637
end # @muladd
638
639