Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/callbacks_step/amr_dg1d.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
# Refine elements in the DG solver based on a list of cell_ids that should be refined
9
function refine!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1},
10
equations, dg::DGSEM, cache, elements_to_refine)
11
# Return early if there is nothing to do
12
if isempty(elements_to_refine)
13
return
14
end
15
16
# Determine for each existing element whether it needs to be refined
17
needs_refinement = falses(nelements(dg, cache))
18
needs_refinement[elements_to_refine] .= true
19
20
# Retain current solution data
21
old_n_elements = nelements(dg, cache)
22
old_u_ode = copy(u_ode)
23
GC.@preserve old_u_ode begin # OBS! If we don't GC.@preserve old_u_ode, it might be GC'ed
24
old_u = wrap_array(old_u_ode, mesh, equations, dg, cache)
25
26
# Get new list of leaf cells
27
leaf_cell_ids = local_leaf_cells(mesh.tree)
28
29
# re-initialize elements container
30
@unpack elements = cache
31
resize!(elements, length(leaf_cell_ids))
32
init_elements!(elements, leaf_cell_ids, mesh, dg.basis)
33
@assert nelements(dg, cache) > old_n_elements
34
35
resize!(u_ode,
36
nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache))
37
u = wrap_array(u_ode, mesh, equations, dg, cache)
38
39
# Loop over all elements in old container and either copy them or refine them
40
element_id = 1
41
for old_element_id in 1:old_n_elements
42
if needs_refinement[old_element_id]
43
# Refine element and store solution directly in new data structure
44
refine_element!(u, element_id, old_u, old_element_id,
45
adaptor, equations, dg)
46
element_id += 2^ndims(mesh)
47
else
48
# Copy old element data to new element container
49
@views u[:, .., element_id] .= old_u[:, .., old_element_id]
50
element_id += 1
51
end
52
end
53
# If everything is correct, we should have processed all elements.
54
# Depending on whether the last element processed above had to be refined or not,
55
# the counter `element_id` can have two different values at the end.
56
@assert element_id ==
57
nelements(dg, cache) +
58
1||element_id == nelements(dg, cache) + 2^ndims(mesh) "element_id = $element_id, nelements(dg, cache) = $(nelements(dg, cache))"
59
end # GC.@preserve old_u_ode
60
61
# re-initialize interfaces container
62
@unpack interfaces = cache
63
resize!(interfaces, count_required_interfaces(mesh, leaf_cell_ids))
64
init_interfaces!(interfaces, elements, mesh)
65
66
# re-initialize boundaries container
67
@unpack boundaries = cache
68
resize!(boundaries, count_required_boundaries(mesh, leaf_cell_ids))
69
init_boundaries!(boundaries, elements, mesh)
70
71
# Sanity check
72
if isperiodic(mesh.tree)
73
@assert ninterfaces(interfaces)==1 * nelements(dg, cache) ("For 1D and periodic domains, the number of interfaces must be the same as the number of elements")
74
end
75
76
return nothing
77
end
78
79
function refine!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1},
80
equations, dg::DGSEM, cache, cache_parabolic,
81
elements_to_refine)
82
# Call `refine!` for the hyperbolic part, which does the heavy lifting of
83
# actually transferring the solution to the refined cells
84
refine!(u_ode, adaptor, mesh, equations, dg, cache, elements_to_refine)
85
86
# Resize parabolic helper variables
87
@unpack viscous_container = cache_parabolic
88
resize!(viscous_container, equations, dg, cache)
89
reinitialize_containers!(mesh, equations, dg, cache_parabolic)
90
91
# Sanity check
92
@unpack interfaces = cache_parabolic
93
if isperiodic(mesh.tree)
94
@assert ninterfaces(interfaces)==1 * nelements(dg, cache_parabolic) ("For 1D and periodic domains, the number of interfaces must be the same as the number of elements")
95
end
96
97
return nothing
98
end
99
100
# TODO: Taal compare performance of different implementations
101
# Refine solution data u for an element, using L2 projection (interpolation)
102
function refine_element!(u::AbstractArray{<:Any, 3}, element_id,
103
old_u, old_element_id,
104
adaptor::LobattoLegendreAdaptorL2, equations, dg)
105
@unpack forward_upper, forward_lower = adaptor
106
107
# Store new element ids
108
left_id = element_id
109
right_id = element_id + 1
110
111
@boundscheck begin
112
@assert old_element_id >= 1
113
@assert size(old_u, 1) == nvariables(equations)
114
@assert size(old_u, 2) == nnodes(dg)
115
@assert size(old_u, 3) >= old_element_id
116
@assert element_id >= 1
117
@assert size(u, 1) == nvariables(equations)
118
@assert size(u, 2) == nnodes(dg)
119
@assert size(u, 3) >= element_id + 1
120
end
121
122
# Interpolate to left element
123
for i in eachnode(dg)
124
acc = zero(get_node_vars(u, equations, dg, i, element_id))
125
for k in eachnode(dg)
126
acc += get_node_vars(old_u, equations, dg, k, old_element_id) *
127
forward_lower[i, k]
128
end
129
set_node_vars!(u, acc, equations, dg, i, left_id)
130
end
131
132
# Interpolate to right element
133
for i in eachnode(dg)
134
acc = zero(get_node_vars(u, equations, dg, i, element_id))
135
for k in eachnode(dg)
136
acc += get_node_vars(old_u, equations, dg, k, old_element_id) *
137
forward_upper[i, k]
138
end
139
set_node_vars!(u, acc, equations, dg, i, right_id)
140
end
141
142
return nothing
143
end
144
145
# Coarsen elements in the DG solver based on a list of cell_ids that should be removed
146
function coarsen!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1},
147
equations, dg::DGSEM, cache, elements_to_remove)
148
# Return early if there is nothing to do
149
if isempty(elements_to_remove)
150
return
151
end
152
153
# Determine for each old element whether it needs to be removed
154
to_be_removed = falses(nelements(dg, cache))
155
to_be_removed[elements_to_remove] .= true
156
157
# Retain current solution data
158
old_n_elements = nelements(dg, cache)
159
old_u_ode = copy(u_ode)
160
GC.@preserve old_u_ode begin # OBS! If we don't GC.@preserve old_u_ode, it might be GC'ed
161
old_u = wrap_array(old_u_ode, mesh, equations, dg, cache)
162
163
# Get new list of leaf cells
164
leaf_cell_ids = local_leaf_cells(mesh.tree)
165
166
# re-initialize elements container
167
@unpack elements = cache
168
resize!(elements, length(leaf_cell_ids))
169
init_elements!(elements, leaf_cell_ids, mesh, dg.basis)
170
@assert nelements(dg, cache) < old_n_elements
171
172
resize!(u_ode,
173
nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache))
174
u = wrap_array(u_ode, mesh, equations, dg, cache)
175
176
# Loop over all elements in old container and either copy them or coarsen them
177
skip = 0
178
element_id = 1
179
for old_element_id in 1:old_n_elements
180
# If skip is non-zero, we just coarsened 2^ndims elements and need to omit the following elements
181
if skip > 0
182
skip -= 1
183
continue
184
end
185
186
if to_be_removed[old_element_id]
187
# If an element is to be removed, sanity check if the following elements
188
# are also marked - otherwise there would be an error in the way the
189
# cells/elements are sorted
190
@assert all(to_be_removed[old_element_id:(old_element_id + 2^ndims(mesh) - 1)]) "bad cell/element order"
191
192
# Coarsen elements and store solution directly in new data structure
193
coarsen_elements!(u, element_id, old_u, old_element_id,
194
adaptor, equations, dg)
195
element_id += 1
196
skip = 2^ndims(mesh) - 1
197
else
198
# Copy old element data to new element container
199
@views u[:, .., element_id] .= old_u[:, .., old_element_id]
200
element_id += 1
201
end
202
end
203
# If everything is correct, we should have processed all elements.
204
@assert element_id==nelements(dg, cache) + 1 "element_id = $element_id, nelements(dg, cache) = $(nelements(dg, cache))"
205
end # GC.@preserve old_u_ode
206
207
# re-initialize interfaces container
208
@unpack interfaces = cache
209
resize!(interfaces, count_required_interfaces(mesh, leaf_cell_ids))
210
init_interfaces!(interfaces, elements, mesh)
211
212
# re-initialize boundaries container
213
@unpack boundaries = cache
214
resize!(boundaries, count_required_boundaries(mesh, leaf_cell_ids))
215
init_boundaries!(boundaries, elements, mesh)
216
217
# Sanity check
218
if isperiodic(mesh.tree)
219
@assert ninterfaces(interfaces)==1 * nelements(dg, cache) ("For 1D and periodic domains, the number of interfaces must be the same as the number of elements")
220
end
221
222
return nothing
223
end
224
225
function coarsen!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1},
226
equations, dg::DGSEM, cache, cache_parabolic,
227
elements_to_remove)
228
# Call `coarsen!` for the hyperbolic part, which does the heavy lifting of
229
# actually transferring the solution to the coarsened cells
230
coarsen!(u_ode, adaptor, mesh, equations, dg, cache, elements_to_remove)
231
232
# Resize parabolic helper variables
233
@unpack viscous_container = cache_parabolic
234
resize!(viscous_container, equations, dg, cache)
235
reinitialize_containers!(mesh, equations, dg, cache_parabolic)
236
237
# Sanity check
238
@unpack interfaces = cache_parabolic
239
if isperiodic(mesh.tree)
240
@assert ninterfaces(interfaces)==1 * nelements(dg, cache_parabolic) ("For 1D and periodic domains, the number of interfaces must be the same as the number of elements")
241
end
242
243
return nothing
244
end
245
246
# TODO: Taal compare performance of different implementations
247
# Coarsen solution data u for two elements, using L2 projection
248
function coarsen_elements!(u::AbstractArray{<:Any, 3}, element_id,
249
old_u, old_element_id,
250
adaptor::LobattoLegendreAdaptorL2, equations, dg)
251
@unpack reverse_upper, reverse_lower = adaptor
252
253
# Store old element ids
254
left_id = old_element_id
255
right_id = old_element_id + 1
256
257
@boundscheck begin
258
@assert old_element_id >= 1
259
@assert size(old_u, 1) == nvariables(equations)
260
@assert size(old_u, 2) == nnodes(dg)
261
@assert size(old_u, 3) >= old_element_id + 1
262
@assert element_id >= 1
263
@assert size(u, 1) == nvariables(equations)
264
@assert size(u, 2) == nnodes(dg)
265
@assert size(u, 3) >= element_id
266
end
267
268
for i in eachnode(dg)
269
acc = zero(get_node_vars(u, equations, dg, i, element_id))
270
271
# Project from lower left element
272
for k in eachnode(dg)
273
acc += get_node_vars(old_u, equations, dg, k, left_id) * reverse_lower[i, k]
274
end
275
276
# Project from lower right element
277
for k in eachnode(dg)
278
acc += get_node_vars(old_u, equations, dg, k, right_id) *
279
reverse_upper[i, k]
280
end
281
282
# Update value
283
set_node_vars!(u, acc, equations, dg, i, element_id)
284
end
285
286
return nothing
287
end
288
end # @muladd
289
290