Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/solvers/dgsem_structured/containers_2d.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
# Initialize data structures in element container
9
function init_elements!(elements, mesh::Union{StructuredMesh{2}, StructuredMeshView{2}},
10
basis::LobattoLegendreBasis)
11
@unpack node_coordinates, left_neighbors,
12
jacobian_matrix, contravariant_vectors, inverse_jacobian = elements
13
14
linear_indices = LinearIndices(size(mesh))
15
16
# Calculate node coordinates, Jacobian matrix, and inverse Jacobian determinant
17
for cell_y in 1:size(mesh, 2), cell_x in 1:size(mesh, 1)
18
element = linear_indices[cell_x, cell_y]
19
20
calc_node_coordinates!(node_coordinates, element, cell_x, cell_y, mesh.mapping,
21
mesh, basis)
22
23
calc_jacobian_matrix!(jacobian_matrix, element, node_coordinates, basis)
24
25
calc_contravariant_vectors!(contravariant_vectors, element, jacobian_matrix)
26
27
calc_inverse_jacobian!(inverse_jacobian, element, jacobian_matrix)
28
end
29
30
initialize_left_neighbor_connectivity!(left_neighbors, mesh, linear_indices)
31
32
return nothing
33
end
34
35
# Calculate physical coordinates to which every node of the reference element is mapped
36
# `mesh.mapping` is passed as an additional argument for type stability (function barrier)
37
function calc_node_coordinates!(node_coordinates, element,
38
cell_x, cell_y, mapping,
39
mesh::StructuredMesh{2},
40
basis::LobattoLegendreBasis)
41
@unpack nodes = basis
42
43
# Get cell length in reference mesh
44
dx = 2 / size(mesh, 1)
45
dy = 2 / size(mesh, 2)
46
47
# Calculate node coordinates of reference mesh
48
cell_x_offset = -1 + (cell_x - 1) * dx + dx / 2
49
cell_y_offset = -1 + (cell_y - 1) * dy + dy / 2
50
51
for j in eachnode(basis), i in eachnode(basis)
52
# node_coordinates are the mapped reference node_coordinates
53
node_coordinates[:, i, j, element] .= mapping(cell_x_offset + dx / 2 * nodes[i],
54
cell_y_offset + dy / 2 * nodes[j])
55
end
56
57
return nothing
58
end
59
60
# Calculate Jacobian matrix of the mapping from the reference element to the element in the physical domain
61
function calc_jacobian_matrix!(jacobian_matrix, element,
62
node_coordinates::AbstractArray{<:Any, 4},
63
basis::AbstractBasisSBP)
64
@unpack derivative_matrix = basis
65
66
# The code below is equivalent to the following matrix multiplications, which
67
# seem to end up calling generic linear algebra code from Julia. Thus, the
68
# optimized code below using `@turbo` is much faster.
69
# jacobian_matrix[1, 1, :, :, element] = derivative_matrix * node_coordinates[1, :, :, element] # x_ξ
70
# jacobian_matrix[2, 1, :, :, element] = derivative_matrix * node_coordinates[2, :, :, element] # y_ξ
71
# jacobian_matrix[1, 2, :, :, element] = node_coordinates[1, :, :, element] * derivative_matrix' # x_η
72
# jacobian_matrix[2, 2, :, :, element] = node_coordinates[2, :, :, element] * derivative_matrix' # y_η
73
74
# x_ξ, y_ξ
75
@turbo for xy in indices((jacobian_matrix, node_coordinates), (1, 1))
76
for j in indices((jacobian_matrix, node_coordinates), (4, 3)),
77
i in indices((jacobian_matrix, derivative_matrix), (3, 1))
78
79
result = zero(eltype(jacobian_matrix))
80
for ii in indices((node_coordinates, derivative_matrix), (2, 2))
81
result += derivative_matrix[i, ii] *
82
node_coordinates[xy, ii, j, element]
83
end
84
jacobian_matrix[xy, 1, i, j, element] = result
85
end
86
end
87
88
# x_η, y_η
89
@turbo for xy in indices((jacobian_matrix, node_coordinates), (1, 1))
90
for j in indices((jacobian_matrix, derivative_matrix), (4, 1)),
91
i in indices((jacobian_matrix, node_coordinates), (3, 2))
92
93
result = zero(eltype(jacobian_matrix))
94
for jj in indices((node_coordinates, derivative_matrix), (3, 2))
95
result += derivative_matrix[j, jj] *
96
node_coordinates[xy, i, jj, element]
97
end
98
jacobian_matrix[xy, 2, i, j, element] = result
99
end
100
end
101
102
return jacobian_matrix
103
end
104
105
# Calculate contravariant vectors, multiplied by the Jacobian determinant J of the transformation mapping.
106
# Those are called Ja^i in Kopriva's blue book.
107
function calc_contravariant_vectors!(contravariant_vectors::AbstractArray{<:Any, 5},
108
element, jacobian_matrix)
109
# The code below is equivalent to the following using broadcasting but much faster.
110
# # First contravariant vector Ja^1
111
# contravariant_vectors[1, 1, :, :, element] = jacobian_matrix[2, 2, :, :, element]
112
# contravariant_vectors[2, 1, :, :, element] = -jacobian_matrix[1, 2, :, :, element]
113
# # Second contravariant vector Ja^2
114
# contravariant_vectors[1, 2, :, :, element] = -jacobian_matrix[2, 1, :, :, element]
115
# contravariant_vectors[2, 2, :, :, element] = jacobian_matrix[1, 1, :, :, element]
116
117
@turbo for j in indices((contravariant_vectors, jacobian_matrix), (4, 4)),
118
i in indices((contravariant_vectors, jacobian_matrix), (3, 3))
119
# First contravariant vector Ja^1
120
contravariant_vectors[1, 1, i, j, element] = jacobian_matrix[2, 2, i, j,
121
element]
122
contravariant_vectors[2, 1, i, j, element] = -jacobian_matrix[1, 2, i, j,
123
element]
124
125
# Second contravariant vector Ja^2
126
contravariant_vectors[1, 2, i, j, element] = -jacobian_matrix[2, 1, i, j,
127
element]
128
contravariant_vectors[2, 2, i, j, element] = jacobian_matrix[1, 1, i, j,
129
element]
130
end
131
132
return contravariant_vectors
133
end
134
135
# Calculate inverse Jacobian (determinant of Jacobian matrix of the mapping) in each node
136
function calc_inverse_jacobian!(inverse_jacobian::AbstractArray{<:Any, 3}, element,
137
jacobian_matrix)
138
# The code below is equivalent to the following high-level code but much faster.
139
# inverse_jacobian[i, j, element] = inv(det(jacobian_matrix[:, :, i, j, element])
140
141
@turbo for j in indices((inverse_jacobian, jacobian_matrix), (2, 4)),
142
i in indices((inverse_jacobian, jacobian_matrix), (1, 3))
143
144
inverse_jacobian[i, j, element] = inv(jacobian_matrix[1, 1, i, j, element] *
145
jacobian_matrix[2, 2, i, j, element] -
146
jacobian_matrix[1, 2, i, j, element] *
147
jacobian_matrix[2, 1, i, j, element])
148
end
149
150
return inverse_jacobian
151
end
152
153
# Save id of left neighbor of every element
154
function initialize_left_neighbor_connectivity!(left_neighbors,
155
mesh::Union{StructuredMesh{2},
156
StructuredMeshView{2}},
157
linear_indices)
158
# Neighbors in x-direction
159
for cell_y in 1:size(mesh, 2)
160
# Inner elements
161
for cell_x in 2:size(mesh, 1)
162
element = linear_indices[cell_x, cell_y]
163
left_neighbors[1, element] = linear_indices[cell_x - 1, cell_y]
164
end
165
166
if isperiodic(mesh, 1)
167
# Periodic boundary
168
left_neighbors[1, linear_indices[1, cell_y]] = linear_indices[end, cell_y]
169
else
170
# Use boundary conditions
171
left_neighbors[1, linear_indices[1, cell_y]] = 0
172
end
173
end
174
175
# Neighbors in y-direction
176
for cell_x in 1:size(mesh, 1)
177
# Inner elements
178
for cell_y in 2:size(mesh, 2)
179
element = linear_indices[cell_x, cell_y]
180
left_neighbors[2, element] = linear_indices[cell_x, cell_y - 1]
181
end
182
183
if isperiodic(mesh, 2)
184
# Periodic boundary
185
left_neighbors[2, linear_indices[cell_x, 1]] = linear_indices[cell_x, end]
186
else
187
# Use boundary conditions
188
left_neighbors[2, linear_indices[cell_x, 1]] = 0
189
end
190
end
191
192
return left_neighbors
193
end
194
195
# Compute the normal vectors for freestream-preserving FV method on curvilinear subcells, see
196
# equations (14) and (B.53) in:
197
# - Hennemann, Rueda-Ramírez, Hindenlang, Gassner (2020)
198
# A provably entropy stable subcell shock capturing approach for high order split form DG for the compressible Euler equations
199
# [arXiv: 2008.12044v2](https://arxiv.org/pdf/2008.12044)
200
function calc_normalvectors_subcell_fv!(normal_vectors_1, normal_vectors_2,
201
mesh::Union{StructuredMesh{2},
202
UnstructuredMesh2D,
203
P4estMesh{2}, T8codeMesh{2}},
204
dg, cache_containers)
205
@unpack contravariant_vectors = cache_containers.elements
206
@unpack weights, derivative_matrix = dg.basis
207
208
@threaded for element in eachelement(dg, cache_containers)
209
# First contravariant vector/direction
210
for j in eachnode(dg)
211
# We do not store i = 1, as it is never used, see `calcflux_fv!`.
212
# => Store i = 2 at position 1
213
@views normal_vectors_1[:, 1, j, element] .= get_contravariant_vector(1,
214
contravariant_vectors,
215
1,
216
j,
217
element)
218
for m in eachnode(dg)
219
wD_im = weights[1] * derivative_matrix[1, m]
220
@views normal_vectors_1[:, 1, j, element] .+= wD_im *
221
get_contravariant_vector(1,
222
contravariant_vectors,
223
m,
224
j,
225
element)
226
end
227
228
for i in 2:(nnodes(dg) - 1) # Actual indices: 3 to nnodes(dg)
229
@views normal_vectors_1[:, i, j, element] .= normal_vectors_1[:,
230
i - 1,
231
j,
232
element]
233
for m in eachnode(dg)
234
wD_im = weights[i] * derivative_matrix[i, m]
235
@views normal_vectors_1[:, i, j, element] .+= wD_im *
236
get_contravariant_vector(1,
237
contravariant_vectors,
238
m,
239
j,
240
element)
241
end
242
end
243
end
244
245
# Second contravariant vector/direction
246
for i in eachnode(dg)
247
# We do not store j = 1, as it is never used.
248
# => Store physical j = 2 at position 1
249
@views normal_vectors_2[:, i, 1, element] .= get_contravariant_vector(2,
250
contravariant_vectors,
251
i,
252
1,
253
element)
254
for m in eachnode(dg)
255
wD_jm = weights[1] * derivative_matrix[1, m]
256
@views normal_vectors_2[:, i, 1, element] .+= wD_jm *
257
get_contravariant_vector(2,
258
contravariant_vectors,
259
i,
260
m,
261
element)
262
end
263
264
for j in 2:(nnodes(dg) - 1) # Actual indices: 3 to nnodes(dg)
265
@views normal_vectors_2[:, i, j, element] .= normal_vectors_2[:,
266
i,
267
j - 1,
268
element]
269
270
for m in eachnode(dg)
271
wD_jm = weights[j] * derivative_matrix[j, m]
272
@views normal_vectors_2[:, i, j, element] .+= wD_jm *
273
get_contravariant_vector(2,
274
contravariant_vectors,
275
i,
276
m,
277
element)
278
end
279
end
280
end
281
end
282
283
return nothing
284
end
285
286
# Used for both fixed (`StructuredMesh{2}` or `UnstructuredMesh2D`)
287
# and adaptive meshes (`P4estMesh{2}` or `T8codeMesh{2}`)
288
mutable struct NormalVectorContainer2D{RealT <: Real} <:
289
AbstractNormalVectorContainer
290
const n_nodes::Int
291
# For normal vectors computed from first contravariant vectors
292
normal_vectors_1::Array{RealT, 4} # [NDIMS, NNODES - 1, NNODES, NELEMENTS]
293
# For normal vectors computed from second contravariant vectors
294
normal_vectors_2::Array{RealT, 4} # [NDIMS, NNODES, NNODES - 1, NELEMENTS]
295
296
# internal `resize!`able storage
297
_normal_vectors_1::Vector{RealT}
298
_normal_vectors_2::Vector{RealT}
299
end
300
301
function NormalVectorContainer2D(mesh::Union{StructuredMesh{2}, UnstructuredMesh2D,
302
P4estMesh{2}, T8codeMesh{2}},
303
dg, cache_containers)
304
@unpack contravariant_vectors = cache_containers.elements
305
RealT = eltype(contravariant_vectors)
306
n_elements = nelements(dg, cache_containers)
307
n_nodes = nnodes(dg.basis)
308
309
_normal_vectors_1 = Vector{RealT}(undef, 2 * (n_nodes - 1) * n_nodes * n_elements)
310
normal_vectors_1 = unsafe_wrap(Array, pointer(_normal_vectors_1),
311
(2, n_nodes - 1, n_nodes,
312
n_elements))
313
314
_normal_vectors_2 = Vector{RealT}(undef, 2 * n_nodes * (n_nodes - 1) * n_elements)
315
normal_vectors_2 = unsafe_wrap(Array, pointer(_normal_vectors_2),
316
(2, n_nodes, n_nodes - 1,
317
n_elements))
318
319
calc_normalvectors_subcell_fv!(normal_vectors_1, normal_vectors_2,
320
mesh, dg, cache_containers)
321
322
return NormalVectorContainer2D{RealT}(n_nodes,
323
normal_vectors_1, normal_vectors_2,
324
_normal_vectors_1, _normal_vectors_2)
325
end
326
327
# For `TreeMesh`, no subcell normal vectors need to be computed
328
resize_normal_vectors!(cache, mesh::TreeMesh, capacity) = nothing
329
# It suffices to specialize on the non-Cartesian mesh types with AMR only
330
function resize_normal_vectors!(cache, mesh::Union{P4estMesh, T8codeMesh}, capacity)
331
resize!(cache.normal_vectors, capacity)
332
333
return nothing
334
end
335
336
# For `TreeMesh`, no subcell normal vectors need to be computed
337
reinit_normal_vectors!(cache, mesh::TreeMesh, dg) = nothing
338
# It suffices to specialize on the non-Cartesian mesh types with AMR only
339
function reinit_normal_vectors!(cache, mesh::Union{P4estMesh, T8codeMesh}, dg)
340
init_normal_vectors!(cache.normal_vectors, mesh, dg, cache)
341
342
return nothing
343
end
344
345
# Required only for adaptive meshes (`P4estMesh` or `T8codeMesh`)
346
function Base.resize!(normal_vectors::NormalVectorContainer2D, capacity)
347
@unpack n_nodes, _normal_vectors_1, _normal_vectors_2 = normal_vectors
348
ArrayType = storage_type(normal_vectors)
349
350
resize!(_normal_vectors_1, 2 * (n_nodes - 1) * n_nodes * capacity)
351
normal_vectors.normal_vectors_1 = unsafe_wrap_or_alloc(ArrayType, _normal_vectors_1,
352
(2,
353
n_nodes - 1, n_nodes,
354
capacity))
355
356
resize!(_normal_vectors_2, 2 * n_nodes * (n_nodes - 1) * capacity)
357
normal_vectors.normal_vectors_2 = unsafe_wrap_or_alloc(ArrayType, _normal_vectors_2,
358
(2,
359
n_nodes, n_nodes - 1,
360
capacity))
361
362
return nothing
363
end
364
365
# Required only for adaptive meshes (`P4estMesh` or `T8codeMesh`)
366
function init_normal_vectors!(normal_vectors::NormalVectorContainer2D,
367
mesh::Union{P4estMesh{2}, T8codeMesh{2}}, dg, cache)
368
@unpack normal_vectors_1, normal_vectors_2 = normal_vectors
369
calc_normalvectors_subcell_fv!(normal_vectors_1, normal_vectors_2,
370
mesh, dg, cache)
371
372
return nothing
373
end
374
end # @muladd
375
376