Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/meshes/mesh_io.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
# Save current mesh with some context information as an HDF5 file.
9
function save_mesh_file(mesh::Union{TreeMesh, P4estMesh, P4estMeshView, T8codeMesh},
10
output_directory,
11
timestep = 0)
12
save_mesh_file(mesh, output_directory, timestep, mpi_parallel(mesh))
13
end
14
15
function save_mesh_file(mesh::TreeMesh, output_directory, timestep,
16
mpi_parallel::False)
17
# Create output directory (if it does not exist)
18
mkpath(output_directory)
19
20
# Determine file name based on existence of meaningful time step
21
if timestep > 0
22
filename = joinpath(output_directory, @sprintf("mesh_%09d.h5", timestep))
23
else
24
filename = joinpath(output_directory, "mesh.h5")
25
end
26
27
# Open file (clobber existing content)
28
h5open(filename, "w") do file
29
# Add context information as attributes
30
n_cells = length(mesh.tree)
31
attributes(file)["mesh_type"] = get_name(mesh)
32
attributes(file)["ndims"] = ndims(mesh)
33
attributes(file)["n_cells"] = n_cells
34
attributes(file)["capacity"] = mesh.tree.capacity
35
attributes(file)["n_leaf_cells"] = count_leaf_cells(mesh.tree)
36
attributes(file)["minimum_level"] = minimum_level(mesh.tree)
37
attributes(file)["maximum_level"] = maximum_level(mesh.tree)
38
attributes(file)["center_level_0"] = mesh.tree.center_level_0
39
attributes(file)["length_level_0"] = mesh.tree.length_level_0
40
attributes(file)["periodicity"] = collect(mesh.tree.periodicity)
41
42
# Add tree data
43
file["parent_ids"] = @view mesh.tree.parent_ids[1:n_cells]
44
file["child_ids"] = @view mesh.tree.child_ids[:, 1:n_cells]
45
file["neighbor_ids"] = @view mesh.tree.neighbor_ids[:, 1:n_cells]
46
file["levels"] = @view mesh.tree.levels[1:n_cells]
47
file["coordinates"] = @view mesh.tree.coordinates[:, 1:n_cells]
48
end
49
50
return filename
51
end
52
53
# Save current mesh with some context information as an HDF5 file.
54
function save_mesh_file(mesh::TreeMesh, output_directory, timestep,
55
mpi_parallel::True)
56
# Create output directory (if it does not exist)
57
mpi_isroot() && mkpath(output_directory)
58
59
# Determine file name based on existence of meaningful time step
60
if timestep >= 0
61
filename = joinpath(output_directory, @sprintf("mesh_%09d.h5", timestep))
62
else
63
filename = joinpath(output_directory, "mesh.h5")
64
end
65
66
# Since the mesh is replicated on all ranks, only save from MPI root
67
if !mpi_isroot()
68
return filename
69
end
70
71
# Open file (clobber existing content)
72
h5open(filename, "w") do file
73
# Add context information as attributes
74
n_cells = length(mesh.tree)
75
attributes(file)["mesh_type"] = get_name(mesh)
76
attributes(file)["ndims"] = ndims(mesh)
77
attributes(file)["n_cells"] = n_cells
78
attributes(file)["capacity"] = mesh.tree.capacity
79
attributes(file)["n_leaf_cells"] = count_leaf_cells(mesh.tree)
80
attributes(file)["minimum_level"] = minimum_level(mesh.tree)
81
attributes(file)["maximum_level"] = maximum_level(mesh.tree)
82
attributes(file)["center_level_0"] = mesh.tree.center_level_0
83
attributes(file)["length_level_0"] = mesh.tree.length_level_0
84
attributes(file)["periodicity"] = collect(mesh.tree.periodicity)
85
86
# Add tree data
87
file["parent_ids"] = @view mesh.tree.parent_ids[1:n_cells]
88
file["child_ids"] = @view mesh.tree.child_ids[:, 1:n_cells]
89
file["neighbor_ids"] = @view mesh.tree.neighbor_ids[:, 1:n_cells]
90
file["levels"] = @view mesh.tree.levels[1:n_cells]
91
file["coordinates"] = @view mesh.tree.coordinates[:, 1:n_cells]
92
end
93
94
return filename
95
end
96
97
# Does not save the mesh itself to an HDF5 file. Instead saves important attributes
98
# of the mesh, like its size and the type of boundary mapping function.
99
# Then, within Trixi2Vtk, the StructuredMesh and its node coordinates are reconstructed from
100
# these attributes for plotting purposes
101
# Note: the `timestep` argument is needed for compatibility with the method for
102
# `StructuredMeshView`
103
function save_mesh_file(mesh::StructuredMesh, output_directory; system = "",
104
timestep = 0)
105
# Create output directory (if it does not exist)
106
mkpath(output_directory)
107
108
if isempty(system)
109
filename = joinpath(output_directory, "mesh.h5")
110
else
111
filename = joinpath(output_directory, @sprintf("mesh_%s.h5", system))
112
end
113
114
# Open file (clobber existing content)
115
h5open(filename, "w") do file
116
# Add context information as attributes
117
attributes(file)["mesh_type"] = get_name(mesh)
118
attributes(file)["ndims"] = ndims(mesh)
119
attributes(file)["size"] = collect(size(mesh))
120
attributes(file)["mapping"] = mesh.mapping_as_string
121
end
122
123
return filename
124
end
125
126
# Does not save the mesh itself to an HDF5 file. Instead saves important attributes
127
# of the mesh, like its size and the corresponding `.mesh` file used to construct the mesh.
128
# Then, within Trixi2Vtk, the UnstructuredMesh2D and its node coordinates are reconstructed
129
# from these attributes for plotting purposes
130
function save_mesh_file(mesh::UnstructuredMesh2D, output_directory)
131
# Create output directory (if it does not exist)
132
mkpath(output_directory)
133
134
filename = joinpath(output_directory, "mesh.h5")
135
136
# Open file (clobber existing content)
137
h5open(filename, "w") do file
138
# Add context information as attributes
139
attributes(file)["mesh_type"] = get_name(mesh)
140
attributes(file)["ndims"] = ndims(mesh)
141
attributes(file)["size"] = length(mesh)
142
attributes(file)["mesh_filename"] = mesh.filename
143
attributes(file)["periodicity"] = collect(mesh.periodicity)
144
end
145
146
return filename
147
end
148
149
# Does not save the mesh itself to an HDF5 file. Instead saves important attributes
150
# of the mesh, like its size and the type of boundary mapping function.
151
# Then, within Trixi2Vtk, the P4estMesh and its node coordinates are reconstructed from
152
# these attributes for plotting purposes
153
function save_mesh_file(mesh::P4estMesh, output_directory, timestep,
154
mpi_parallel::False)
155
# Create output directory (if it does not exist)
156
mkpath(output_directory)
157
158
# Determine file name based on existence of meaningful time step
159
if timestep > 0
160
filename = joinpath(output_directory, @sprintf("mesh_%09d.h5", timestep))
161
p4est_filename = @sprintf("p4est_data_%09d", timestep)
162
else
163
filename = joinpath(output_directory, "mesh.h5")
164
p4est_filename = "p4est_data"
165
end
166
167
p4est_file = joinpath(output_directory, p4est_filename)
168
169
# Save the complete connectivity and `p4est` data to disk.
170
save_p4est!(p4est_file, mesh.p4est)
171
172
# Open file (clobber existing content)
173
h5open(filename, "w") do file
174
# Add context information as attributes
175
attributes(file)["mesh_type"] = get_name(mesh)
176
attributes(file)["ndims"] = ndims(mesh)
177
attributes(file)["p4est_file"] = p4est_filename
178
179
file["tree_node_coordinates"] = mesh.tree_node_coordinates
180
file["nodes"] = Vector(mesh.nodes) # the mesh uses `SVector`s for the nodes
181
# to increase the runtime performance
182
# but HDF5 can only handle plain arrays
183
file["boundary_names"] = mesh.boundary_names .|> String
184
end
185
186
return filename
187
end
188
189
function save_mesh_file(mesh::P4estMesh, output_directory, timestep, mpi_parallel::True)
190
# Create output directory (if it does not exist)
191
mpi_isroot() && mkpath(output_directory)
192
193
# Determine file name based on existence of meaningful time step
194
if timestep > 0
195
filename = joinpath(output_directory, @sprintf("mesh_%09d.h5", timestep))
196
p4est_filename = @sprintf("p4est_data_%09d", timestep)
197
else
198
filename = joinpath(output_directory, "mesh.h5")
199
p4est_filename = "p4est_data"
200
end
201
202
p4est_file = joinpath(output_directory, p4est_filename)
203
204
# Save the complete connectivity/p4est data to disk.
205
save_p4est!(p4est_file, mesh.p4est)
206
207
# Since the mesh attributes are replicated on all ranks, only save from MPI root
208
if !mpi_isroot()
209
return filename
210
end
211
212
# Open file (clobber existing content)
213
h5open(filename, "w") do file
214
# Add context information as attributes
215
attributes(file)["mesh_type"] = get_name(mesh)
216
attributes(file)["ndims"] = ndims(mesh)
217
attributes(file)["p4est_file"] = p4est_filename
218
219
file["tree_node_coordinates"] = mesh.tree_node_coordinates
220
file["nodes"] = Vector(mesh.nodes) # the mesh uses `SVector`s for the nodes
221
# to increase the runtime performance
222
# but HDF5 can only handle plain arrays
223
file["boundary_names"] = mesh.boundary_names .|> String
224
end
225
226
return filename
227
end
228
229
# This routine works for both, serial and MPI parallel mode. The forest
230
# information is collected on all ranks and then gathered by the root rank.
231
# Since only the `levels` array of UInt8 and the global number of elements per
232
# tree (Int32) is necessary to reconstruct the forest it is not worth the
233
# effort to have a collective write to the HDF5 file. Instead, `levels` and
234
# `num_elements_per_tree` gets gathered by the root rank and written to disk.
235
function save_mesh_file(mesh::T8codeMesh, output_directory, timestep,
236
mpi_parallel::Union{False, True})
237
238
# Create output directory (if it does not exist).
239
mpi_isroot() && mkpath(output_directory)
240
241
# Determine file name based on existence of meaningful time step.
242
if timestep > 0
243
filename = joinpath(output_directory, @sprintf("mesh_%09d.h5", timestep))
244
else
245
filename = joinpath(output_directory, "mesh.h5")
246
end
247
248
# Retrieve refinement levels of all elements.
249
local_levels = get_levels(mesh)
250
if mpi_isparallel()
251
count = [length(local_levels)]
252
counts = MPI.Gather(view(count, 1), mpi_root(), mpi_comm())
253
254
if mpi_isroot()
255
levels = similar(local_levels, ncellsglobal(mesh))
256
MPI.Gatherv!(local_levels, MPI.VBuffer(levels, counts),
257
mpi_root(), mpi_comm())
258
else
259
MPI.Gatherv!(local_levels, nothing, mpi_root(), mpi_comm())
260
end
261
else
262
levels = local_levels
263
end
264
265
# Retrieve the number of elements per tree. Since a tree can be distributed
266
# among multiple ranks a reduction operation sums them all up. The latter
267
# is done on the root rank only.
268
num_global_trees = t8_forest_get_num_global_trees(mesh.forest)
269
num_elements_per_tree = zeros(t8_gloidx_t, num_global_trees)
270
num_local_trees = t8_forest_get_num_local_trees(mesh.forest)
271
for local_tree_id in 0:(num_local_trees - 1)
272
num_local_elements_in_tree = t8_forest_get_tree_num_elements(mesh.forest,
273
local_tree_id)
274
global_tree_id = t8_forest_global_tree_id(mesh.forest, local_tree_id)
275
num_elements_per_tree[global_tree_id + 1] = num_local_elements_in_tree
276
end
277
278
if mpi_isparallel()
279
MPI.Reduce!(num_elements_per_tree, +, mpi_comm())
280
end
281
282
# Since the mesh attributes are replicated on all ranks, only save from MPI
283
# root.
284
if !mpi_isroot()
285
return filename
286
end
287
288
# Retrieve face connectivity info of the coarse mesh.
289
treeIDs, neighIDs, faces, duals, orientations = get_cmesh_info(mesh)
290
291
# Open file (clobber existing content).
292
h5open(filename, "w") do file
293
# Add context information as attributes.
294
attributes(file)["mesh_type"] = get_name(mesh)
295
attributes(file)["ndims"] = ndims(mesh)
296
attributes(file)["ntrees"] = ntrees(mesh)
297
attributes(file)["nelements"] = ncellsglobal(mesh)
298
299
file["tree_node_coordinates"] = mesh.tree_node_coordinates
300
file["nodes"] = Vector(mesh.nodes)
301
file["boundary_names"] = mesh.boundary_names .|> String
302
file["treeIDs"] = treeIDs
303
file["neighIDs"] = neighIDs
304
file["faces"] = faces
305
file["duals"] = duals
306
file["orientations"] = orientations
307
file["levels"] = levels
308
file["num_elements_per_tree"] = num_elements_per_tree
309
end
310
311
return filename
312
end
313
314
@inline get_VXYZ(md::StartUpDG.MeshData) = get_VXYZ(md.mesh_type)
315
@inline get_VXYZ(mesh_type::StartUpDG.VertexMappedMesh) = mesh_type.VXYZ
316
@inline get_VXYZ(mesh_type::StartUpDG.CurvedMesh) = get_VXYZ(mesh_type.original_mesh_type)
317
@inline get_VXYZ(mesh_type::StartUpDG.HOHQMeshType) = mesh_type.hmd.VXYZ
318
319
@inline get_EToV(md::StartUpDG.MeshData) = get_EToV(md.mesh_type)
320
@inline get_EToV(mesh_type::StartUpDG.VertexMappedMesh) = mesh_type.EToV
321
@inline get_EToV(mesh_type::StartUpDG.CurvedMesh) = get_EToV(mesh_type.original_mesh_type)
322
@inline get_EToV(mesh_type::StartUpDG.HOHQMeshType) = mesh_type.hmd.EToV
323
324
# To save the data needed to reconstruct a DGMultiMesh object, we must include additional
325
# information contained within `dg.basis`. Currently, only the element shape and polynomial
326
# degree are stored, and it is assumed that the solution is stored at the default node
327
# positions for the `Polynomial` or `TensorProductWedge` approximation of that element
328
# shape and polynomial degree.
329
function save_mesh_file(mesh::DGMultiMesh, basis, output_directory, timestep = 0)
330
331
# Create output directory (if it does not exist).
332
mkpath(output_directory)
333
334
# Determine file name based on existence of meaningful time step.
335
if timestep > 0
336
filename = joinpath(output_directory, @sprintf("mesh_%09d.h5", timestep))
337
else
338
filename = joinpath(output_directory, "mesh.h5")
339
end
340
341
# Open file (clobber existing content)
342
h5open(filename, "w") do file
343
# Add context information as attributes
344
attributes(file)["mesh_type"] = get_name(mesh)
345
attributes(file)["ndims"] = ndims(mesh)
346
attributes(file)["nelements"] = ncells(mesh)
347
348
# For TensorProductWedge, the polynomial degree is a tuple
349
if basis.approximation_type isa TensorProductWedge
350
attributes(file)["polydeg_tri"] = basis.N[2]
351
attributes(file)["polydeg_line"] = basis.N[1]
352
else
353
attributes(file)["polydeg"] = basis.N
354
end
355
356
attributes(file)["element_type"] = basis.element_type |> typeof |> nameof |>
357
string
358
359
# Mesh-coordinates per element.
360
for idim in 1:ndims(mesh)
361
# ASCII: Char(120) => 'x'
362
file[119 + idim |> Char |> string] = mesh.md.xyz[idim]
363
end
364
365
# Transfer vectors of vectors to a matrix (2D array) and store into h5 file.
366
for (idim, vectors) in enumerate(get_VXYZ(mesh.md))
367
matrix = zeros(length(vectors[1]), length(vectors))
368
for ielem in eachindex(vectors)
369
@views matrix[:, ielem] .= vectors[ielem]
370
end
371
# ASCII: Char(58) => 'X'
372
# Vertex-coordinates per element.
373
file["V" * (87 + idim |> Char |> string)] = matrix
374
end
375
376
# Mapping element corners to vertices `VXYZ`.
377
file["EToV"] = get_EToV(mesh.md)
378
379
# TODO: Save boundaries.
380
# file["boundary_names"] = mesh.boundary_faces .|> String
381
end
382
383
return filename
384
end
385
386
"""
387
load_mesh(restart_file::AbstractString; n_cells_max)
388
389
Load the mesh from the `restart_file`.
390
"""
391
function load_mesh(restart_file::AbstractString; n_cells_max = 0, RealT = Float64)
392
if mpi_isparallel()
393
mesh_file = get_restart_mesh_filename(restart_file, True())
394
return load_mesh_parallel(mesh_file; n_cells_max = n_cells_max, RealT = RealT)
395
else
396
mesh_file = get_restart_mesh_filename(restart_file, False())
397
load_mesh_serial(mesh_file; n_cells_max = n_cells_max, RealT = RealT)
398
end
399
end
400
401
function load_mesh_serial(mesh_file::AbstractString; n_cells_max, RealT)
402
ndims, mesh_type = h5open(mesh_file, "r") do file
403
return read(attributes(file)["ndims"]),
404
read(attributes(file)["mesh_type"])
405
end
406
407
if mesh_type == "TreeMesh"
408
capacity = h5open(mesh_file, "r") do file
409
return read(attributes(file)["capacity"])
410
end
411
mesh = TreeMesh(SerialTree{ndims, RealT}, max(n_cells_max, capacity),
412
RealT = RealT)
413
load_mesh!(mesh, mesh_file)
414
elseif mesh_type in ("StructuredMesh", "StructuredMeshView")
415
size_, mapping_as_string = h5open(mesh_file, "r") do file
416
return read(attributes(file)["size"]),
417
read(attributes(file)["mapping"])
418
end
419
420
size = Tuple(size_)
421
422
# TODO: `@eval` is evil
423
#
424
# This should be replaced with something more robust and secure,
425
# see https://github.com/trixi-framework/Trixi.jl/issues/541).
426
if ndims == 1
427
mapping = eval(Meta.parse("""function (xi)
428
$mapping_as_string
429
mapping(xi)
430
end
431
"""))
432
elseif ndims == 2
433
mapping = eval(Meta.parse("""function (xi, eta)
434
$mapping_as_string
435
mapping(xi, eta)
436
end
437
"""))
438
else # ndims == 3
439
mapping = eval(Meta.parse("""function (xi, eta, zeta)
440
$mapping_as_string
441
mapping(xi, eta, zeta)
442
end
443
"""))
444
end
445
446
mesh = StructuredMesh(size, mapping; RealT = RealT, unsaved_changes = false,
447
mapping_as_string = mapping_as_string)
448
mesh.current_filename = mesh_file
449
elseif mesh_type == "UnstructuredMesh2D"
450
mesh_filename, periodicity_ = h5open(mesh_file, "r") do file
451
return read(attributes(file)["mesh_filename"]),
452
read(attributes(file)["periodicity"])
453
end
454
mesh = UnstructuredMesh2D(mesh_filename; RealT = RealT,
455
periodicity = periodicity_,
456
unsaved_changes = false)
457
mesh.current_filename = mesh_file
458
elseif mesh_type == "P4estMesh"
459
p4est_filename, tree_node_coordinates,
460
nodes, boundary_names_ = h5open(mesh_file, "r") do file
461
return read(attributes(file)["p4est_file"]),
462
read(file["tree_node_coordinates"]),
463
read(file["nodes"]),
464
read(file["boundary_names"])
465
end
466
467
boundary_names = boundary_names_ .|> Symbol
468
469
p4est_file = joinpath(dirname(mesh_file), p4est_filename)
470
# Prevent Julia crashes when `p4est` can't find the file
471
@assert isfile(p4est_file)
472
473
p4est = load_p4est(p4est_file, Val(ndims))
474
475
mesh = P4estMesh{ndims}(p4est, tree_node_coordinates,
476
nodes, boundary_names, mesh_file, false, true)
477
elseif mesh_type == "P4estMeshView"
478
p4est_filename, cell_ids, tree_node_coordinates,
479
nodes, boundary_names_ = h5open(mesh_file, "r") do file
480
return read(attributes(file)["p4est_file"]),
481
read(attributes(file)["cell_ids"]),
482
read(file["tree_node_coordinates"]),
483
read(file["nodes"]),
484
read(file["boundary_names"])
485
end
486
487
boundary_names = boundary_names_ .|> Symbol
488
489
p4est_file = joinpath(dirname(mesh_file), p4est_filename)
490
# Prevent Julia crashes when `p4est` can't find the file
491
@assert isfile(p4est_file)
492
493
p4est = load_p4est(p4est_file, Val(ndims))
494
495
unsaved_changes = false
496
p4est_partition_allow_for_coarsening = true
497
parent_mesh = P4estMesh{ndims}(p4est, tree_node_coordinates,
498
nodes, boundary_names, mesh_file,
499
unsaved_changes,
500
p4est_partition_allow_for_coarsening)
501
502
mesh = P4estMeshView(parent_mesh, cell_ids)
503
504
elseif mesh_type == "T8codeMesh"
505
ndims, ntrees, nelements, tree_node_coordinates,
506
nodes, boundary_names_, treeIDs, neighIDs, faces, duals, orientations,
507
levels, num_elements_per_tree = h5open(mesh_file, "r") do file
508
return read(attributes(file)["ndims"]),
509
read(attributes(file)["ntrees"]),
510
read(attributes(file)["nelements"]),
511
read(file["tree_node_coordinates"]),
512
read(file["nodes"]),
513
read(file["boundary_names"]),
514
read(file["treeIDs"]),
515
read(file["neighIDs"]),
516
read(file["faces"]),
517
read(file["duals"]),
518
read(file["orientations"]),
519
read(file["levels"]),
520
read(file["num_elements_per_tree"])
521
end
522
523
boundary_names = boundary_names_ .|> Symbol
524
525
mesh = T8codeMesh(ndims, ntrees, nelements, tree_node_coordinates,
526
nodes, boundary_names, treeIDs, neighIDs, faces,
527
duals, orientations, levels, num_elements_per_tree)
528
529
elseif mesh_type == "DGMultiMesh"
530
ndims, nelements, etype_str, EToV = h5open(mesh_file, "r") do file
531
return read(attributes(file)["ndims"]),
532
read(attributes(file)["nelements"]),
533
read(attributes(file)["element_type"]),
534
read(file["EToV"])
535
end
536
537
# Load RefElemData.
538
etype = get_element_type_from_string(etype_str)()
539
540
polydeg = h5open(mesh_file, "r") do file
541
if etype isa Trixi.Wedge && haskey(attributes(file), "polydeg_tri")
542
return tuple(read(attributes(file)["polydeg_tri"]),
543
read(attributes(file)["polydeg_line"]))
544
else
545
return read(attributes(file)["polydeg"])
546
end
547
end
548
549
# Currently, we assume that `basis.approximation_type` is a `TensorProductWedge`
550
# with 2-tuple polynomial degree or a `Polynomial` with integer polynomial degree.
551
# TODO: Add support for other approximation types. This would requires further
552
# information to be saved to the HDF5 file.
553
if etype isa StartUpDG.Wedge && polydeg isa NTuple{2}
554
factor_a = RefElemData(StartUpDG.Tri(), Polynomial(), polydeg[1])
555
factor_b = RefElemData(StartUpDG.Line(), Polynomial(), polydeg[2])
556
557
tensor = StartUpDG.TensorProductWedge(factor_a, factor_b)
558
rd = RefElemData(etype, tensor)
559
else
560
rd = RefElemData(etype, Polynomial(), polydeg)
561
end
562
563
# Load physical nodes.
564
xyz = h5open(mesh_file, "r") do file
565
# ASCII: Char(120) => 'x'
566
return tuple([read(file[119 + i |> Char |> string]) for i in 1:ndims]...)
567
end
568
569
# Load element vertices.
570
vxyz = h5open(mesh_file, "r") do file
571
# ASCII: Char(58) => 'X'
572
return tuple([read(file["V" * (87 + i |> Char |> string)]) for i in 1:ndims]...)
573
end
574
575
if ndims == 1
576
md = MeshData(vxyz[1][1, :], EToV, rd)
577
else
578
# Load MeshData and restore original physical nodes.
579
md = MeshData(rd, MeshData(vxyz, EToV, rd), xyz...)
580
end
581
582
mesh = DGMultiMesh{}(md, [])
583
else
584
error("Unknown mesh type!")
585
end
586
587
return mesh
588
end
589
590
function load_mesh!(mesh::SerialTreeMesh, mesh_file::AbstractString)
591
mesh.current_filename = mesh_file
592
mesh.unsaved_changes = false
593
594
# Read mesh file
595
h5open(mesh_file, "r") do file
596
# Set domain information
597
mesh.tree.center_level_0 = read(attributes(file)["center_level_0"])
598
mesh.tree.length_level_0 = read(attributes(file)["length_level_0"])
599
mesh.tree.periodicity = Tuple(read(attributes(file)["periodicity"]))
600
601
# Set length
602
n_cells = read(attributes(file)["n_cells"])
603
resize!(mesh.tree, n_cells)
604
605
# Read in data
606
mesh.tree.parent_ids[1:n_cells] = read(file["parent_ids"])
607
mesh.tree.child_ids[:, 1:n_cells] = read(file["child_ids"])
608
mesh.tree.neighbor_ids[:, 1:n_cells] = read(file["neighbor_ids"])
609
mesh.tree.levels[1:n_cells] = read(file["levels"])
610
mesh.tree.coordinates[:, 1:n_cells] = read(file["coordinates"])
611
end
612
613
return mesh
614
end
615
616
function load_mesh_parallel(mesh_file::AbstractString; n_cells_max, RealT)
617
if mpi_isroot()
618
ndims_, mesh_type = h5open(mesh_file, "r") do file
619
return read(attributes(file)["ndims"]),
620
read(attributes(file)["mesh_type"])
621
end
622
MPI.Bcast!(Ref(ndims_), mpi_root(), mpi_comm())
623
MPI.bcast(mesh_type, mpi_root(), mpi_comm())
624
else
625
ndims_ = MPI.Bcast!(Ref(0), mpi_root(), mpi_comm())[]
626
mesh_type = MPI.bcast(nothing, mpi_root(), mpi_comm())
627
end
628
629
if mesh_type == "TreeMesh"
630
if mpi_isroot()
631
n_cells, capacity = h5open(mesh_file, "r") do file
632
return read(attributes(file)["n_cells"]),
633
read(attributes(file)["capacity"])
634
end
635
MPI.Bcast!(Ref(n_cells), mpi_root(), mpi_comm())
636
MPI.Bcast!(Ref(capacity), mpi_root(), mpi_comm())
637
else
638
n_cells = MPI.Bcast!(Ref(0), mpi_root(), mpi_comm())[]
639
capacity = MPI.Bcast!(Ref(0), mpi_root(), mpi_comm())[]
640
end
641
642
mesh = TreeMesh(ParallelTree{ndims_, RealT},
643
max(n_cells, n_cells_max, capacity),
644
RealT = RealT)
645
load_mesh!(mesh, mesh_file)
646
elseif mesh_type == "P4estMesh"
647
if mpi_isroot()
648
p4est_filename, tree_node_coordinates,
649
nodes, boundary_names_ = h5open(mesh_file, "r") do file
650
return read(attributes(file)["p4est_file"]),
651
read(file["tree_node_coordinates"]),
652
read(file["nodes"]),
653
read(file["boundary_names"])
654
end
655
656
boundary_names = boundary_names_ .|> Symbol
657
658
p4est_file = joinpath(dirname(mesh_file), p4est_filename)
659
660
data = (p4est_file, tree_node_coordinates, nodes, boundary_names)
661
MPI.bcast(data, mpi_root(), mpi_comm())
662
else
663
data = MPI.bcast(nothing, mpi_root(), mpi_comm())
664
p4est_file, tree_node_coordinates, nodes, boundary_names = data
665
end
666
667
# Prevent Julia crashes when `p4est` can't find the file
668
@assert isfile(p4est_file)
669
670
p4est = load_p4est(p4est_file, Val(ndims_))
671
672
mesh = P4estMesh{ndims_}(p4est, tree_node_coordinates,
673
nodes, boundary_names, mesh_file, false, true)
674
675
elseif mesh_type == "T8codeMesh"
676
if mpi_isroot()
677
ndims, ntrees, nelements, tree_node_coordinates, nodes,
678
boundary_names_, treeIDs, neighIDs, faces, duals, orientations, levels,
679
num_elements_per_tree = h5open(mesh_file, "r") do file
680
return read(attributes(file)["ndims"]),
681
read(attributes(file)["ntrees"]),
682
read(attributes(file)["nelements"]),
683
read(file["tree_node_coordinates"]),
684
read(file["nodes"]),
685
read(file["boundary_names"]),
686
read(file["treeIDs"]),
687
read(file["neighIDs"]),
688
read(file["faces"]),
689
read(file["duals"]),
690
read(file["orientations"]),
691
read(file["levels"]),
692
read(file["num_elements_per_tree"])
693
end
694
695
boundary_names = boundary_names_ .|> Symbol
696
697
data = (ndims, ntrees, nelements, tree_node_coordinates, nodes,
698
boundary_names, treeIDs, neighIDs, faces, duals,
699
orientations, levels, num_elements_per_tree)
700
MPI.bcast(data, mpi_root(), mpi_comm())
701
else
702
data = MPI.bcast(nothing, mpi_root(), mpi_comm())
703
ndims, ntrees, nelements, tree_node_coordinates, nodes,
704
boundary_names, treeIDs, neighIDs, faces, duals, orientations, levels,
705
num_elements_per_tree = data
706
end
707
708
mesh = T8codeMesh(ndims, ntrees, nelements, tree_node_coordinates,
709
nodes, boundary_names, treeIDs, neighIDs, faces,
710
duals, orientations, levels, num_elements_per_tree)
711
else
712
error("Unknown mesh type!")
713
end
714
715
return mesh
716
end
717
718
function load_mesh!(mesh::ParallelTreeMesh, mesh_file::AbstractString)
719
mesh.current_filename = mesh_file
720
mesh.unsaved_changes = false
721
722
if mpi_isroot()
723
h5open(mesh_file, "r") do file
724
# Set domain information
725
mesh.tree.center_level_0 = read(attributes(file)["center_level_0"])
726
mesh.tree.length_level_0 = read(attributes(file)["length_level_0"])
727
mesh.tree.periodicity = Tuple(read(attributes(file)["periodicity"]))
728
MPI.Bcast!(collect(mesh.tree.center_level_0), mpi_root(), mpi_comm())
729
MPI.Bcast!(collect(mesh.tree.length_level_0), mpi_root(), mpi_comm())
730
MPI.Bcast!(collect(mesh.tree.periodicity), mpi_root(), mpi_comm())
731
732
# Set length
733
n_cells = read(attributes(file)["n_cells"])
734
MPI.Bcast!(Ref(n_cells), mpi_root(), mpi_comm())
735
resize!(mesh.tree, n_cells)
736
737
# Read in data
738
mesh.tree.parent_ids[1:n_cells] = read(file["parent_ids"])
739
mesh.tree.child_ids[:, 1:n_cells] = read(file["child_ids"])
740
mesh.tree.neighbor_ids[:, 1:n_cells] = read(file["neighbor_ids"])
741
mesh.tree.levels[1:n_cells] = read(file["levels"])
742
mesh.tree.coordinates[:, 1:n_cells] = read(file["coordinates"])
743
@views MPI.Bcast!(mesh.tree.parent_ids[1:n_cells], mpi_root(), mpi_comm())
744
@views MPI.Bcast!(mesh.tree.child_ids[:, 1:n_cells], mpi_root(), mpi_comm())
745
@views MPI.Bcast!(mesh.tree.neighbor_ids[:, 1:n_cells], mpi_root(),
746
mpi_comm())
747
@views MPI.Bcast!(mesh.tree.levels[1:n_cells], mpi_root(), mpi_comm())
748
@views MPI.Bcast!(mesh.tree.coordinates[:, 1:n_cells], mpi_root(),
749
mpi_comm())
750
end
751
else # non-root ranks
752
# Set domain information
753
mesh.tree.center_level_0 = MPI.Bcast!(collect(mesh.tree.center_level_0),
754
mpi_root(), mpi_comm())
755
mesh.tree.length_level_0 = MPI.Bcast!(collect(mesh.tree.length_level_0),
756
mpi_root(), mpi_comm())[1]
757
mesh.tree.periodicity = Tuple(MPI.Bcast!(collect(mesh.tree.periodicity),
758
mpi_root(), mpi_comm()))
759
760
# Set length
761
n_cells = MPI.Bcast!(Ref(0), mpi_root(), mpi_comm())[]
762
resize!(mesh.tree, n_cells)
763
764
# Read in data
765
@views MPI.Bcast!(mesh.tree.parent_ids[1:n_cells], mpi_root(), mpi_comm())
766
@views MPI.Bcast!(mesh.tree.child_ids[:, 1:n_cells], mpi_root(), mpi_comm())
767
@views MPI.Bcast!(mesh.tree.neighbor_ids[:, 1:n_cells], mpi_root(), mpi_comm())
768
@views MPI.Bcast!(mesh.tree.levels[1:n_cells], mpi_root(), mpi_comm())
769
@views MPI.Bcast!(mesh.tree.coordinates[:, 1:n_cells], mpi_root(), mpi_comm())
770
end
771
772
# Partition mesh
773
partition!(mesh)
774
775
return mesh
776
end
777
end # @muladd
778
779