Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/meshes/unstructured_mesh.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
UnstructuredMesh2D <: AbstractMesh{2}
10
11
An unstructured (possibly curved) quadrilateral mesh.
12
13
UnstructuredMesh2D(filename; RealT=Float64, periodicity=false)
14
15
All mesh information, neighbour coupling, and boundary curve information is read in
16
from a mesh file `filename`.
17
"""
18
mutable struct UnstructuredMesh2D{RealT <: Real,
19
CurvedSurfaceT <: CurvedSurface{RealT}} <:
20
AbstractMesh{2}
21
filename :: String
22
n_corners :: Int
23
n_surfaces :: Int # total number of surfaces
24
n_interfaces :: Int # number of interior surfaces
25
n_boundaries :: Int # number of surfaces on the physical boundary
26
n_elements :: Int
27
polydeg :: Int
28
corners :: Array{RealT, 2} # [ndims, n_corners]
29
neighbour_information :: Array{Int, 2} # [neighbour node/element/edge ids, n_surfaces]
30
boundary_names :: Array{Symbol, 2} # [local sides, n_elements]
31
periodicity :: Bool
32
element_node_ids :: Array{Int, 2} # [node ids, n_elements]
33
element_is_curved :: Vector{Bool}
34
surface_curves :: Array{CurvedSurfaceT, 2} # [local sides, n_elements]
35
current_filename :: String
36
unsaved_changes :: Bool # if true, the mesh will be saved for plotting
37
end
38
39
# constructor for an unstructured mesh read in from a file
40
# TODO: this mesh file parsing and construction of the mesh skeleton can likely be improved in terms
41
# of performance
42
function UnstructuredMesh2D(filename; RealT = Float64, periodicity = false,
43
unsaved_changes = true)
44
45
# readin all the information from the mesh file into a string array
46
file_lines = readlines(open(filename))
47
48
# readin the number of nodes, number of interfaces, number of elements and local polynomial degree
49
current_line = split(file_lines[2])
50
n_corners = parse(Int, current_line[1])
51
n_surfaces = parse(Int, current_line[2])
52
n_elements = parse(Int, current_line[3])
53
mesh_polydeg = parse(Int, current_line[4])
54
55
mesh_nnodes = mesh_polydeg + 1
56
57
# The types of structs used in the following depend on information read from
58
# the mesh file. Thus, this cannot be type stable at all. Hence, we allocate
59
# the memory now and introduce a function barrier before continuing to read
60
# data from the file.
61
corner_nodes = Array{RealT}(undef, (2, n_corners))
62
interface_info = Array{Int}(undef, (6, n_surfaces))
63
element_node_ids = Array{Int}(undef, (4, n_elements))
64
curved_check = Vector{Int}(undef, 4)
65
quad_corners = Array{RealT}(undef, (4, 2))
66
quad_corners_flipped = Array{RealT}(undef, (4, 2))
67
curve_values = Array{RealT}(undef, (mesh_nnodes, 2))
68
element_is_curved = Array{Bool}(undef, n_elements)
69
CurvedSurfaceT = CurvedSurface{RealT}
70
surface_curves = Array{CurvedSurfaceT}(undef, (4, n_elements))
71
boundary_names = Array{Symbol}(undef, (4, n_elements))
72
73
# create the Chebyshev-Gauss-Lobatto nodes used to represent any curved boundaries that are
74
# required to construct the sides
75
cheby_nodes_, _ = chebyshev_gauss_lobatto_nodes_weights(mesh_nnodes)
76
bary_weights_ = barycentric_weights(cheby_nodes_)
77
cheby_nodes = SVector{mesh_nnodes}(cheby_nodes_)
78
bary_weights = SVector{mesh_nnodes}(bary_weights_)
79
80
arrays = (; corner_nodes, interface_info, element_node_ids, curved_check,
81
quad_corners, quad_corners_flipped, curve_values,
82
element_is_curved, surface_curves, boundary_names)
83
counters = (; n_corners, n_surfaces, n_elements)
84
85
n_boundaries = parse_mesh_file!(arrays, RealT, CurvedSurfaceT, file_lines, counters,
86
cheby_nodes, bary_weights)
87
88
# get the number of internal interfaces in the mesh
89
if periodicity
90
n_interfaces = n_surfaces
91
n_boundaries = 0
92
else
93
n_interfaces = n_surfaces - n_boundaries
94
end
95
96
return UnstructuredMesh2D{RealT, CurvedSurfaceT}(filename, n_corners, n_surfaces,
97
n_interfaces, n_boundaries,
98
n_elements, mesh_polydeg,
99
corner_nodes,
100
interface_info, boundary_names,
101
periodicity,
102
element_node_ids,
103
element_is_curved, surface_curves,
104
"", unsaved_changes)
105
end
106
107
function parse_mesh_file!(arrays, RealT, CurvedSurfaceT, file_lines, counters,
108
cheby_nodes, bary_weights)
109
@unpack (corner_nodes, interface_info, element_node_ids, curved_check,
110
quad_corners, quad_corners_flipped, curve_values,
111
element_is_curved, surface_curves, boundary_names) = arrays
112
@unpack n_corners, n_surfaces, n_elements = counters
113
mesh_nnodes = length(cheby_nodes)
114
115
# counter to step through the mesh file line by line
116
file_idx = 3
117
118
# readin an store the nodes that dictate the corners of the elements needed to construct the
119
# element geometry terms
120
for j in 1:n_corners
121
current_line = split(file_lines[file_idx])
122
corner_nodes[1, j] = parse(RealT, current_line[1])
123
corner_nodes[2, j] = parse(RealT, current_line[2])
124
file_idx += 1
125
end
126
127
# readin an store the nodes that dictate the interfaces, neighbour data, and orientations contains
128
# the following:
129
# interface_info[1] = start node ID
130
# interface_info[2] = end node ID
131
# interface_info[3] = ID of the primary element
132
# interface_info[4] = ID of the secondary element (if 0 then it is a physical boundary)
133
# interface_info[5] = local side ID on the primary element
134
# interface_info[6] = local side ID on the secondary element
135
# container to for the interface neighbour information and connectivity
136
n_boundaries = 0
137
for j in 1:n_surfaces
138
current_line = split(file_lines[file_idx])
139
interface_info[1, j] = parse(Int, current_line[1])
140
interface_info[2, j] = parse(Int, current_line[2])
141
interface_info[3, j] = parse(Int, current_line[3])
142
interface_info[4, j] = parse(Int, current_line[4])
143
interface_info[5, j] = parse(Int, current_line[5])
144
interface_info[6, j] = parse(Int, current_line[6])
145
146
# count the number of physical boundaries
147
if interface_info[4, j] == 0
148
n_boundaries += 1
149
end
150
file_idx += 1
151
end
152
153
# work arrays to pull to correct corners of a given element (agnostic to curvature) and local
154
# copies of the curved boundary information
155
156
# readin an store the curved boundary information of the elements
157
158
for j in 1:n_elements
159
# pull the corner node IDs
160
current_line = split(file_lines[file_idx])
161
element_node_ids[1, j] = parse(Int, current_line[1])
162
element_node_ids[2, j] = parse(Int, current_line[2])
163
element_node_ids[3, j] = parse(Int, current_line[3])
164
element_node_ids[4, j] = parse(Int, current_line[4])
165
for i in 1:4
166
# pull the (x,y) values of these corners out of the nodes array
167
quad_corners[i, :] .= corner_nodes[:, element_node_ids[i, j]]
168
end
169
# pull the information to check if boundary is curved in order to read in additional data
170
file_idx += 1
171
current_line = split(file_lines[file_idx])
172
curved_check[1] = parse(Int, current_line[1])
173
curved_check[2] = parse(Int, current_line[2])
174
curved_check[3] = parse(Int, current_line[3])
175
curved_check[4] = parse(Int, current_line[4])
176
if sum(curved_check) == 0
177
# quadrilateral element is straight sided
178
element_is_curved[j] = false
179
file_idx += 1
180
# read all the boundary names
181
boundary_names[:, j] = map(Symbol, split(file_lines[file_idx]))
182
else
183
# quadrilateral element has at least one curved side
184
element_is_curved[j] = true
185
186
# flip node ordering to make sure the element is right-handed for the interpolations
187
m1 = 1
188
m2 = 2
189
@views quad_corners_flipped[1, :] .= quad_corners[4, :]
190
@views quad_corners_flipped[2, :] .= quad_corners[2, :]
191
@views quad_corners_flipped[3, :] .= quad_corners[3, :]
192
@views quad_corners_flipped[4, :] .= quad_corners[1, :]
193
for i in 1:4
194
if curved_check[i] == 0
195
# when curved_check[i] is 0 then the "curve" from corner `i` to corner `i+1` is a
196
# straight line. So we must construct the interpolant for this line
197
for k in 1:mesh_nnodes
198
curve_values[k, 1] = linear_interpolate(cheby_nodes[k],
199
quad_corners_flipped[m1,
200
1],
201
quad_corners_flipped[m2,
202
1])
203
curve_values[k, 2] = linear_interpolate(cheby_nodes[k],
204
quad_corners_flipped[m1,
205
2],
206
quad_corners_flipped[m2,
207
2])
208
end
209
else
210
# when curved_check[i] is 1 this curved boundary information is supplied by the mesh
211
# generator. So we just read it into a work array
212
for k in 1:mesh_nnodes
213
file_idx += 1
214
current_line = split(file_lines[file_idx])
215
curve_values[k, 1] = parse(RealT, current_line[1])
216
curve_values[k, 2] = parse(RealT, current_line[2])
217
end
218
end
219
# construct the curve interpolant for the current side
220
surface_curves[i, j] = CurvedSurfaceT(cheby_nodes, bary_weights,
221
copy(curve_values))
222
# indexing update that contains a "flip" to ensure correct element orientation
223
# if we need to construct the straight line "curves" when curved_check[i] == 0
224
m1 += 1
225
if i == 3
226
m2 = 1
227
else
228
m2 += 1
229
end
230
end
231
# finally read in the boundary names where "---" means an internal connection
232
file_idx += 1
233
boundary_names[:, j] = map(Symbol, split(file_lines[file_idx]))
234
end
235
# one last increment to the global index to read the next piece of element information
236
file_idx += 1
237
end
238
239
return n_boundaries
240
end
241
242
@inline Base.ndims(::UnstructuredMesh2D) = 2
243
@inline Base.real(::UnstructuredMesh2D{RealT}) where {RealT} = RealT
244
245
# Check if mesh is periodic
246
isperiodic(mesh::UnstructuredMesh2D) = mesh.periodicity
247
248
Base.length(mesh::UnstructuredMesh2D) = mesh.n_elements
249
250
function Base.show(io::IO,
251
::UnstructuredMesh2D{RealT, CurvedSurfaceT}) where {RealT,
252
CurvedSurfaceT}
253
print(io, "UnstructuredMesh2D{2, ", RealT, ", ", CurvedSurfaceT, "}")
254
end
255
256
function Base.show(io::IO, ::MIME"text/plain",
257
mesh::UnstructuredMesh2D{RealT, CurvedSurfaceT}) where {RealT,
258
CurvedSurfaceT
259
}
260
if get(io, :compact, false)
261
show(io, mesh)
262
else
263
summary_header(io,
264
"UnstructuredMesh2D{" * string(2) * ", " * string(RealT) * ", " *
265
string(CurvedSurfaceT) * "}")
266
summary_line(io, "mesh file", mesh.filename)
267
summary_line(io, "number of elements", length(mesh))
268
summary_line(io, "faces", mesh.n_surfaces)
269
summary_line(io, "mesh polynomial degree", mesh.polydeg)
270
summary_footer(io)
271
end
272
end
273
end # @muladd
274
275