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
2055 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
parent::Parent
16
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, NDIMS_AMBIENT, RealT} = RealT
39
40
function extract_p4est_mesh_view(elements_parent,
41
interfaces_parent,
42
boundaries_parent,
43
mortars_parent,
44
mesh,
45
equations,
46
dg,
47
::Type{uEltype}) where {uEltype <: Real}
48
# Create deepcopy to get completely independent elements container
49
elements = deepcopy(elements_parent)
50
resize!(elements, length(mesh.cell_ids))
51
52
# Copy relevant entries from parent mesh
53
@views elements.inverse_jacobian .= elements_parent.inverse_jacobian[..,
54
mesh.cell_ids]
55
@views elements.jacobian_matrix .= elements_parent.jacobian_matrix[..,
56
mesh.cell_ids]
57
@views elements.node_coordinates .= elements_parent.node_coordinates[..,
58
mesh.cell_ids]
59
@views elements.contravariant_vectors .= elements_parent.contravariant_vectors[..,
60
mesh.cell_ids]
61
@views elements.surface_flux_values .= elements_parent.surface_flux_values[..,
62
mesh.cell_ids]
63
# Extract interfaces that belong to mesh view
64
interfaces = extract_interfaces(mesh, interfaces_parent)
65
66
return elements, interfaces, boundaries_parent, mortars_parent
67
end
68
69
# Remove all interfaces that have a tuple of neighbor_ids where at least one is
70
# not part of this meshview, i.e. mesh.cell_ids, and return the new interface container
71
function extract_interfaces(mesh::P4estMeshView, interfaces_parent)
72
# Identify interfaces that need to be retained
73
mask = BitArray(undef, ninterfaces(interfaces_parent))
74
for interface in 1:size(interfaces_parent.neighbor_ids)[2]
75
mask[interface] = (interfaces_parent.neighbor_ids[1, interface] in mesh.cell_ids) &&
76
(interfaces_parent.neighbor_ids[2, interface] in mesh.cell_ids)
77
end
78
79
# Create deepcopy to get completely independent interfaces container
80
interfaces = deepcopy(interfaces_parent)
81
resize!(interfaces, sum(mask))
82
83
# Copy relevant entries from parent mesh
84
@views interfaces.u .= interfaces_parent.u[.., mask]
85
@views interfaces.node_indices .= interfaces_parent.node_indices[.., mask]
86
@views neighbor_ids = interfaces_parent.neighbor_ids[.., mask]
87
88
# Transform the global (parent) indices into local (view) indices.
89
interfaces.neighbor_ids = zeros(Int, size(neighbor_ids))
90
for interface in 1:size(neighbor_ids)[2]
91
interfaces.neighbor_ids[1, interface] = findall(id -> id ==
92
neighbor_ids[1, interface],
93
mesh.cell_ids)[1]
94
interfaces.neighbor_ids[2, interface] = findall(id -> id ==
95
neighbor_ids[2, interface],
96
mesh.cell_ids)[1]
97
end
98
99
return interfaces
100
end
101
102
# Does not save the mesh itself to an HDF5 file. Instead saves important attributes
103
# of the mesh, like its size and the type of boundary mapping function.
104
# Then, within Trixi2Vtk, the P4estMeshView and its node coordinates are reconstructured from
105
# these attributes for plotting purposes
106
# | Warning: This overwrites any existing mesh file, either for a mesh view or parent mesh.
107
function save_mesh_file(mesh::P4estMeshView, output_directory, timestep,
108
mpi_parallel::False)
109
# Create output directory (if it does not exist)
110
mkpath(output_directory)
111
112
# Determine file name based on existence of meaningful time step
113
if timestep > 0
114
filename = joinpath(output_directory, @sprintf("mesh_%09d.h5", timestep))
115
p4est_filename = @sprintf("p4est_data_%09d", timestep)
116
else
117
filename = joinpath(output_directory, "mesh.h5")
118
p4est_filename = "p4est_data"
119
end
120
121
p4est_file = joinpath(output_directory, p4est_filename)
122
123
# Save the complete connectivity and `p4est` data to disk.
124
save_p4est!(p4est_file, mesh.parent.p4est)
125
126
# Open file (clobber existing content)
127
h5open(filename, "w") do file
128
# Add context information as attributes
129
attributes(file)["mesh_type"] = get_name(mesh)
130
attributes(file)["ndims"] = ndims(mesh)
131
attributes(file)["p4est_file"] = p4est_filename
132
attributes(file)["cell_ids"] = mesh.cell_ids
133
134
file["tree_node_coordinates"] = mesh.parent.tree_node_coordinates
135
file["nodes"] = Vector(mesh.parent.nodes) # the mesh uses `SVector`s for the nodes
136
# to increase the runtime performance
137
# but HDF5 can only handle plain arrays
138
file["boundary_names"] = mesh.parent.boundary_names .|> String
139
end
140
141
return filename
142
end
143
144
# Interpolate tree_node_coordinates to each quadrant at the specified nodes
145
# Note: This is a copy of the corresponding function in src/solvers/dgsem_p4est/containers_2d.jl,
146
# with modifications to skip cells not part of the mesh view
147
function calc_node_coordinates!(node_coordinates,
148
mesh::P4estMeshView{2, NDIMS_AMBIENT},
149
nodes::AbstractVector) where {NDIMS_AMBIENT}
150
# We use `StrideArray`s here since these buffers are used in performance-critical
151
# places and the additional information passed to the compiler makes them faster
152
# than native `Array`s.
153
tmp1 = StrideArray(undef, real(mesh),
154
StaticInt(NDIMS_AMBIENT), static_length(nodes),
155
static_length(mesh.parent.nodes))
156
matrix1 = StrideArray(undef, real(mesh),
157
static_length(nodes), static_length(mesh.parent.nodes))
158
matrix2 = similar(matrix1)
159
baryweights_in = barycentric_weights(mesh.parent.nodes)
160
161
# Macros from `p4est`
162
p4est_root_len = 1 << P4EST_MAXLEVEL
163
p4est_quadrant_len(l) = 1 << (P4EST_MAXLEVEL - l)
164
165
trees = unsafe_wrap_sc(p4est_tree_t, mesh.parent.p4est.trees)
166
167
mesh_view_cell_id = 0
168
for tree_id in eachindex(trees)
169
tree_offset = trees[tree_id].quadrants_offset
170
quadrants = unsafe_wrap_sc(p4est_quadrant_t, trees[tree_id].quadrants)
171
172
for i in eachindex(quadrants)
173
parent_mesh_cell_id = tree_offset + i
174
if !(parent_mesh_cell_id in mesh.cell_ids)
175
# This cell is not part of the mesh view, thus skip it
176
continue
177
end
178
mesh_view_cell_id += 1
179
180
quad = quadrants[i]
181
182
quad_length = p4est_quadrant_len(quad.level) / p4est_root_len
183
184
nodes_out_x = 2 * (quad_length * 1 / 2 * (nodes .+ 1) .+
185
quad.x / p4est_root_len) .- 1
186
nodes_out_y = 2 * (quad_length * 1 / 2 * (nodes .+ 1) .+
187
quad.y / p4est_root_len) .- 1
188
polynomial_interpolation_matrix!(matrix1, mesh.parent.nodes, nodes_out_x,
189
baryweights_in)
190
polynomial_interpolation_matrix!(matrix2, mesh.parent.nodes, nodes_out_y,
191
baryweights_in)
192
193
multiply_dimensionwise!(view(node_coordinates, :, :, :, mesh_view_cell_id),
194
matrix1, matrix2,
195
view(mesh.parent.tree_node_coordinates, :, :, :,
196
tree_id),
197
tmp1)
198
end
199
end
200
201
return node_coordinates
202
end
203
204
@inline mpi_parallel(mesh::P4estMeshView) = False()
205
end # @muladd
206
207