Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/meshes/p4est_mesh_view.jl
5586 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
"""
9
P4estMeshView{NDIMS, NDIMS_AMBIENT, RealT <: Real, Parent} <: AbstractMesh{NDIMS}
10
11
A view on a [`P4estMesh`](@ref).
12
"""
13
mutable struct P4estMeshView{NDIMS, NDIMS_AMBIENT, RealT <: Real, Parent} <:
14
AbstractMesh{NDIMS}
15
const parent::Parent
16
const cell_ids::Vector{Int}
17
unsaved_changes::Bool
18
current_filename::String
19
end
20
21
"""
22
P4estMeshView(parent; cell_ids)
23
24
Create a `P4estMeshView` on a [`P4estMesh`](@ref) parent.
25
26
# Arguments
27
- `parent`: the parent `P4estMesh`.
28
- `cell_ids`: array of cell ids that are part of this view.
29
"""
30
function P4estMeshView(parent::P4estMesh{NDIMS, NDIMS_AMBIENT, RealT},
31
cell_ids::Vector) where {NDIMS, NDIMS_AMBIENT, RealT}
32
return P4estMeshView{NDIMS, NDIMS_AMBIENT, RealT, typeof(parent)}(parent, cell_ids,
33
parent.unsaved_changes,
34
parent.current_filename)
35
end
36
37
@inline Base.ndims(::P4estMeshView{NDIMS}) where {NDIMS} = NDIMS
38
@inline Base.real(::P4estMeshView{NDIMS, NDIMS_AMBIENT, RealT}) where {NDIMS,
39
NDIMS_AMBIENT,
40
RealT} = RealT
41
42
# Extract interfaces, boundaries and parent element ids from the neighbors.
43
function extract_p4est_mesh_view(elements_parent,
44
interfaces_parent,
45
boundaries_parent,
46
mortars_parent,
47
mesh,
48
equations,
49
dg,
50
::Type{uEltype}) where {uEltype <: Real}
51
# Create deepcopy to get completely independent elements container
52
elements = deepcopy(elements_parent)
53
resize!(elements, length(mesh.cell_ids))
54
55
# Copy relevant entries from parent mesh
56
@views elements.inverse_jacobian .= elements_parent.inverse_jacobian[..,
57
mesh.cell_ids]
58
@views elements.jacobian_matrix .= elements_parent.jacobian_matrix[..,
59
mesh.cell_ids]
60
@views elements.node_coordinates .= elements_parent.node_coordinates[..,
61
mesh.cell_ids]
62
@views elements.contravariant_vectors .= elements_parent.contravariant_vectors[..,
63
mesh.cell_ids]
64
@views elements.surface_flux_values .= elements_parent.surface_flux_values[..,
65
mesh.cell_ids]
66
# Extract interfaces that belong to mesh view.
67
interfaces = extract_interfaces(mesh, interfaces_parent)
68
69
# Extract boundaries of this mesh view.
70
boundaries = extract_boundaries(mesh, boundaries_parent, interfaces_parent,
71
interfaces)
72
73
# Get the parent element ids of the neighbors.
74
neighbor_ids_parent = extract_neighbor_ids_parent(mesh, boundaries_parent,
75
interfaces_parent,
76
boundaries)
77
78
return elements, interfaces, boundaries, mortars_parent, neighbor_ids_parent
79
end
80
81
# Remove all interfaces that have a tuple of neighbor_ids where at least one is
82
# not part of this mesh view, i.e. mesh.cell_ids, and return the new interface container.
83
function extract_interfaces(mesh::P4estMeshView, interfaces_parent)
84
# Identify interfaces that need to be retained
85
mask = BitArray(undef, ninterfaces(interfaces_parent))
86
# Loop over all interfaces (index 2).
87
for interface in 1:size(interfaces_parent.neighbor_ids)[2]
88
mask[interface] = (interfaces_parent.neighbor_ids[1,
89
interface] in mesh.cell_ids) &&
90
(interfaces_parent.neighbor_ids[2,
91
interface] in mesh.cell_ids)
92
end
93
94
# Create deepcopy to get completely independent interfaces container
95
interfaces = deepcopy(interfaces_parent)
96
resize!(interfaces, sum(mask))
97
98
# Copy relevant entries from parent mesh
99
@views interfaces.u .= interfaces_parent.u[.., mask]
100
if interfaces.normal_directions !== nothing # Needed for Gauss-Legendre basis
101
@views interfaces.normal_directions .= interfaces_parent.normal_directions[..,
102
mask]
103
end
104
@views interfaces.node_indices .= interfaces_parent.node_indices[.., mask]
105
@views neighbor_ids = interfaces_parent.neighbor_ids[.., mask]
106
107
# Transform the parent indices into view indices.
108
interfaces.neighbor_ids = zeros(Int, size(neighbor_ids))
109
for interface in 1:size(neighbor_ids)[2]
110
interfaces.neighbor_ids[1, interface] = findall(id -> id ==
111
neighbor_ids[1,
112
interface],
113
mesh.cell_ids)[1]
114
interfaces.neighbor_ids[2, interface] = findall(id -> id ==
115
neighbor_ids[2,
116
interface],
117
mesh.cell_ids)[1]
118
end
119
120
return interfaces
121
end
122
123
# Remove all boundaries that are not part of this p4est mesh view and add new boundaries
124
# that were interfaces of the parent mesh.
125
function extract_boundaries(mesh::P4estMeshView{2},
126
boundaries_parent, interfaces_parent,
127
interfaces)
128
# Remove all boundaries that are not part of this p4est mesh view.
129
boundaries = deepcopy(boundaries_parent)
130
mask = BitArray(undef, nboundaries(boundaries_parent))
131
for boundary in 1:nboundaries(boundaries_parent)
132
mask[boundary] = boundaries_parent.neighbor_ids[boundary] in mesh.cell_ids
133
end
134
boundaries.neighbor_ids = parent_cell_id_to_view(boundaries_parent.neighbor_ids[mask],
135
mesh)
136
boundaries.name = boundaries_parent.name[mask]
137
boundaries.node_indices = boundaries_parent.node_indices[mask]
138
139
# Add new boundaries that were interfaces of the parent mesh.
140
# Loop over all interfaces (index 2).
141
for interface in 1:ninterfaces(interfaces_parent)
142
# Create new boundary if exactly one of the neighbor cells is in the mesh view ("exclusive or" with ⊻)
143
if ((interfaces_parent.neighbor_ids[1, interface] in mesh.cell_ids)
144
(interfaces_parent.neighbor_ids[2, interface] in mesh.cell_ids))
145
# Determine which of the ids is part of the mesh view.
146
if interfaces_parent.neighbor_ids[1, interface] in mesh.cell_ids
147
neighbor_id = interfaces_parent.neighbor_ids[1, interface]
148
view_idx = 1
149
else
150
neighbor_id = interfaces_parent.neighbor_ids[2, interface]
151
view_idx = 2
152
end
153
154
# Update the neighbor ids.
155
push!(boundaries.neighbor_ids,
156
parent_cell_id_to_view(neighbor_id, mesh))
157
# Update the boundary names to reflect where the neighboring cell is
158
# relative to this one, i.e. left, right, up, down.
159
# In 3d one would need to add the third dimension.
160
if (interfaces_parent.node_indices[view_idx, interface] ==
161
(:end, :i_forward))
162
push!(boundaries.name, :x_pos)
163
elseif (interfaces_parent.node_indices[view_idx, interface] ==
164
(:begin, :i_forward))
165
push!(boundaries.name, :x_neg)
166
elseif (interfaces_parent.node_indices[view_idx, interface] ==
167
(:i_forward, :end))
168
push!(boundaries.name, :y_pos)
169
else
170
push!(boundaries.name, :y_neg)
171
end
172
173
# Update the node indices.
174
push!(boundaries.node_indices,
175
interfaces_parent.node_indices[view_idx, interface])
176
end
177
end
178
179
# Create the boundary vector for u, which will be populated later.
180
n_dims = ndims(boundaries)
181
n_nodes = size(boundaries.u, 2)
182
n_variables = size(boundaries.u, 1)
183
capacity = length(boundaries.neighbor_ids)
184
185
resize!(boundaries._u, n_variables * n_nodes^(n_dims - 1) * capacity)
186
boundaries.u = unsafe_wrap(Array, pointer(boundaries._u),
187
(n_variables, ntuple(_ -> n_nodes, n_dims - 1)...,
188
capacity))
189
190
return boundaries
191
end
192
193
# Extract the ids of the neighboring elements using the parent mesh indexing.
194
# For every boundary of the mesh view find the neighboring cell id in global (parent) indexing.
195
# Such neighboring cells are either inside the domain and have an interface
196
# in the parent mesh, or they are physical boundaries for which we then
197
# construct a periodic coupling by assigning as neighbor id the cell id
198
# on the other end of the domain.
199
function extract_neighbor_ids_parent(mesh::P4estMeshView,
200
boundaries_parent, interfaces_parent,
201
boundaries)
202
# Determine the parent indices of the neighboring elements.
203
neighbor_ids_parent = similar(boundaries.neighbor_ids)
204
for (idx, id) in enumerate(boundaries.neighbor_ids)
205
parent_id = mesh.cell_ids[id]
206
# Find this id in the parent's interfaces.
207
for interface in eachindex(interfaces_parent.neighbor_ids[1, :])
208
if (parent_id == interfaces_parent.neighbor_ids[1, interface] ||
209
parent_id == interfaces_parent.neighbor_ids[2, interface])
210
if parent_id == interfaces_parent.neighbor_ids[1, interface]
211
matching_boundary = 1
212
else
213
matching_boundary = 2
214
end
215
# Check if interfaces with this id have the right name/node_indices.
216
if (boundaries.name[idx] ==
217
node_indices_to_name(interfaces_parent.node_indices[matching_boundary,
218
interface]))
219
if parent_id == interfaces_parent.neighbor_ids[1, interface]
220
neighbor_ids_parent[idx] = interfaces_parent.neighbor_ids[2,
221
interface]
222
else
223
neighbor_ids_parent[idx] = interfaces_parent.neighbor_ids[1,
224
interface]
225
end
226
end
227
end
228
end
229
230
# Find this id in the parent's boundaries.
231
parent_xneg_cell_ids = boundaries_parent.neighbor_ids[boundaries_parent.name .== :x_neg]
232
parent_xpos_cell_ids = boundaries_parent.neighbor_ids[boundaries_parent.name .== :x_pos]
233
parent_yneg_cell_ids = boundaries_parent.neighbor_ids[boundaries_parent.name .== :y_neg]
234
parent_ypos_cell_ids = boundaries_parent.neighbor_ids[boundaries_parent.name .== :y_pos]
235
for (parent_idx, boundary) in enumerate(boundaries_parent.neighbor_ids)
236
if parent_id == boundary
237
# Check if boundaries with this id have the right name/node_indices.
238
if boundaries.name[idx] == boundaries_parent.name[parent_idx]
239
# Make the coupling periodic.
240
if boundaries_parent.name[parent_idx] == :x_neg
241
neighbor_ids_parent[idx] = parent_xpos_cell_ids[findfirst(parent_xneg_cell_ids .==
242
boundary)]
243
elseif boundaries_parent.name[parent_idx] == :x_pos
244
neighbor_ids_parent[idx] = parent_xneg_cell_ids[findfirst(parent_xpos_cell_ids .==
245
boundary)]
246
elseif boundaries_parent.name[parent_idx] == :y_neg
247
neighbor_ids_parent[idx] = parent_ypos_cell_ids[findfirst(parent_yneg_cell_ids .==
248
boundary)]
249
elseif boundaries_parent.name[parent_idx] == :y_pos
250
neighbor_ids_parent[idx] = parent_yneg_cell_ids[findfirst(parent_ypos_cell_ids .==
251
boundary)]
252
else
253
error("Unknown boundary name: $(boundaries_parent.name[parent_idx])")
254
end
255
end
256
end
257
end
258
end
259
260
return neighbor_ids_parent
261
end
262
263
# Translate the interface indices into boundary names.
264
# This works only in 2d currently.
265
function node_indices_to_name(node_index)
266
if node_index == (:end, :i_forward)
267
return :x_pos
268
elseif node_index == (:begin, :i_forward)
269
return :x_neg
270
elseif node_index == (:i_forward, :end)
271
return :y_pos
272
elseif node_index == (:i_forward, :begin)
273
return :y_neg
274
else
275
error("Unknown node index: $node_index")
276
end
277
end
278
279
# Convert a parent cell id to a view cell id in the mesh view.
280
function parent_cell_id_to_view(id::Integer, mesh::P4estMeshView)
281
# Find the index of the cell id in the mesh view
282
view_id = searchsortedfirst(mesh.cell_ids, id)
283
284
return view_id
285
end
286
287
# Convert an array of parent cell ids to view cell ids in the mesh view.
288
function parent_cell_id_to_view(ids::AbstractArray, mesh::P4estMeshView)
289
# Find the index of the cell id in the mesh view
290
view_id = zeros(Int, length(ids))
291
for i in eachindex(ids)
292
view_id[i] = parent_cell_id_to_view(ids[i], mesh)
293
end
294
return view_id
295
end
296
297
# Does not save the mesh itself to an HDF5 file. Instead saves important attributes
298
# of the mesh, like its size and the type of boundary mapping function.
299
# Then, within Trixi2Vtk, the P4estMeshView and its node coordinates are reconstructured from
300
# these attributes for plotting purposes
301
# | Warning: This overwrites any existing mesh file, either for a mesh view or parent mesh.
302
function save_mesh_file(mesh::P4estMeshView, output_directory; system = "",
303
timestep = 0)
304
# Create output directory (if it does not exist)
305
mkpath(output_directory)
306
307
filename = joinpath(output_directory, "mesh.h5")
308
p4est_filename = "p4est_data"
309
p4est_file = joinpath(output_directory, p4est_filename)
310
311
# Save the complete connectivity and `p4est` data to disk.
312
save_p4est!(p4est_file, mesh.parent.p4est)
313
314
# Open file (clobber existing content)
315
h5open(filename, "w") do file
316
# Add context information as attributes
317
attributes(file)["mesh_type"] = get_name(mesh)
318
attributes(file)["ndims"] = ndims(mesh)
319
attributes(file)["p4est_file"] = p4est_filename
320
attributes(file)["cell_ids"] = mesh.cell_ids
321
322
file["tree_node_coordinates"] = mesh.parent.tree_node_coordinates
323
file["nodes"] = Vector(mesh.parent.nodes) # the mesh uses `SVector`s for the nodes
324
# to increase the runtime performance
325
# but HDF5 can only handle plain arrays
326
file["boundary_names"] = mesh.parent.boundary_names .|> String
327
return nothing
328
end
329
330
return filename
331
end
332
333
# Interpolate tree_node_coordinates to each quadrant at the specified nodes
334
# Note: This is a copy of the corresponding function in src/solvers/dgsem_p4est/containers_2d.jl,
335
# with modifications to skip cells not part of the mesh view
336
function calc_node_coordinates!(node_coordinates,
337
mesh::P4estMeshView{2, NDIMS_AMBIENT},
338
nodes::AbstractVector) where {NDIMS_AMBIENT}
339
# We use `StrideArray`s here since these buffers are used in performance-critical
340
# places and the additional information passed to the compiler makes them faster
341
# than native `Array`s.
342
tmp1 = StrideArray(undef, real(mesh),
343
StaticInt(NDIMS_AMBIENT), static_length(nodes),
344
static_length(mesh.parent.nodes))
345
matrix1 = StrideArray(undef, real(mesh),
346
static_length(nodes), static_length(mesh.parent.nodes))
347
matrix2 = similar(matrix1)
348
baryweights_in = barycentric_weights(mesh.parent.nodes)
349
350
# Macros from `p4est`
351
p4est_root_len = 1 << P4EST_MAXLEVEL
352
p4est_quadrant_len(l) = 1 << (P4EST_MAXLEVEL - l)
353
354
trees = unsafe_wrap_sc(p4est_tree_t, mesh.parent.p4est.trees)
355
356
mesh_view_cell_id = 0
357
for tree_id in eachindex(trees)
358
tree_offset = trees[tree_id].quadrants_offset
359
quadrants = unsafe_wrap_sc(p4est_quadrant_t, trees[tree_id].quadrants)
360
361
for i in eachindex(quadrants)
362
parent_mesh_cell_id = tree_offset + i
363
if !(parent_mesh_cell_id in mesh.cell_ids)
364
# This cell is not part of the mesh view, thus skip it
365
continue
366
end
367
mesh_view_cell_id += 1
368
369
quad = quadrants[i]
370
371
quad_length = p4est_quadrant_len(quad.level) / p4est_root_len
372
373
nodes_out_x = 2 * (quad_length * 1 / 2 * (nodes .+ 1) .+
374
quad.x / p4est_root_len) .- 1
375
nodes_out_y = 2 * (quad_length * 1 / 2 * (nodes .+ 1) .+
376
quad.y / p4est_root_len) .- 1
377
polynomial_interpolation_matrix!(matrix1, mesh.parent.nodes, nodes_out_x,
378
baryweights_in)
379
polynomial_interpolation_matrix!(matrix2, mesh.parent.nodes, nodes_out_y,
380
baryweights_in)
381
382
multiply_dimensionwise!(view(node_coordinates, :, :, :, mesh_view_cell_id),
383
matrix1, matrix2,
384
view(mesh.parent.tree_node_coordinates, :, :, :,
385
tree_id),
386
tmp1)
387
end
388
end
389
390
return node_coordinates
391
end
392
393
@inline mpi_parallel(mesh::P4estMeshView) = False()
394
end # @muladd
395
396