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
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
# 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
parent_ids::Vector{Int}
30
child_ids::Matrix{Int}
31
neighbor_ids::Matrix{Int}
32
levels::Vector{Int}
33
coordinates::Matrix{RealT}
34
original_cell_ids::Vector{Int}
35
36
capacity::Int
37
length::Int
38
dummy::Int
39
40
center_level_0::SVector{NDIMS, RealT}
41
length_level_0::RealT
42
periodicity::NTuple{NDIMS, Bool}
43
44
function SerialTree{NDIMS, RealT}(capacity::Integer) where {NDIMS, RealT <: Real}
45
# Verify that NDIMS is an integer
46
@assert NDIMS isa Integer
47
48
# Create instance
49
t = new()
50
51
# Initialize fields with defaults
52
# Note: length as capacity + 1 is to use `capacity + 1` as temporary storage for swap operations
53
t.parent_ids = fill(typemin(Int), capacity + 1)
54
t.child_ids = fill(typemin(Int), 2^NDIMS, capacity + 1)
55
t.neighbor_ids = fill(typemin(Int), 2 * NDIMS, capacity + 1)
56
t.levels = fill(typemin(Int), capacity + 1)
57
t.coordinates = fill(convert(RealT, NaN), NDIMS, capacity + 1) # `NaN` is of type Float64
58
t.original_cell_ids = fill(typemin(Int), capacity + 1)
59
60
t.capacity = capacity
61
t.length = 0
62
t.dummy = capacity + 1
63
64
t.center_level_0 = SVector(ntuple(_ -> convert(RealT, NaN), NDIMS))
65
t.length_level_0 = convert(RealT, NaN)
66
67
return t
68
end
69
end
70
71
# Constructor for passing the dimension as an argument. Default datatype: Float64
72
SerialTree(::Val{NDIMS}, args...) where {NDIMS} = SerialTree{NDIMS, Float64}(args...)
73
74
# Create and initialize tree
75
function SerialTree{NDIMS, RealT}(capacity::Int, center::AbstractArray{RealT},
76
length::RealT,
77
periodicity = true) where {NDIMS, RealT <: Real}
78
# Create instance
79
t = SerialTree{NDIMS, RealT}(capacity)
80
81
# Initialize root cell
82
init!(t, center, length, periodicity)
83
84
return t
85
end
86
87
# Constructors accepting a single number as center (as opposed to an array) for 1D
88
function SerialTree{1, RealT}(cap::Int, center::RealT, len::RealT,
89
periodicity = true) where {RealT <: Real}
90
SerialTree{1, RealT}(cap, [center], len, periodicity)
91
end
92
function SerialTree{1}(cap::Int, center::RealT, len::RealT,
93
periodicity = true) where {RealT <: Real}
94
SerialTree{1, RealT}(cap, [center], len, periodicity)
95
end
96
97
# Clear tree with deleting data structures, store center and length, and create root cell
98
function init!(t::SerialTree, center::AbstractArray{RealT}, length::RealT,
99
periodicity = true) where {RealT}
100
clear!(t)
101
102
# Set domain information
103
t.center_level_0 = center
104
t.length_level_0 = length
105
106
# Create root cell
107
t.length += 1
108
t.parent_ids[1] = 0
109
t.child_ids[:, 1] .= 0
110
t.levels[1] = 0
111
set_cell_coordinates!(t, t.center_level_0, 1)
112
t.original_cell_ids[1] = 0
113
114
# Set neighbor ids: for each periodic direction, the level-0 cell is its own neighbor
115
if all(periodicity)
116
# Also catches case where periodicity = true
117
t.neighbor_ids[:, 1] .= 1
118
t.periodicity = ntuple(x -> true, ndims(t))
119
elseif !any(periodicity)
120
# Also catches case where periodicity = false
121
t.neighbor_ids[:, 1] .= 0
122
t.periodicity = ntuple(x -> false, ndims(t))
123
else
124
# Default case if periodicity is an iterable
125
for dimension in 1:ndims(t)
126
if periodicity[dimension]
127
t.neighbor_ids[2 * dimension - 1, 1] = 1
128
t.neighbor_ids[2 * dimension - 0, 1] = 1
129
else
130
t.neighbor_ids[2 * dimension - 1, 1] = 0
131
t.neighbor_ids[2 * dimension - 0, 1] = 0
132
end
133
end
134
135
t.periodicity = Tuple(periodicity)
136
end
137
end
138
139
# Convenience output for debugging
140
function Base.show(io::IO, ::MIME"text/plain", t::SerialTree)
141
@nospecialize t # reduce precompilation time
142
143
l = t.length
144
println(io, '*'^20)
145
println(io, "t.parent_ids[1:l] = $(t.parent_ids[1:l])")
146
println(io, "transpose(t.child_ids[:, 1:l]) = $(transpose(t.child_ids[:, 1:l]))")
147
println(io,
148
"transpose(t.neighbor_ids[:, 1:l]) = $(transpose(t.neighbor_ids[:, 1:l]))")
149
println(io, "t.levels[1:l] = $(t.levels[1:l])")
150
println(io,
151
"transpose(t.coordinates[:, 1:l]) = $(transpose(t.coordinates[:, 1:l]))")
152
println(io, "t.original_cell_ids[1:l] = $(t.original_cell_ids[1:l])")
153
println(io, "t.capacity = $(t.capacity)")
154
println(io, "t.length = $(t.length)")
155
println(io, "t.dummy = $(t.dummy)")
156
println(io, "t.center_level_0 = $(t.center_level_0)")
157
println(io, "t.length_level_0 = $(t.length_level_0)")
158
println(io, '*'^20)
159
end
160
161
# Set information for child cell `child_id` based on parent cell `cell_id` (except neighbors)
162
function init_child!(t::SerialTree, cell_id, child, child_id)
163
t.parent_ids[child_id] = cell_id
164
t.child_ids[child, cell_id] = child_id
165
t.child_ids[:, child_id] .= 0
166
t.levels[child_id] = t.levels[cell_id] + 1
167
set_cell_coordinates!(t,
168
child_coordinates(t, cell_coordinates(t, cell_id),
169
length_at_cell(t, cell_id), child),
170
child_id)
171
t.original_cell_ids[child_id] = 0
172
173
return nothing
174
end
175
176
# Reset range of cells to values that are prone to cause errors as soon as they are used.
177
#
178
# Rationale: If an invalid cell is accidentally used, we want to know it as soon as possible.
179
function invalidate!(t::SerialTree{NDIMS, RealT},
180
first::Int, last::Int) where {NDIMS, RealT <: Real}
181
@assert first > 0
182
@assert last <= t.capacity + 1
183
184
# Integer values are set to smallest negative value, floating point values to NaN
185
t.parent_ids[first:last] .= typemin(Int)
186
t.child_ids[:, first:last] .= typemin(Int)
187
t.neighbor_ids[:, first:last] .= typemin(Int)
188
t.levels[first:last] .= typemin(Int)
189
t.coordinates[:, first:last] .= convert(RealT, NaN) # `NaN` is of type Float64
190
t.original_cell_ids[first:last] .= typemin(Int)
191
192
return nothing
193
end
194
195
# Raw copy operation for ranges of cells.
196
#
197
# This method is used by the higher-level copy operations for AbstractContainer
198
function raw_copy!(target::SerialTree, source::SerialTree, first::Int, last::Int,
199
destination::Int)
200
copy_data!(target.parent_ids, source.parent_ids, first, last, destination)
201
copy_data!(target.child_ids, source.child_ids, first, last, destination,
202
n_children_per_cell(target))
203
copy_data!(target.neighbor_ids, source.neighbor_ids, first, last,
204
destination, n_directions(target))
205
copy_data!(target.levels, source.levels, first, last, destination)
206
copy_data!(target.coordinates, source.coordinates, first, last, destination,
207
ndims(target))
208
copy_data!(target.original_cell_ids, source.original_cell_ids, first, last,
209
destination)
210
end
211
212
# Reset data structures by recreating all internal storage containers and invalidating all elements
213
function reset_data_structures!(t::SerialTree{NDIMS, RealT}) where {NDIMS, RealT <:
214
Real}
215
t.parent_ids = Vector{Int}(undef, t.capacity + 1)
216
t.child_ids = Matrix{Int}(undef, 2^NDIMS, t.capacity + 1)
217
t.neighbor_ids = Matrix{Int}(undef, 2 * NDIMS, t.capacity + 1)
218
t.levels = Vector{Int}(undef, t.capacity + 1)
219
t.coordinates = Matrix{RealT}(undef, NDIMS, t.capacity + 1)
220
t.original_cell_ids = Vector{Int}(undef, t.capacity + 1)
221
222
invalidate!(t, 1, capacity(t) + 1)
223
end
224
end # @muladd
225
226