Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/meshes/tree_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
include("abstract_tree.jl")
9
include("serial_tree.jl")
10
include("parallel_tree.jl")
11
12
get_name(mesh::AbstractMesh) = mesh |> typeof |> nameof |> string
13
14
# Composite type to hold the actual tree in addition to other mesh-related data
15
# that is not strictly part of the tree.
16
# The mesh is really just about the connectivity, size, and location of the individual
17
# tree nodes. Neighbor information between interfaces or the large sides for mortars is
18
# something that is solver-specific and that might not be needed by all solvers (or in a
19
# different form). Also, these data values can be performance critical, so a mesh would
20
# have to store them for all solvers in an efficient way - OTOH, different solvers might
21
# use different cells of a shared mesh, so "efficient" is again solver dependent.
22
"""
23
TreeMesh{NDIMS} <: AbstractMesh{NDIMS}
24
25
A Cartesian mesh based on trees of hypercubes to support adaptive mesh refinement.
26
"""
27
mutable struct TreeMesh{NDIMS, TreeType <: AbstractTree{NDIMS}, RealT <: Real} <:
28
AbstractMesh{NDIMS}
29
tree::TreeType
30
current_filename::String
31
unsaved_changes::Bool
32
first_cell_by_rank::OffsetVector{Int, Vector{Int}}
33
n_cells_by_rank::OffsetVector{Int, Vector{Int}}
34
35
function TreeMesh{NDIMS, TreeType, RealT}(n_cells_max::Integer) where {NDIMS,
36
TreeType <:
37
AbstractTree{NDIMS},
38
RealT <:
39
Real}
40
# Create mesh
41
m = new()
42
m.tree = TreeType(n_cells_max)
43
m.current_filename = ""
44
m.unsaved_changes = true
45
m.first_cell_by_rank = OffsetVector(Int[], 0)
46
m.n_cells_by_rank = OffsetVector(Int[], 0)
47
48
return m
49
end
50
51
# TODO: Taal refactor, order of important arguments, use of n_cells_max?
52
function TreeMesh{NDIMS, TreeType, RealT}(n_cells_max::Integer,
53
domain_center::AbstractArray{RealT},
54
domain_length::RealT,
55
periodicity = true) where {NDIMS,
56
TreeType <:
57
AbstractTree{NDIMS},
58
RealT <: Real}
59
@assert NDIMS isa Integer && NDIMS > 0
60
61
# Create mesh
62
m = new()
63
m.tree = TreeType(n_cells_max, domain_center, domain_length, periodicity)
64
m.current_filename = ""
65
m.unsaved_changes = true
66
m.first_cell_by_rank = OffsetVector(Int[], 0)
67
m.n_cells_by_rank = OffsetVector(Int[], 0)
68
69
return m
70
end
71
end
72
73
const TreeMesh1D = TreeMesh{1, TreeType} where {TreeType <: AbstractTree{1}}
74
const TreeMesh2D = TreeMesh{2, TreeType} where {TreeType <: AbstractTree{2}}
75
const TreeMesh3D = TreeMesh{3, TreeType} where {TreeType <: AbstractTree{3}}
76
77
const SerialTreeMesh{NDIMS} = TreeMesh{NDIMS, <:SerialTree{NDIMS}}
78
const ParallelTreeMesh{NDIMS} = TreeMesh{NDIMS, <:ParallelTree{NDIMS}}
79
80
@inline mpi_parallel(mesh::SerialTreeMesh) = False()
81
@inline mpi_parallel(mesh::ParallelTreeMesh) = True()
82
83
partition!(mesh::SerialTreeMesh) = nothing
84
85
# Constructor for passing the dimension and mesh type as an argument
86
function TreeMesh(::Type{TreeType}, args...;
87
RealT = Float64) where {NDIMS, TreeType <: AbstractTree{NDIMS}}
88
TreeMesh{NDIMS, TreeType, RealT}(args...)
89
end
90
91
# Constructor accepting a single number as center (as opposed to an array) for 1D
92
function TreeMesh{1, TreeType, RealT}(n::Int, center::RealT, len::RealT,
93
periodicity = true) where {
94
TreeType <:
95
AbstractTree{1},
96
RealT <: Real}
97
return TreeMesh{1, TreeType, RealT}(n, SVector{1, RealT}(center), len, periodicity)
98
end
99
100
function TreeMesh{NDIMS, TreeType, RealT}(n_cells_max::Integer,
101
domain_center::NTuple{NDIMS, RealT},
102
domain_length::RealT,
103
periodicity = true) where {NDIMS,
104
TreeType <:
105
AbstractTree{NDIMS},
106
RealT <: Real}
107
TreeMesh{NDIMS, TreeType, RealT}(n_cells_max, SVector{NDIMS, RealT}(domain_center),
108
domain_length, periodicity)
109
end
110
111
function TreeMesh(coordinates_min::NTuple{NDIMS, Real},
112
coordinates_max::NTuple{NDIMS, Real};
113
n_cells_max,
114
periodicity = true,
115
initial_refinement_level,
116
refinement_patches = (),
117
coarsening_patches = (),
118
RealT = Float64) where {NDIMS}
119
# check arguments
120
if !(n_cells_max isa Integer && n_cells_max > 0)
121
throw(ArgumentError("`n_cells_max` must be a positive integer (provided `n_cells_max = $n_cells_max`)"))
122
end
123
if !(initial_refinement_level isa Integer && initial_refinement_level >= 0)
124
throw(ArgumentError("`initial_refinement_level` must be a non-negative integer (provided `initial_refinement_level = $initial_refinement_level`)"))
125
end
126
127
# Check if elements in coordinates_min and coordinates_max are all of type RealT
128
for i in 1:NDIMS
129
if !(coordinates_min[i] isa RealT)
130
@warn "Element $i in `coordinates_min` is not of type $RealT (provided `coordinates_min[$i] = $(coordinates_min[i])`)"
131
end
132
if !(coordinates_max[i] isa RealT)
133
@warn "Element $i in `coordinates_max` is not of type $RealT (provided `coordinates_max[$i] = $(coordinates_max[i])`)"
134
end
135
end
136
137
coordinates_min_max_check(coordinates_min, coordinates_max)
138
139
# TreeMesh requires equal domain lengths in all dimensions
140
domain_center = @. convert(RealT, (coordinates_min + coordinates_max) / 2)
141
domain_length = convert(RealT, coordinates_max[1] - coordinates_min[1])
142
if !all(coordinates_max[i] - coordinates_min[i] domain_length for i in 2:NDIMS)
143
throw(ArgumentError("The TreeMesh domain must be a hypercube (provided `coordinates_max` .- `coordinates_min` = $(coordinates_max .- coordinates_min))"))
144
end
145
146
# TODO: MPI, create nice interface for a parallel tree/mesh
147
if mpi_isparallel()
148
if mpi_isroot() && NDIMS != 2
149
println(stderr,
150
"ERROR: The TreeMesh supports parallel execution with MPI only in 2 dimensions")
151
MPI.Abort(mpi_comm(), 1)
152
end
153
TreeType = ParallelTree{NDIMS, RealT}
154
else
155
TreeType = SerialTree{NDIMS, RealT}
156
end
157
158
# Create mesh
159
mesh = @trixi_timeit timer() "creation" TreeMesh{NDIMS, TreeType, RealT}(n_cells_max,
160
domain_center,
161
domain_length,
162
periodicity)
163
164
# Initialize mesh
165
initialize!(mesh, initial_refinement_level, refinement_patches, coarsening_patches)
166
167
return mesh
168
end
169
170
function initialize!(mesh::TreeMesh, initial_refinement_level,
171
refinement_patches, coarsening_patches)
172
# Create initial refinement
173
@trixi_timeit timer() "initial refinement" refine_uniformly!(mesh.tree,
174
initial_refinement_level)
175
176
# Apply refinement patches
177
@trixi_timeit timer() "refinement patches" for patch in refinement_patches
178
# TODO: Taal refactor, use multiple dispatch?
179
if patch.type == "box"
180
refine_box!(mesh.tree, patch.coordinates_min, patch.coordinates_max)
181
elseif patch.type == "sphere"
182
refine_sphere!(mesh.tree, patch.center, patch.radius)
183
else
184
error("unknown refinement patch type '$(patch.type)'")
185
end
186
end
187
188
# Apply coarsening patches
189
@trixi_timeit timer() "coarsening patches" for patch in coarsening_patches
190
# TODO: Taal refactor, use multiple dispatch
191
if patch.type == "box"
192
coarsen_box!(mesh.tree, patch.coordinates_min, patch.coordinates_max)
193
else
194
error("unknown coarsening patch type '$(patch.type)'")
195
end
196
end
197
198
# Partition the mesh among multiple MPI ranks (does nothing if run in serial)
199
partition!(mesh)
200
201
return nothing
202
end
203
204
function TreeMesh(coordinates_min::Real, coordinates_max::Real;
205
kwargs...)
206
TreeMesh((coordinates_min,), (coordinates_max,); kwargs...)
207
end
208
209
function Base.show(io::IO, mesh::TreeMesh{NDIMS, TreeType}) where {NDIMS, TreeType}
210
print(io, "TreeMesh{", NDIMS, ", ", TreeType, "} with length ", mesh.tree.length)
211
end
212
213
function Base.show(io::IO, ::MIME"text/plain",
214
mesh::TreeMesh{NDIMS, TreeType}) where {NDIMS, TreeType}
215
if get(io, :compact, false)
216
show(io, mesh)
217
else
218
setup = [
219
"center" => mesh.tree.center_level_0,
220
"length" => mesh.tree.length_level_0,
221
"periodicity" => mesh.tree.periodicity,
222
"current #cells" => mesh.tree.length,
223
"#leaf-cells" => count_leaf_cells(mesh.tree),
224
"maximum #cells" => mesh.tree.capacity
225
]
226
summary_box(io, "TreeMesh{" * string(NDIMS) * ", " * string(TreeType) * "}",
227
setup)
228
end
229
end
230
231
@inline Base.ndims(mesh::TreeMesh) = ndims(mesh.tree)
232
233
# Obtain the mesh filename from a restart file
234
function get_restart_mesh_filename(restart_filename, mpi_parallel::False)
235
# Get directory name
236
dirname, _ = splitdir(restart_filename)
237
238
# Read mesh filename from restart file
239
mesh_file = ""
240
h5open(restart_filename, "r") do file
241
mesh_file = read(attributes(file)["mesh_file"])
242
end
243
244
# Construct and return filename
245
return joinpath(dirname, mesh_file)
246
end
247
248
function total_volume(mesh::TreeMesh)
249
return mesh.tree.length_level_0^ndims(mesh)
250
end
251
252
isperiodic(mesh::TreeMesh) = isperiodic(mesh.tree)
253
isperiodic(mesh::TreeMesh, dimension) = isperiodic(mesh.tree, dimension)
254
255
Base.real(::TreeMesh{NDIMS, TreeType, RealT}) where {NDIMS, TreeType, RealT} = RealT
256
257
include("parallel_tree_mesh.jl")
258
end # @muladd
259
260