Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/solvers/dgsem_p4est/containers_3d.jl
5616 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{P4estMesh{3}, T8codeMesh{3}},
10
basis::LobattoLegendreBasis)
11
@unpack node_coordinates, jacobian_matrix,
12
contravariant_vectors, inverse_jacobian = elements
13
14
calc_node_coordinates!(node_coordinates, mesh, basis)
15
16
for element in 1:ncells(mesh)
17
calc_jacobian_matrix!(jacobian_matrix, element, node_coordinates, basis)
18
19
calc_contravariant_vectors!(contravariant_vectors, element, jacobian_matrix,
20
node_coordinates, basis)
21
22
calc_inverse_jacobian!(inverse_jacobian, element, jacobian_matrix, basis)
23
end
24
25
return nothing
26
end
27
28
# Interpolate tree_node_coordinates to each quadrant at the nodes of the specified basis
29
function calc_node_coordinates!(node_coordinates,
30
mesh::Union{P4estMesh{3}, T8codeMesh{3}},
31
basis::LobattoLegendreBasis)
32
# Hanging nodes will cause holes in the mesh if its polydeg is higher
33
# than the polydeg of the solver.
34
@assert length(basis.nodes)>=length(mesh.nodes) "The solver can't have a lower polydeg than the mesh"
35
36
return calc_node_coordinates!(node_coordinates, mesh, basis.nodes)
37
end
38
39
# Interpolate tree_node_coordinates to each quadrant at the specified nodes
40
function calc_node_coordinates!(node_coordinates,
41
mesh::P4estMesh{3},
42
nodes::AbstractVector)
43
# Macros from `p4est`
44
p4est_root_len = 1 << P4EST_MAXLEVEL
45
p4est_quadrant_len(l) = 1 << (P4EST_MAXLEVEL - l)
46
47
trees = unsafe_wrap_sc(p8est_tree_t, mesh.p4est.trees)
48
49
for tree in eachindex(trees)
50
offset = trees[tree].quadrants_offset
51
quadrants = unsafe_wrap_sc(p8est_quadrant_t, trees[tree].quadrants)
52
53
for i in eachindex(quadrants)
54
element = offset + i
55
quad = quadrants[i]
56
57
quad_length = p4est_quadrant_len(quad.level) / p4est_root_len
58
59
nodes_out_x = 2 * (quad_length * 1 / 2 * (nodes .+ 1) .+
60
quad.x / p4est_root_len) .- 1
61
nodes_out_y = 2 * (quad_length * 1 / 2 * (nodes .+ 1) .+
62
quad.y / p4est_root_len) .- 1
63
nodes_out_z = 2 * (quad_length * 1 / 2 * (nodes .+ 1) .+
64
quad.z / p4est_root_len) .- 1
65
66
matrix1 = polynomial_interpolation_matrix(mesh.nodes, nodes_out_x)
67
matrix2 = polynomial_interpolation_matrix(mesh.nodes, nodes_out_y)
68
matrix3 = polynomial_interpolation_matrix(mesh.nodes, nodes_out_z)
69
70
multiply_dimensionwise!(view(node_coordinates, :, :, :, :, element),
71
matrix1, matrix2, matrix3,
72
view(mesh.tree_node_coordinates, :, :, :, :, tree))
73
end
74
end
75
76
return node_coordinates
77
end
78
79
# Not yet implemented and needed for 3D
80
function init_normal_directions!(interfaces::P4estInterfaceContainer{3},
81
basis::LobattoLegendreBasis, elements)
82
return nothing
83
end
84
85
# Initialize node_indices of interface container
86
@inline function init_interface_node_indices!(interfaces::P4estInterfaceContainer{3},
87
faces, orientation, interface_id)
88
# Iterate over primary and secondary element
89
for side in 1:2
90
# Align interface at the primary element (primary element has surface indices (:i_forward, :j_forward)).
91
# The secondary element needs to be indexed differently.
92
if side == 1
93
surface_index1 = :i_forward
94
surface_index2 = :j_forward
95
else
96
surface_index1, surface_index2 = orientation_to_indices_p4est(faces[2],
97
faces[1],
98
orientation)
99
end
100
101
if faces[side] == 0
102
# Index face in negative x-direction
103
interfaces.node_indices[side, interface_id] = (:begin, surface_index1,
104
surface_index2)
105
elseif faces[side] == 1
106
# Index face in positive x-direction
107
interfaces.node_indices[side, interface_id] = (:end, surface_index1,
108
surface_index2)
109
elseif faces[side] == 2
110
# Index face in negative y-direction
111
interfaces.node_indices[side, interface_id] = (surface_index1, :begin,
112
surface_index2)
113
elseif faces[side] == 3
114
# Index face in positive y-direction
115
interfaces.node_indices[side, interface_id] = (surface_index1, :end,
116
surface_index2)
117
elseif faces[side] == 4
118
# Index face in negative z-direction
119
interfaces.node_indices[side, interface_id] = (surface_index1,
120
surface_index2, :begin)
121
else # faces[side] == 5
122
# Index face in positive z-direction
123
interfaces.node_indices[side, interface_id] = (surface_index1,
124
surface_index2, :end)
125
end
126
end
127
128
return interfaces
129
end
130
131
# Initialize node_indices of boundary container
132
@inline function init_boundary_node_indices!(boundaries::P4estBoundaryContainer{3},
133
face, boundary_id)
134
if face == 0
135
# Index face in negative x-direction
136
boundaries.node_indices[boundary_id] = (:begin, :i_forward, :j_forward)
137
elseif face == 1
138
# Index face in positive x-direction
139
boundaries.node_indices[boundary_id] = (:end, :i_forward, :j_forward)
140
elseif face == 2
141
# Index face in negative y-direction
142
boundaries.node_indices[boundary_id] = (:i_forward, :begin, :j_forward)
143
elseif face == 3
144
# Index face in positive y-direction
145
boundaries.node_indices[boundary_id] = (:i_forward, :end, :j_forward)
146
elseif face == 4
147
# Index face in negative z-direction
148
boundaries.node_indices[boundary_id] = (:i_forward, :j_forward, :begin)
149
else # face == 5
150
# Index face in positive z-direction
151
boundaries.node_indices[boundary_id] = (:i_forward, :j_forward, :end)
152
end
153
154
return boundaries
155
end
156
157
# Initialize node_indices of mortar container
158
# faces[1] is expected to be the face of the small side.
159
@inline function init_mortar_node_indices!(mortars::P4estMortarContainer{3},
160
faces, orientation, mortar_id)
161
for side in 1:2
162
# Align mortar at small side.
163
# The large side needs to be indexed differently.
164
if side == 1
165
surface_index1 = :i_forward
166
surface_index2 = :j_forward
167
else
168
surface_index1, surface_index2 = orientation_to_indices_p4est(faces[2],
169
faces[1],
170
orientation)
171
end
172
173
if faces[side] == 0
174
# Index face in negative x-direction
175
mortars.node_indices[side, mortar_id] = (:begin, surface_index1,
176
surface_index2)
177
elseif faces[side] == 1
178
# Index face in positive x-direction
179
mortars.node_indices[side, mortar_id] = (:end, surface_index1,
180
surface_index2)
181
elseif faces[side] == 2
182
# Index face in negative y-direction
183
mortars.node_indices[side, mortar_id] = (surface_index1, :begin,
184
surface_index2)
185
elseif faces[side] == 3
186
# Index face in positive y-direction
187
mortars.node_indices[side, mortar_id] = (surface_index1, :end,
188
surface_index2)
189
elseif faces[side] == 4
190
# Index face in negative z-direction
191
mortars.node_indices[side, mortar_id] = (surface_index1, surface_index2,
192
:begin)
193
else # faces[side] == 5
194
# Index face in positive z-direction
195
mortars.node_indices[side, mortar_id] = (surface_index1, surface_index2,
196
:end)
197
end
198
end
199
200
return mortars
201
end
202
203
# Convert `p4est` orientation code to node indices.
204
# Return node indices that index "my side" wrt "other side",
205
# i.e., i and j are indices of other side.
206
function orientation_to_indices_p4est(my_face, other_face, orientation_code)
207
# my_face and other_face are the face directions (zero-based)
208
# of "my side" and "other side" respectively.
209
# Face corner 0 of the face with the lower face direction connects to a corner of the other face.
210
# The number of this corner is the orientation code in `p4est`.
211
lower = my_face <= other_face
212
213
# x_pos, y_neg, and z_pos are the directions in which the face has right-handed coordinates
214
# when looked at from the outside.
215
my_right_handed = my_face in (1, 2, 5)
216
other_right_handed = other_face in (1, 2, 5)
217
218
# If both or none are right-handed when looked at from the outside, they will have different
219
# orientations when looked at from the same side of the interface.
220
flipped = my_right_handed == other_right_handed
221
222
# In the following illustrations, the face corner numbering of `p4est` is shown.
223
# ξ and η are the local coordinates of the respective face.
224
# We're looking at both faces from the same side of the interface, so that "other side"
225
# (in the illustrations on the left) has right-handed coordinates.
226
if !flipped
227
if orientation_code == 0
228
# Corner 0 of other side matches corner 0 of my side
229
# 2┌──────┐3 2┌──────┐3
230
# │ │ │ │
231
# │ │ │ │
232
# 0└──────┘1 0└──────┘1
233
# η η
234
# ↑ ↑
235
# │ │
236
# └───> ξ └───> ξ
237
surface_index1 = :i_forward
238
surface_index2 = :j_forward
239
elseif ((lower && orientation_code == 2) # Corner 0 of my side matches corner 2 of other side
240
||
241
(!lower && orientation_code == 1)) # Corner 0 of other side matches corner 1 of my side
242
# 2┌──────┐3 0┌──────┐2
243
# │ │ │ │
244
# │ │ │ │
245
# 0└──────┘1 1└──────┘3
246
# η ┌───> η
247
# ↑ │
248
# │ ↓
249
# └───> ξ ξ
250
surface_index1 = :j_backward
251
surface_index2 = :i_forward
252
elseif ((lower && orientation_code == 1) # Corner 0 of my side matches corner 1 of other side
253
||
254
(!lower && orientation_code == 2)) # Corner 0 of other side matches corner 2 of my side
255
# 2┌──────┐3 3┌──────┐1
256
# │ │ │ │
257
# │ │ │ │
258
# 0└──────┘1 2└──────┘0
259
# η ξ
260
# ↑ ↑
261
# │ │
262
# └───> ξ η <───┘
263
surface_index1 = :j_forward
264
surface_index2 = :i_backward
265
else # orientation_code == 3
266
# Corner 0 of my side matches corner 3 of other side and
267
# corner 0 of other side matches corner 3 of my side.
268
# 2┌──────┐3 1┌──────┐0
269
# │ │ │ │
270
# │ │ │ │
271
# 0└──────┘1 3└──────┘2
272
# η ξ <───┐
273
# ↑ │
274
# │ ↓
275
# └───> ξ η
276
surface_index1 = :i_backward
277
surface_index2 = :j_backward
278
end
279
else # flipped
280
if orientation_code == 0
281
# Corner 0 of other side matches corner 0 of my side
282
# 2┌──────┐3 1┌──────┐3
283
# │ │ │ │
284
# │ │ │ │
285
# 0└──────┘1 0└──────┘2
286
# η ξ
287
# ↑ ↑
288
# │ │
289
# └───> ξ └───> η
290
surface_index1 = :j_forward
291
surface_index2 = :i_forward
292
elseif orientation_code == 2
293
# Corner 0 of my side matches corner 2 of other side and
294
# corner 0 of other side matches corner 2 of my side.
295
# 2┌──────┐3 0┌──────┐1
296
# │ │ │ │
297
# │ │ │ │
298
# 0└──────┘1 2└──────┘3
299
# η ┌───> ξ
300
# ↑ │
301
# │ ↓
302
# └───> ξ η
303
surface_index1 = :i_forward
304
surface_index2 = :j_backward
305
elseif orientation_code == 1
306
# Corner 0 of my side matches corner 1 of other side and
307
# corner 0 of other side matches corner 1 of my side.
308
# 2┌──────┐3 3┌──────┐2
309
# │ │ │ │
310
# │ │ │ │
311
# 0└──────┘1 1└──────┘0
312
# η η
313
# ↑ ↑
314
# │ │
315
# └───> ξ ξ <───┘
316
surface_index1 = :i_backward
317
surface_index2 = :j_forward
318
else # orientation_code == 3
319
# Corner 0 of my side matches corner 3 of other side and
320
# corner 0 of other side matches corner 3 of my side.
321
# 2┌──────┐3 2┌──────┐0
322
# │ │ │ │
323
# │ │ │ │
324
# 0└──────┘1 3└──────┘1
325
# η η <───┐
326
# ↑ │
327
# │ ↓
328
# └───> ξ ξ
329
surface_index1 = :j_backward
330
surface_index2 = :i_backward
331
end
332
end
333
334
return surface_index1, surface_index2
335
end
336
end # @muladd
337
338