Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/meshes/serial_tree.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
# Composite type that represents a NDIMS-dimensional tree (serial version).
9
#
10
# Implements everything required for AbstractContainer.
11
#
12
# Note: The way the data structures are set up and the way most algorithms
13
# work, it is *always* assumed that
14
# a) we have a balanced tree (= at most one level difference between
15
# neighboring cells, or 2:1 rule)
16
# b) we may not have all children (= some children may not exist)
17
# c) the tree is stored depth-first
18
#
19
# However, the way the refinement/coarsening algorithms are currently
20
# implemented, we only have fully refined cells. That is, a cell either has 2^NDIMS children or
21
# no children at all (= leaf cell). This restriction is also assumed at
22
# multiple positions in the refinement/coarsening algorithms.
23
#
24
# An exception to the 2:1 rule exists for the low-level `refine_unbalanced!`
25
# function, which is required for implementing level-wise refinement in a sane
26
# way. Also, depth-first ordering *might* not by guaranteed during
27
# refinement/coarsening operations.
28
mutable struct SerialTree{NDIMS, RealT <: Real} <: AbstractTree{NDIMS}
29
const capacity::Int
30
length::Int
31
32
parent_ids::Vector{Int}
33
child_ids::Matrix{Int}
34
neighbor_ids::Matrix{Int}
35
levels::Vector{Int}
36
coordinates::Matrix{RealT}
37
original_cell_ids::Vector{Int}
38
39
center_level_0::SVector{NDIMS, RealT}
40
length_level_0::RealT
41
periodicity::NTuple{NDIMS, Bool}
42
43
function SerialTree{NDIMS, RealT}(capacity::Integer) where {NDIMS, RealT <: Real}
44
@assert NDIMS isa Integer
45
46
parent_ids = fill(typemin(Int), capacity + 1)
47
child_ids = fill(typemin(Int), 2^NDIMS, capacity + 1)
48
neighbor_ids = fill(typemin(Int), 2 * NDIMS, capacity + 1)
49
levels = fill(typemin(Int), capacity + 1)
50
coordinates = fill(convert(RealT, NaN), NDIMS, capacity + 1)
51
original_cell_ids = fill(typemin(Int), capacity + 1)
52
53
center_level_0 = SVector(ntuple(_ -> convert(RealT, NaN), NDIMS))
54
length_level_0 = convert(RealT, NaN)
55
periodicity = ntuple(_ -> false, NDIMS)
56
57
return new(capacity, 0, # length
58
parent_ids, child_ids, neighbor_ids,
59
levels, coordinates, original_cell_ids,
60
center_level_0, length_level_0,
61
periodicity)
62
end
63
end
64
65
# Constructor for passing the dimension as an argument. Default datatype: Float64
66
SerialTree(::Val{NDIMS}, args...) where {NDIMS} = SerialTree{NDIMS, Float64}(args...)
67
68
# Create and initialize tree
69
function SerialTree{NDIMS, RealT}(capacity::Int, center::AbstractArray{RealT},
70
length::RealT,
71
periodicity = false) where {NDIMS, RealT <: Real}
72
# Create instance
73
t = SerialTree{NDIMS, RealT}(capacity)
74
75
# Initialize root cell
76
init!(t, center, length, periodicity)
77
78
return t
79
end
80
81
# Constructors accepting a single number as center (as opposed to an array) for 1D
82
function SerialTree{1, RealT}(cap::Int, center::RealT, len::RealT,
83
periodicity = false) where {RealT <: Real}
84
return SerialTree{1, RealT}(cap, [center], len, periodicity)
85
end
86
function SerialTree{1}(cap::Int, center::RealT, len::RealT,
87
periodicity = false) where {RealT <: Real}
88
return SerialTree{1, RealT}(cap, [center], len, periodicity)
89
end
90
91
# Clear tree with deleting data structures, store center and length, and create root cell
92
function init!(t::SerialTree, center::AbstractArray{RealT}, length::RealT,
93
periodicity = false) where {RealT}
94
clear!(t)
95
96
# Set domain information
97
t.center_level_0 = center
98
t.length_level_0 = length
99
100
# Create root cell
101
t.length += 1
102
t.parent_ids[1] = 0
103
t.child_ids[:, 1] .= 0
104
t.levels[1] = 0
105
set_cell_coordinates!(t, t.center_level_0, 1)
106
t.original_cell_ids[1] = 0
107
108
# Set neighbor ids: for each periodic direction, the level-0 cell is its own neighbor
109
if all(periodicity)
110
# Also catches case where periodicity = true
111
t.neighbor_ids[:, 1] .= 1
112
t.periodicity = ntuple(x -> true, ndims(t))
113
elseif !any(periodicity)
114
# Also catches case where periodicity = false
115
t.neighbor_ids[:, 1] .= 0
116
t.periodicity = ntuple(x -> false, ndims(t))
117
else
118
# Default case if periodicity is an iterable
119
for dimension in 1:ndims(t)
120
if periodicity[dimension]
121
t.neighbor_ids[2 * dimension - 1, 1] = 1
122
t.neighbor_ids[2 * dimension - 0, 1] = 1
123
else
124
t.neighbor_ids[2 * dimension - 1, 1] = 0
125
t.neighbor_ids[2 * dimension - 0, 1] = 0
126
end
127
end
128
129
t.periodicity = Tuple(periodicity)
130
end
131
end
132
133
# Convenience output for debugging
134
function Base.show(io::IO, ::MIME"text/plain", t::SerialTree)
135
@nospecialize t # reduce precompilation time
136
137
l = t.length
138
println(io, '*'^20)
139
println(io, "t.parent_ids[1:l] = $(t.parent_ids[1:l])")
140
println(io, "transpose(t.child_ids[:, 1:l]) = $(transpose(t.child_ids[:, 1:l]))")
141
println(io,
142
"transpose(t.neighbor_ids[:, 1:l]) = $(transpose(t.neighbor_ids[:, 1:l]))")
143
println(io, "t.levels[1:l] = $(t.levels[1:l])")
144
println(io,
145
"transpose(t.coordinates[:, 1:l]) = $(transpose(t.coordinates[:, 1:l]))")
146
println(io, "t.original_cell_ids[1:l] = $(t.original_cell_ids[1:l])")
147
println(io, "t.capacity = $(t.capacity)")
148
println(io, "t.length = $(t.length)")
149
println(io, "t.center_level_0 = $(t.center_level_0)")
150
println(io, "t.length_level_0 = $(t.length_level_0)")
151
println(io, '*'^20)
152
return nothing
153
end
154
155
# Set information for child cell `child_id` based on parent cell `cell_id` (except neighbors)
156
function init_child!(t::SerialTree, cell_id, child, child_id)
157
t.parent_ids[child_id] = cell_id
158
t.child_ids[child, cell_id] = child_id
159
t.child_ids[:, child_id] .= 0
160
t.levels[child_id] = t.levels[cell_id] + 1
161
set_cell_coordinates!(t,
162
child_coordinates(t, cell_coordinates(t, cell_id),
163
length_at_cell(t, cell_id), child),
164
child_id)
165
t.original_cell_ids[child_id] = 0
166
167
return nothing
168
end
169
170
# Reset range of cells to values that are prone to cause errors as soon as they are used.
171
#
172
# Rationale: If an invalid cell is accidentally used, we want to know it as soon as possible.
173
function invalidate!(t::SerialTree{NDIMS, RealT},
174
first::Int, last::Int) where {NDIMS, RealT <: Real}
175
@assert first > 0
176
@assert last <= t.capacity + 1
177
178
# Integer values are set to smallest negative value, floating point values to NaN
179
t.parent_ids[first:last] .= typemin(Int)
180
t.child_ids[:, first:last] .= typemin(Int)
181
t.neighbor_ids[:, first:last] .= typemin(Int)
182
t.levels[first:last] .= typemin(Int)
183
t.coordinates[:, first:last] .= convert(RealT, NaN) # `NaN` is of type Float64
184
t.original_cell_ids[first:last] .= typemin(Int)
185
186
return nothing
187
end
188
189
# Raw copy operation for ranges of cells.
190
#
191
# This method is used by the higher-level copy operations for AbstractContainer
192
function raw_copy!(target::SerialTree, source::SerialTree, first::Int, last::Int,
193
destination::Int)
194
copy_data!(target.parent_ids, source.parent_ids, first, last, destination)
195
copy_data!(target.child_ids, source.child_ids, first, last, destination,
196
n_children_per_cell(target))
197
copy_data!(target.neighbor_ids, source.neighbor_ids, first, last,
198
destination, n_directions(target))
199
copy_data!(target.levels, source.levels, first, last, destination)
200
copy_data!(target.coordinates, source.coordinates, first, last, destination,
201
ndims(target))
202
return copy_data!(target.original_cell_ids, source.original_cell_ids, first, last,
203
destination)
204
end
205
206
# Reset data structures by recreating all internal storage containers and invalidating all elements
207
function reset_data_structures!(t::SerialTree{NDIMS, RealT}) where {NDIMS, RealT <:
208
Real}
209
t.parent_ids = Vector{Int}(undef, t.capacity + 1)
210
t.child_ids = Matrix{Int}(undef, 2^NDIMS, t.capacity + 1)
211
t.neighbor_ids = Matrix{Int}(undef, 2 * NDIMS, t.capacity + 1)
212
t.levels = Vector{Int}(undef, t.capacity + 1)
213
t.coordinates = Matrix{RealT}(undef, NDIMS, t.capacity + 1)
214
t.original_cell_ids = Vector{Int}(undef, t.capacity + 1)
215
216
return invalidate!(t, 1, capacity(t) + 1)
217
end
218
end # @muladd
219
220