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
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
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
# These are needed for distributed memory (i.e., MPI) parallelization
33
first_cell_by_rank::OffsetVector{Int, Vector{Int}}
34
n_cells_by_rank::OffsetVector{Int, Vector{Int}}
35
36
function TreeMesh{NDIMS, TreeType, RealT}(n_cells_max::Integer) where {NDIMS,
37
TreeType <:
38
AbstractTree{NDIMS},
39
RealT <:
40
Real}
41
tree = TreeType(n_cells_max)
42
current_filename = ""
43
unsaved_changes = true
44
first_cell_by_rank = OffsetVector(Int[], 0)
45
n_cells_by_rank = OffsetVector(Int[], 0)
46
47
return new(tree, current_filename, unsaved_changes,
48
first_cell_by_rank, n_cells_by_rank)
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 = false) where {NDIMS,
56
TreeType <:
57
AbstractTree{NDIMS},
58
RealT <: Real}
59
tree = TreeType(n_cells_max, domain_center, domain_length, periodicity)
60
current_filename = ""
61
unsaved_changes = true
62
first_cell_by_rank = OffsetVector(Int[], 0)
63
n_cells_by_rank = OffsetVector(Int[], 0)
64
65
return new(tree, current_filename, unsaved_changes,
66
first_cell_by_rank, n_cells_by_rank)
67
end
68
end
69
70
const TreeMesh1D = TreeMesh{1, TreeType} where {TreeType <: AbstractTree{1}}
71
const TreeMesh2D = TreeMesh{2, TreeType} where {TreeType <: AbstractTree{2}}
72
const TreeMesh3D = TreeMesh{3, TreeType} where {TreeType <: AbstractTree{3}}
73
74
const TreeMeshSerial{NDIMS} = TreeMesh{NDIMS, <:SerialTree{NDIMS}}
75
const TreeMeshParallel{NDIMS} = TreeMesh{NDIMS, <:ParallelTree{NDIMS}}
76
77
@inline mpi_parallel(mesh::TreeMeshSerial) = False()
78
@inline mpi_parallel(mesh::TreeMeshParallel) = True()
79
80
partition!(mesh::TreeMeshSerial) = nothing
81
82
# Constructor for passing the dimension and mesh type as an argument
83
function TreeMesh(::Type{TreeType}, args...;
84
RealT = Float64) where {NDIMS, TreeType <: AbstractTree{NDIMS}}
85
return TreeMesh{NDIMS, TreeType, RealT}(args...)
86
end
87
88
# Constructor accepting a single number as center (as opposed to an array) for 1D
89
function TreeMesh{1, TreeType, RealT}(n::Int, center::RealT, len::RealT,
90
periodicity = false) where {
91
TreeType <:
92
AbstractTree{1},
93
RealT <: Real}
94
return TreeMesh{1, TreeType, RealT}(n, SVector{1, RealT}(center), len, periodicity)
95
end
96
97
function TreeMesh{NDIMS, TreeType, RealT}(n_cells_max::Integer,
98
domain_center::NTuple{NDIMS, RealT},
99
domain_length::RealT,
100
periodicity = false) where {NDIMS,
101
TreeType <:
102
AbstractTree{NDIMS},
103
RealT <: Real}
104
return TreeMesh{NDIMS, TreeType, RealT}(n_cells_max,
105
SVector{NDIMS, RealT}(domain_center),
106
domain_length, periodicity)
107
end
108
109
"""
110
TreeMesh(coordinates_min::NTuple{NDIMS, Real},
111
coordinates_max::NTuple{NDIMS, Real};
112
n_cells_max,
113
periodicity = false,
114
initial_refinement_level,
115
refinement_patches = (),
116
coarsening_patches = (),
117
RealT = Float64) where {NDIMS}
118
119
Create a `TreeMesh` in `NDIMS` dimensions with real type `RealT` covering the domain defined by
120
`coordinates_min` and `coordinates_max`. The mesh is initialized with a uniform
121
refinement to the specified `initial_refinement_level`. Further refinement and
122
coarsening patches can be specified using `refinement_patches` and
123
`coarsening_patches`, respectively. The maximum number of cells allowed in the mesh is
124
given by `n_cells_max`. The periodicity in each dimension can be specified using the
125
`periodicity` argument (default: non-periodic in all dimensions). If it is a single
126
`Bool`, the same periodicity is applied in all dimensions; otherwise, a tuple of
127
`Bool`s of length `NDIMS` must be provided. Note that the domain must be a hypercube, i.e.,
128
all dimensions must have the same length.
129
"""
130
function TreeMesh(coordinates_min::NTuple{NDIMS, Real},
131
coordinates_max::NTuple{NDIMS, Real};
132
n_cells_max,
133
periodicity = false,
134
initial_refinement_level,
135
refinement_patches = (),
136
coarsening_patches = (),
137
RealT = Float64) where {NDIMS}
138
# check arguments
139
if !(n_cells_max isa Integer && n_cells_max > 0)
140
throw(ArgumentError("`n_cells_max` must be a positive integer (provided `n_cells_max = $n_cells_max`)"))
141
end
142
if !(initial_refinement_level isa Integer && initial_refinement_level >= 0)
143
throw(ArgumentError("`initial_refinement_level` must be a non-negative integer (provided `initial_refinement_level = $initial_refinement_level`)"))
144
end
145
146
# Check if elements in coordinates_min and coordinates_max are all of type RealT
147
for i in 1:NDIMS
148
if !(coordinates_min[i] isa RealT)
149
@warn "Element $i in `coordinates_min` is not of type $RealT (provided `coordinates_min[$i] = $(coordinates_min[i])`)"
150
end
151
if !(coordinates_max[i] isa RealT)
152
@warn "Element $i in `coordinates_max` is not of type $RealT (provided `coordinates_max[$i] = $(coordinates_max[i])`)"
153
end
154
end
155
156
coordinates_min_max_check(coordinates_min, coordinates_max)
157
158
# TreeMesh requires equal domain lengths in all dimensions
159
domain_center = @. convert(RealT, (coordinates_min + coordinates_max) / 2)
160
domain_length = convert(RealT, coordinates_max[1] - coordinates_min[1])
161
if !all(coordinates_max[i] - coordinates_min[i] domain_length for i in 2:NDIMS)
162
throw(ArgumentError("The TreeMesh domain must be a hypercube (provided `coordinates_max` .- `coordinates_min` = $(coordinates_max .- coordinates_min))"))
163
end
164
165
# TODO: MPI, create nice interface for a parallel tree/mesh
166
if mpi_isparallel()
167
if mpi_isroot() && NDIMS != 2
168
println(stderr,
169
"ERROR: The TreeMesh supports parallel execution with MPI only in 2 dimensions")
170
MPI.Abort(mpi_comm(), 1)
171
end
172
TreeType = ParallelTree{NDIMS, RealT}
173
else
174
TreeType = SerialTree{NDIMS, RealT}
175
end
176
177
# Create mesh
178
mesh = @trixi_timeit timer() "creation" TreeMesh{NDIMS, TreeType, RealT}(n_cells_max,
179
domain_center,
180
domain_length,
181
periodicity)
182
183
# Initialize mesh
184
initialize!(mesh, initial_refinement_level, refinement_patches, coarsening_patches)
185
186
return mesh
187
end
188
189
function initialize!(mesh::TreeMesh, initial_refinement_level,
190
refinement_patches, coarsening_patches)
191
# Create initial refinement
192
@trixi_timeit timer() "initial refinement" refine_uniformly!(mesh.tree,
193
initial_refinement_level)
194
195
# Apply refinement patches
196
@trixi_timeit timer() "refinement patches" for patch in refinement_patches
197
# TODO: Taal refactor, use multiple dispatch?
198
if patch.type == "box"
199
refine_box!(mesh.tree, patch.coordinates_min, patch.coordinates_max)
200
elseif patch.type == "sphere"
201
refine_sphere!(mesh.tree, patch.center, patch.radius)
202
else
203
error("unknown refinement patch type '$(patch.type)'")
204
end
205
end
206
207
# Apply coarsening patches
208
@trixi_timeit timer() "coarsening patches" for patch in coarsening_patches
209
# TODO: Taal refactor, use multiple dispatch
210
if patch.type == "box"
211
coarsen_box!(mesh.tree, patch.coordinates_min, patch.coordinates_max)
212
else
213
error("unknown coarsening patch type '$(patch.type)'")
214
end
215
end
216
217
# Partition the mesh among multiple MPI ranks (does nothing if run in serial)
218
partition!(mesh)
219
220
return nothing
221
end
222
223
function TreeMesh(coordinates_min::Real, coordinates_max::Real;
224
kwargs...)
225
return TreeMesh((coordinates_min,), (coordinates_max,); kwargs...)
226
end
227
228
function Base.show(io::IO, mesh::TreeMesh{NDIMS, TreeType}) where {NDIMS, TreeType}
229
print(io, "TreeMesh{", NDIMS, ", ", TreeType, "} with length ", mesh.tree.length)
230
return nothing
231
end
232
233
function Base.show(io::IO, ::MIME"text/plain",
234
mesh::TreeMesh{NDIMS, TreeType}) where {NDIMS, TreeType}
235
if get(io, :compact, false)
236
show(io, mesh)
237
else
238
setup = [
239
"center" => mesh.tree.center_level_0,
240
"length" => mesh.tree.length_level_0,
241
"periodicity" => mesh.tree.periodicity,
242
"current #cells" => mesh.tree.length,
243
"#leaf-cells" => count_leaf_cells(mesh.tree),
244
"maximum #cells" => mesh.tree.capacity
245
]
246
summary_box(io, "TreeMesh{" * string(NDIMS) * ", " * string(TreeType) * "}",
247
setup)
248
end
249
end
250
251
@inline Base.ndims(mesh::TreeMesh) = ndims(mesh.tree)
252
253
# Obtain the mesh filename from a restart file
254
function get_restart_mesh_filename(restart_filename, mpi_parallel::False)
255
# Get directory name
256
dirname, _ = splitdir(restart_filename)
257
258
# Read mesh filename from restart file
259
mesh_file = ""
260
h5open(restart_filename, "r") do file
261
mesh_file = read(attributes(file)["mesh_file"])
262
return nothing
263
end
264
265
# Construct and return filename
266
return joinpath(dirname, mesh_file)
267
end
268
269
function total_volume(mesh::TreeMesh)
270
return mesh.tree.length_level_0^ndims(mesh)
271
end
272
273
isperiodic(mesh::TreeMesh) = isperiodic(mesh.tree)
274
isperiodic(mesh::TreeMesh, dimension) = isperiodic(mesh.tree, dimension)
275
276
Base.real(::TreeMesh{NDIMS, TreeType, RealT}) where {NDIMS, TreeType, RealT} = RealT
277
278
@inline ncells(mesh::TreeMesh) = length(local_leaf_cells(mesh.tree))
279
280
include("parallel_tree_mesh.jl")
281
end # @muladd
282
283