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_2d.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,
10
mesh::Union{P4estMesh{2}, P4estMeshView{2}, T8codeMesh{2}},
11
basis::AbstractBasisSBP)
12
@unpack node_coordinates, jacobian_matrix,
13
contravariant_vectors, inverse_jacobian = elements
14
15
calc_node_coordinates!(node_coordinates, mesh, basis)
16
17
for element in 1:ncells(mesh)
18
calc_jacobian_matrix!(jacobian_matrix, element, node_coordinates, basis)
19
20
calc_contravariant_vectors!(contravariant_vectors, element, jacobian_matrix)
21
22
calc_inverse_jacobian!(inverse_jacobian, element, jacobian_matrix)
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{2}, P4estMeshView{2},
31
T8codeMesh{2}},
32
basis::AbstractBasisSBP)
33
# Hanging nodes will cause holes in the mesh if its polydeg is higher
34
# than the polydeg of the solver.
35
@assert length(basis.nodes)>=length(mesh.nodes) "The solver can't have a lower polydeg than the mesh"
36
37
return calc_node_coordinates!(node_coordinates, mesh, basis.nodes)
38
end
39
40
# Interpolate tree_node_coordinates to each quadrant at the specified nodes
41
function calc_node_coordinates!(node_coordinates,
42
mesh::P4estMesh{2, NDIMS_AMBIENT},
43
nodes::AbstractVector) where {NDIMS_AMBIENT}
44
# We use `StrideArray`s here since these buffers are used in performance-critical
45
# places and the additional information passed to the compiler makes them faster
46
# than native `Array`s.
47
tmp1 = StrideArray(undef, real(mesh),
48
StaticInt(NDIMS_AMBIENT), static_length(nodes),
49
static_length(mesh.nodes))
50
matrix1 = StrideArray(undef, real(mesh),
51
static_length(nodes), static_length(mesh.nodes))
52
matrix2 = similar(matrix1)
53
baryweights_in = barycentric_weights(mesh.nodes)
54
55
# Macros from `p4est`
56
p4est_root_len = 1 << P4EST_MAXLEVEL
57
p4est_quadrant_len(l) = 1 << (P4EST_MAXLEVEL - l)
58
59
trees = unsafe_wrap_sc(p4est_tree_t, mesh.p4est.trees)
60
61
for tree in eachindex(trees)
62
offset = trees[tree].quadrants_offset
63
quadrants = unsafe_wrap_sc(p4est_quadrant_t, trees[tree].quadrants)
64
65
for i in eachindex(quadrants)
66
element = offset + i
67
quad = quadrants[i]
68
69
quad_length = p4est_quadrant_len(quad.level) / p4est_root_len
70
71
nodes_out_x = 2 * (quad_length * 1 / 2 * (nodes .+ 1) .+
72
quad.x / p4est_root_len) .- 1
73
nodes_out_y = 2 * (quad_length * 1 / 2 * (nodes .+ 1) .+
74
quad.y / p4est_root_len) .- 1
75
polynomial_interpolation_matrix!(matrix1, mesh.nodes, nodes_out_x,
76
baryweights_in)
77
polynomial_interpolation_matrix!(matrix2, mesh.nodes, nodes_out_y,
78
baryweights_in)
79
80
multiply_dimensionwise!(view(node_coordinates, :, :, :, element),
81
matrix1, matrix2,
82
view(mesh.tree_node_coordinates, :, :, :, tree),
83
tmp1)
84
end
85
end
86
87
return node_coordinates
88
end
89
90
# For Gauss-Lobatto-Legendre (GLL) nodes, interface normals are computed on-the-fly
91
# during flux evaluation and do not need dedicated storage in the interface container.
92
function init_normal_directions!(interfaces::P4estInterfaceContainer{2},
93
basis::LobattoLegendreBasis, elements)
94
return nothing
95
end
96
97
# For Gauss-Legendre (GL) nodes, the interface normals are
98
# computed from interpolation of the volume node normals to the surface nodes.
99
function init_normal_directions!(interfaces::P4estInterfaceContainer{2},
100
basis::GaussLegendreBasis, elements)
101
@unpack neighbor_ids, node_indices, normal_directions = interfaces
102
@unpack contravariant_vectors = elements
103
@unpack boundary_interpolation = basis
104
index_range = eachnode(basis)
105
106
for interface in axes(neighbor_ids, 2)
107
primary_element = neighbor_ids[1, interface]
108
primary_indices = node_indices[1, interface]
109
primary_direction = indices2direction(primary_indices)
110
111
i_primary_start, i_primary_step = index_to_start_step_2d(primary_indices[1],
112
index_range)
113
j_primary_start, j_primary_step = index_to_start_step_2d(primary_indices[2],
114
index_range)
115
116
# The index direction is identified based on `{i,j}_{primary, secondary}_step`.
117
# For step = 0, the direction identified by this index is normal to the face.
118
# For step != 0 (1 or -1), the direction identified by this index is tangential to the face.
119
120
i_primary = i_primary_start
121
j_primary = j_primary_start
122
123
if i_primary_step == 0
124
# i is the normal direction (constant), j varies along the surface
125
# => Interpolate in first/normal direction
126
# Interpolation side is governed by element orientation
127
side = interpolation_side(primary_indices[1])
128
for i in index_range
129
normal_1 = zero(eltype(normal_directions))
130
normal_2 = zero(eltype(normal_directions))
131
for ii in index_range
132
factor = boundary_interpolation[ii, side]
133
# Retrieve normal directions at element/volume nodes
134
normal_direction = get_normal_direction(primary_direction,
135
contravariant_vectors,
136
ii, j_primary,
137
primary_element)
138
normal_1 = normal_1 + normal_direction[1] * factor
139
normal_2 = normal_2 + normal_direction[2] * factor
140
end
141
normal_directions[1, i, interface] = normal_1
142
normal_directions[2, i, interface] = normal_2
143
j_primary += j_primary_step # incrementing j_primary suffices (i_primary_step = 0)
144
end
145
else # j_primary_step == 0
146
# j is the normal direction (constant), i varies along the surface
147
# => Interpolate in second/normal direction
148
# Interpolation side is governed by element orientation
149
side = interpolation_side(primary_indices[2])
150
for i in index_range
151
normal_1 = zero(eltype(normal_directions))
152
normal_2 = zero(eltype(normal_directions))
153
for jj in index_range
154
factor = boundary_interpolation[jj, side]
155
# Retrieve normal directions at element/volume nodes
156
normal_direction = get_normal_direction(primary_direction,
157
contravariant_vectors,
158
i_primary, jj,
159
primary_element)
160
normal_1 = normal_1 + normal_direction[1] * factor
161
normal_2 = normal_2 + normal_direction[2] * factor
162
end
163
normal_directions[1, i, interface] = normal_1
164
normal_directions[2, i, interface] = normal_2
165
i_primary += i_primary_step # incrementing i_primary suffices (j_primary_step = 0)
166
end
167
end
168
end
169
170
return nothing
171
end
172
173
# Initialize node_indices of interface container
174
@inline function init_interface_node_indices!(interfaces::P4estInterfaceContainer{2},
175
faces, orientation, interface_id)
176
# Iterate over primary and secondary element
177
for side in 1:2
178
# Align interface in positive coordinate direction of primary element.
179
# For orientation == 1, the secondary element needs to be indexed backwards
180
# relative to the interface.
181
if side == 1 || orientation == 0
182
# Forward indexing
183
i = :i_forward
184
else
185
# Backward indexing
186
i = :i_backward
187
end
188
189
if faces[side] == 0
190
# Index face in negative x-direction
191
interfaces.node_indices[side, interface_id] = (:begin, i)
192
elseif faces[side] == 1
193
# Index face in positive x-direction
194
interfaces.node_indices[side, interface_id] = (:end, i)
195
elseif faces[side] == 2
196
# Index face in negative y-direction
197
interfaces.node_indices[side, interface_id] = (i, :begin)
198
else # faces[side] == 3
199
# Index face in positive y-direction
200
interfaces.node_indices[side, interface_id] = (i, :end)
201
end
202
end
203
204
return interfaces
205
end
206
207
# Initialize node_indices of boundary container
208
@inline function init_boundary_node_indices!(boundaries::P4estBoundaryContainer{2},
209
face, boundary_id)
210
if face == 0
211
# Index face in negative x-direction
212
boundaries.node_indices[boundary_id] = (:begin, :i_forward)
213
elseif face == 1
214
# Index face in positive x-direction
215
boundaries.node_indices[boundary_id] = (:end, :i_forward)
216
elseif face == 2
217
# Index face in negative y-direction
218
boundaries.node_indices[boundary_id] = (:i_forward, :begin)
219
else # face == 3
220
# Index face in positive y-direction
221
boundaries.node_indices[boundary_id] = (:i_forward, :end)
222
end
223
224
return boundaries
225
end
226
227
# Initialize node_indices of mortar container
228
# faces[1] is expected to be the face of the small side.
229
@inline function init_mortar_node_indices!(mortars, faces, orientation, mortar_id)
230
for side in 1:2
231
# Align mortar in positive coordinate direction of small side.
232
# For orientation == 1, the large side needs to be indexed backwards
233
# relative to the mortar.
234
if side == 1 || orientation == 0
235
# Forward indexing for small side or orientation == 0
236
i = :i_forward
237
else
238
# Backward indexing for large side with reversed orientation
239
i = :i_backward
240
end
241
242
if faces[side] == 0
243
# Index face in negative x-direction
244
mortars.node_indices[side, mortar_id] = (:begin, i)
245
elseif faces[side] == 1
246
# Index face in positive x-direction
247
mortars.node_indices[side, mortar_id] = (:end, i)
248
elseif faces[side] == 2
249
# Index face in negative y-direction
250
mortars.node_indices[side, mortar_id] = (i, :begin)
251
else # faces[side] == 3
252
# Index face in positive y-direction
253
mortars.node_indices[side, mortar_id] = (i, :end)
254
end
255
end
256
257
return mortars
258
end
259
end # @muladd
260
261