Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/visualization/utilities_p4est_t8code.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
#############################################################################
9
# 2D
10
11
# The P4estMesh and T8codeMesh can make use of the efficient search functionality
12
# based on the tree structure to speed up the process of finding the
13
# elements in which the curve points are located.
14
# We hard-code `Float64` here since some parts will be accessed from a Julia
15
# function called as callback from C. Thus, we cannot use multiple dispatch
16
# easily and use concrete types instead. See the comment on
17
# `SearchPointsInP4estT8codeMesh2DHelper` below.
18
# TODO: Implement the efficient search functionality also for the 2D T8codeMesh.
19
function unstructured_2d_to_1d_curve(u, mesh::P4estMesh{2, 2, Float64},
20
equations, dg::DGSEM, cache,
21
curve, slice,
22
point, nvisnodes,
23
solution_variables)
24
# TODO: Currently, the efficient search functionality is only implemented
25
# for linear meshes. If the mesh is not linear, we fall back to the naive
26
# but general approach.
27
if length(mesh.nodes) != 2
28
return unstructured_2d_to_1d_curve_general(u, mesh, equations,
29
dg, cache,
30
curve, slice,
31
point, nvisnodes,
32
solution_variables)
33
end
34
# From here on, we know that we only have to deal with a linear mesh.
35
36
# If no curve is defined, create an axis curve.
37
original_nodes = cache.elements.node_coordinates
38
if curve === nothing
39
curve = axis_curve(view(original_nodes, 1, :, :, :),
40
view(original_nodes, 2, :, :, :),
41
slice, point, nvisnodes)
42
end
43
44
# Set up data structure.
45
@assert size(curve, 1) == 2
46
n_points_curve = size(curve, 2)
47
48
# Get the number of variables after applying the transformation
49
# to solution variables.
50
u_node = get_node_vars(u, equations, dg, 1, 1, 1)
51
var_node = solution_variables(u_node, equations)
52
n_variables = length(var_node)
53
data_on_curve = Array{eltype(var_node)}(undef, n_points_curve, n_variables)
54
55
# Iterate over every point on the curve and determine the solution value at
56
# the given point.
57
# We can use the efficient search functionality of p4est/t8code to speed up
58
# the process. However, the logic is only implemented for linear meshes so far.
59
60
# Retrieve the element in which each point on the curve is located as well as
61
# the local coordinates of the point in the element.
62
data = search_points_in_p4est_t8code_mesh_2d(mesh, curve)
63
64
# We use the DG interpolation to get the solution value at the point.
65
# Thus, we first setup some data for interpolation.
66
nodes = dg.basis.nodes
67
baryweights = barycentric_weights(nodes)
68
# These Vandermonde matrices are really 1×n_nodes matrices, i.e.,
69
# row vectors. We allocate memory here to improve performance.
70
vandermonde_x = polynomial_interpolation_matrix(nodes, zero(eltype(curve)))
71
vandermonde_y = similar(vandermonde_x)
72
73
n_nodes = length(nodes)
74
temp_data = Array{eltype(data_on_curve)}(undef,
75
n_nodes,
76
n_variables)
77
unstructured_data = Array{eltype(data_on_curve)}(undef,
78
n_nodes, n_nodes,
79
n_variables)
80
81
for idx_point in eachindex(data)
82
query = data[idx_point]
83
element = query.index
84
@assert query.found
85
# The normalization in p4est/t8code is [0, 1] but we need [-1, 1] for DGSEM.
86
normalized_coordinates = 2 * SVector(query.x, query.y) .- 1
87
88
# Interpolate to a single point in each element.
89
# These Vandermonde matrices are really 1×n_nodes matrices, i.e.,
90
# row vectors.
91
polynomial_interpolation_matrix!(vandermonde_x, nodes,
92
normalized_coordinates[1], baryweights)
93
polynomial_interpolation_matrix!(vandermonde_y, nodes,
94
normalized_coordinates[2], baryweights)
95
96
# First, we transform the conserved variables `u` to the solution variables
97
# before interpolation.
98
for j in eachnode(dg), i in eachnode(dg)
99
u_node = get_node_vars(u, equations, dg, i, j, element)
100
val = solution_variables(u_node, equations)
101
for v in eachindex(val)
102
unstructured_data[i, j, v] = val[v]
103
end
104
end
105
106
# Next, we interpolate the solution variables to the located point.
107
for v in 1:n_variables
108
for i in 1:n_nodes
109
res_i = zero(eltype(temp_data))
110
for n in 1:n_nodes
111
res_i = res_i + vandermonde_y[n] * unstructured_data[i, n, v]
112
end
113
temp_data[i, v] = res_i
114
end
115
res_v = zero(eltype(data_on_curve))
116
for n in 1:n_nodes
117
res_v = res_v + vandermonde_x[n] * temp_data[n, v]
118
end
119
data_on_curve[idx_point, v] = res_v
120
end
121
end
122
123
mesh_vertices_x = nothing
124
return calc_arc_length(curve), data_on_curve, mesh_vertices_x
125
end
126
127
# This struct collects a point on the curve and the corresponding element index
128
# We hard-code `Float64` here since these structs will be accessed from a Julia
129
# function called as callback from C. Thus, we cannot use multiple dispatch
130
# easily and use concrete types instead.
131
struct SearchPointsInP4estT8codeMesh2DHelper
132
x::Float64
133
y::Float64
134
index::Int64
135
found::Bool
136
end
137
138
#############################################################################
139
# Code specialized to the P4estMesh
140
function search_in_p4est_2d_quadrant_fn(p4est_ptr::Ptr{p4est_t},
141
which_tree::p4est_topidx_t,
142
quadrant_ptr::Ptr{p4est_quadrant_t},
143
local_num::p4est_locidx_t,
144
null_pointer::Ptr{Cvoid})::Cint
145
146
# Continue the search
147
return Cint(1)
148
end
149
150
function search_in_p4est_2d_point_fn(p4est_ptr::Ptr{p4est_t},
151
which_tree::p4est_topidx_t,
152
quadrant_ptr::Ptr{p4est_quadrant_t},
153
local_num::p4est_locidx_t,
154
query_ptr::Ptr{Cvoid})::Cint
155
# Currently, we do not store the `mapping` but only the resulting
156
# `tree_node_coordinates` in the mesh. Thus, we need to use them
157
# to convert the reference coordinates of the `element` to physical
158
# coordinates (assuming polydeg == 1) and then check whether the
159
# coordinates of the points are inside the `element`.
160
161
# Get the references coordinates of the element.
162
# Note: This assumes that we are in 2D.
163
quadrant = PointerWrapper(quadrant_ptr)
164
165
level = quadrant.level[]
166
p4est_root_length = 1 << P4EST_MAXLEVEL
167
p4est_quad_length = 1 << (P4EST_MAXLEVEL - level)
168
169
x0 = quadrant.x[] / p4est_root_length
170
y0 = quadrant.y[] / p4est_root_length
171
172
x1 = x0 + p4est_quad_length / p4est_root_length
173
y1 = y0 + p4est_quad_length / p4est_root_length
174
175
r00 = SVector(x0, y0)
176
r10 = SVector(x1, y0)
177
r01 = SVector(x0, y1)
178
179
# Get the bounding physical coordinates of the tree and use them to
180
# compute the affine transformation.
181
# Note: This assumes additionally that the polynomial degree is 1.
182
p4est = PointerWrapper(p4est_ptr)
183
user_data = Ptr{Float64}(pointer(p4est.user_pointer))
184
number_of_trees = p4est.connectivity.num_trees[]
185
tree_node_coordinates = PtrArray(user_data,
186
(2, 2, 2, number_of_trees))
187
a0, A = get_affine_transformation(r00, r10, r01,
188
tree_node_coordinates,
189
which_tree)
190
191
# Load the query data
192
query = unsafe_load(Ptr{SearchPointsInP4estT8codeMesh2DHelper}(query_ptr))
193
194
# Do nothing if the point has already been found elsewhere.
195
if query.found
196
return Cint(0)
197
end
198
199
# If the point has not already been found, we check whether it is inside
200
# the parallelogram defined by the element.
201
point = SVector(query.x, query.y)
202
203
# Compute coefficients to express the `point` in the basis of the
204
# parallelogram.
205
coefficients = A \ (point - a0)
206
207
# Check if the point is inside the parallelogram.
208
tolerance = 1.0e-13
209
is_inside = -tolerance <= coefficients[1] <= 1 + tolerance &&
210
-tolerance <= coefficients[2] <= 1 + tolerance
211
212
if is_inside
213
if local_num >= 0
214
# If we are in a valid element (leaf of the tree), we store
215
# the element id and the coefficients of the point in the
216
# query data structure.
217
index = local_num + 1
218
new_query = SearchPointsInP4estT8codeMesh2DHelper(coefficients[1],
219
coefficients[2],
220
index,
221
true)
222
unsafe_store!(Ptr{SearchPointsInP4estT8codeMesh2DHelper}(query_ptr),
223
new_query)
224
end
225
226
return Cint(1)
227
else
228
return Cint(0)
229
end
230
end
231
232
@inline function get_affine_transformation(r00, r10, r01,
233
tree_node_coordinates,
234
which_tree)
235
# Get the bounding physical coordinates of the tree
236
t00 = SVector(tree_node_coordinates[1, 1, 1, which_tree + 1],
237
tree_node_coordinates[2, 1, 1, which_tree + 1])
238
t10 = SVector(tree_node_coordinates[1, 2, 1, which_tree + 1],
239
tree_node_coordinates[2, 2, 1, which_tree + 1])
240
t01 = SVector(tree_node_coordinates[1, 1, 2, which_tree + 1],
241
tree_node_coordinates[2, 1, 2, which_tree + 1])
242
243
# Transform the reference coordinates to physical coordinates.
244
# Note: This requires the same assumptions as above (linear hex mesh in 2D)
245
p00 = t00 +
246
r00[1] * (t10 - t00) +
247
r00[2] * (t01 - t00)
248
p10 = t00 +
249
r10[1] * (t10 - t00) +
250
r10[2] * (t01 - t00)
251
p01 = t00 +
252
r01[1] * (t10 - t00) +
253
r01[2] * (t01 - t00)
254
255
# Get the base point a0 and the basis vectors a1, a2 spanning the
256
# parallelogram in physical coordinates.
257
# Note: This requires the same assumptions as above.
258
a0 = p00
259
a1 = p10 - p00
260
a2 = p01 - p00
261
262
# Get the transformation matrix A to compute
263
# the coefficients of the point in the basis of the parallelogram.
264
A = SMatrix{2, 2}(a1[1], a2[1],
265
a1[2], a2[2])
266
267
return a0, A
268
end
269
270
function search_points_in_p4est_t8code_mesh_2d(mesh::P4estMesh,
271
curve::Array{Float64, 2})
272
quadrant_fn = @cfunction(search_in_p4est_2d_quadrant_fn,
273
Cint,
274
(Ptr{p4est_t}, p4est_topidx_t,
275
Ptr{p4est_quadrant_t}, p4est_locidx_t,
276
Ptr{Cvoid}))
277
point_fn = @cfunction(search_in_p4est_2d_point_fn,
278
Cint,
279
(Ptr{p4est_t}, p4est_topidx_t,
280
Ptr{p4est_quadrant_t}, p4est_locidx_t,
281
Ptr{Cvoid}))
282
283
data = Vector{SearchPointsInP4estT8codeMesh2DHelper}(undef, size(curve, 2))
284
for i in eachindex(data)
285
data[i] = SearchPointsInP4estT8codeMesh2DHelper(curve[1, i],
286
curve[2, i],
287
typemin(Int64),
288
false)
289
end
290
queries = sc_array_new_data(pointer(data),
291
sizeof(eltype(data)),
292
length(data))
293
294
call_post = 0
295
mesh.p4est.user_pointer = pointer(mesh.tree_node_coordinates)
296
p4est_search_local(pointer(mesh.p4est), call_post, quadrant_fn, point_fn,
297
queries)
298
299
return data
300
end
301
302
#############################################################################
303
# 3D
304
305
# The P4estMesh and T8codeMesh can make use of the efficient search functionality
306
# based on the tree structure to speed up the process of finding the
307
# elements in which the curve points are located.
308
# We hard-code `Float64` here since some parts will be accessed from a Julia
309
# function called as callback from C. Thus, we cannot use multiple dispatch
310
# easily and use concrete types instead. See the comment on
311
# `SearchPointsInP4estT8codeMesh3DHelper` below.
312
function unstructured_3d_to_1d_curve(u,
313
mesh::Union{P4estMesh{3, 3, Float64},
314
T8codeMesh{3, Float64}},
315
equations, dg::DGSEM, cache,
316
curve, solution_variables)
317
# TODO: Currently, the efficient search functionality is only implemented
318
# for linear meshes. If the mesh is not linear, we fall back to the naive
319
# but general approach.
320
if length(mesh.nodes) != 2
321
return unstructured_3d_to_1d_curve_general(u, equations, dg, cache,
322
curve, solution_variables)
323
end
324
# From here on, we know that we only have to deal with a linear mesh.
325
326
# Set up data structure.
327
@assert size(curve, 1) == 3
328
n_points_curve = size(curve, 2)
329
330
# Get the number of variables after applying the transformation to solution variables.
331
u_node = get_node_vars(u, equations, dg, 1, 1, 1, 1)
332
var_node = solution_variables(u_node, equations)
333
n_variables = length(var_node)
334
data_on_curve = Array{eltype(var_node)}(undef, n_points_curve, n_variables)
335
336
# Iterate over every point on the curve and determine the solution value at
337
# the given point.
338
# We can use the efficient search functionality of p4est/t8code to speed up
339
# the process. However, the logic is only implemented for linear meshes so far.
340
341
# Retrieve the element in which each point on the curve is located as well as
342
# the local coordinates of the point in the element.
343
data = search_points_in_p4est_t8code_mesh_3d(mesh, curve)
344
345
# We use the DG interpolation to get the solution value at the point.
346
# Thus, we first setup some data for interpolation.
347
nodes = dg.basis.nodes
348
baryweights = barycentric_weights(nodes)
349
# These Vandermonde matrices are really 1×n_nodes matrices, i.e.,
350
# row vectors. We allocate memory here to improve performance.
351
vandermonde_x = polynomial_interpolation_matrix(nodes, zero(eltype(curve)))
352
vandermonde_y = similar(vandermonde_x)
353
vandermonde_z = similar(vandermonde_x)
354
355
n_nodes = length(nodes)
356
temp_data = Array{eltype(data_on_curve)}(undef,
357
n_nodes, n_nodes + 1,
358
n_variables)
359
unstructured_data = Array{eltype(data_on_curve)}(undef,
360
n_nodes, n_nodes, n_nodes,
361
n_variables)
362
363
for idx_point in eachindex(data)
364
query = data[idx_point]
365
element = query.index
366
@assert query.found
367
# The normalization in p4est/t8code is [0, 1] but we need [-1, 1] for DGSEM.
368
normalized_coordinates = 2 * SVector(query.x, query.y, query.z) .- 1
369
370
# Interpolate to a single point in each element.
371
# These Vandermonde matrices are really 1×n_nodes matrices, i.e.,
372
# row vectors.
373
polynomial_interpolation_matrix!(vandermonde_x, nodes,
374
normalized_coordinates[1], baryweights)
375
polynomial_interpolation_matrix!(vandermonde_y, nodes,
376
normalized_coordinates[2], baryweights)
377
polynomial_interpolation_matrix!(vandermonde_z, nodes,
378
normalized_coordinates[3], baryweights)
379
380
# First, we transform the conserved variables `u` to the solution variables
381
# before interpolation.
382
for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
383
u_node = get_node_vars(u, equations, dg, i, j, k, element)
384
val = solution_variables(u_node, equations)
385
for v in eachindex(val)
386
unstructured_data[i, j, k, v] = val[v]
387
end
388
end
389
390
# Next, we interpolate the solution variables to the located point.
391
for v in 1:n_variables
392
for i in 1:n_nodes
393
for ii in 1:n_nodes
394
res_ii = zero(eltype(temp_data))
395
for n in 1:n_nodes
396
res_ii = res_ii +
397
vandermonde_z[n] *
398
unstructured_data[i, ii, n, v]
399
end
400
temp_data[i, ii, v] = res_ii
401
end
402
res_i = zero(eltype(temp_data))
403
for n in 1:n_nodes
404
res_i = res_i + vandermonde_y[n] * temp_data[i, n, v]
405
end
406
temp_data[i, n_nodes + 1, v] = res_i
407
end
408
res_v = zero(eltype(temp_data))
409
for n in 1:n_nodes
410
res_v = res_v + vandermonde_x[n] * temp_data[n, n_nodes + 1, v]
411
end
412
data_on_curve[idx_point, v] = res_v
413
end
414
end
415
416
mesh_vertices_x = nothing
417
return calc_arc_length(curve), data_on_curve, mesh_vertices_x
418
end
419
420
# This struct collects a point on the curve and the corresponding element index
421
# We hard-code `Float64` here since these structs will be accessed from a Julia
422
# function called as callback from C. Thus, we cannot use multiple dispatch
423
# easily and use concrete types instead.
424
struct SearchPointsInP4estT8codeMesh3DHelper
425
x::Float64
426
y::Float64
427
z::Float64
428
index::Int64
429
found::Bool
430
end
431
432
#############################################################################
433
# Code specialized to the P4estMesh
434
function search_in_p4est_3d_quadrant_fn(p4est_ptr::Ptr{p8est_t},
435
which_tree::p4est_topidx_t,
436
quadrant_ptr::Ptr{p8est_quadrant_t},
437
local_num::p4est_locidx_t,
438
null_pointer::Ptr{Cvoid})::Cint
439
440
# Continue the search
441
return Cint(1)
442
end
443
444
function search_in_p4est_3d_point_fn(p4est_ptr::Ptr{p8est_t},
445
which_tree::p4est_topidx_t,
446
quadrant_ptr::Ptr{p8est_quadrant_t},
447
local_num::p4est_locidx_t,
448
query_ptr::Ptr{Cvoid})::Cint
449
# Currently, we do not store the `mapping` but only the resulting
450
# `tree_node_coordinates` in the mesh. Thus, we need to use them
451
# to convert the reference coordinates of the `element` to physical
452
# coordinates (assuming polydeg == 1) and then check whether the
453
# coordinates of the points are inside the `element`.
454
455
# Get the references coordinates of the element.
456
# Note: This assumes that we are in 3D.
457
quadrant = PointerWrapper(quadrant_ptr)
458
459
level = quadrant.level[]
460
p4est_root_length = 1 << P4EST_MAXLEVEL
461
p4est_quad_length = 1 << (P4EST_MAXLEVEL - level)
462
463
x0 = quadrant.x[] / p4est_root_length
464
y0 = quadrant.y[] / p4est_root_length
465
z0 = quadrant.z[] / p4est_root_length
466
467
x1 = x0 + p4est_quad_length / p4est_root_length
468
y1 = y0 + p4est_quad_length / p4est_root_length
469
z1 = z0 + p4est_quad_length / p4est_root_length
470
471
r000 = SVector(x0, y0, z0)
472
r100 = SVector(x1, y0, z0)
473
r010 = SVector(x0, y1, z0)
474
r001 = SVector(x0, y0, z1)
475
476
# Get the bounding physical coordinates of the tree and use them to
477
# compute the affine transformation.
478
# Note: This assumes additionally that the polynomial degree is 1.
479
p4est = PointerWrapper(p4est_ptr)
480
user_data = Ptr{Ptr{Float64}}(pointer(p4est.user_pointer))
481
number_of_trees = p4est.connectivity.num_trees[]
482
tree_node_coordinates = PtrArray(unsafe_load(user_data, 1),
483
(3, 2, 2, 2, number_of_trees))
484
a0, A = get_affine_transformation(r000, r100, r010, r001,
485
tree_node_coordinates,
486
which_tree)
487
488
# Load the query data
489
query = unsafe_load(Ptr{SearchPointsInP4estT8codeMesh3DHelper}(query_ptr))
490
491
# Do nothing if the point has already been found elsewhere.
492
if query.found
493
return Cint(0)
494
end
495
496
# If the point has not already been found, we check whether it is inside
497
# the parallelepiped defined by the element.
498
point = SVector(query.x, query.y, query.z)
499
500
# Compute coefficients to express the `point` in the basis of the
501
# parallelepiped.
502
coefficients = A \ (point - a0)
503
504
# Check if the point is inside the parallelepiped.
505
tolerance = 1.0e-13
506
is_inside = -tolerance <= coefficients[1] <= 1 + tolerance &&
507
-tolerance <= coefficients[2] <= 1 + tolerance &&
508
-tolerance <= coefficients[3] <= 1 + tolerance
509
510
if is_inside
511
if local_num >= 0
512
# If we are in a valid element (leaf of the tree), we store
513
# the element id and the coefficients of the point in the
514
# query data structure.
515
index = local_num + 1
516
new_query = SearchPointsInP4estT8codeMesh3DHelper(coefficients[1],
517
coefficients[2],
518
coefficients[3],
519
index,
520
true)
521
unsafe_store!(Ptr{SearchPointsInP4estT8codeMesh3DHelper}(query_ptr),
522
new_query)
523
end
524
525
return Cint(1)
526
else
527
return Cint(0)
528
end
529
end
530
531
@inline function get_affine_transformation(r000, r100, r010, r001,
532
tree_node_coordinates,
533
which_tree)
534
# Get the bounding physical coordinates of the tree
535
t000 = SVector(tree_node_coordinates[1, 1, 1, 1, which_tree + 1],
536
tree_node_coordinates[2, 1, 1, 1, which_tree + 1],
537
tree_node_coordinates[3, 1, 1, 1, which_tree + 1])
538
t100 = SVector(tree_node_coordinates[1, 2, 1, 1, which_tree + 1],
539
tree_node_coordinates[2, 2, 1, 1, which_tree + 1],
540
tree_node_coordinates[3, 2, 1, 1, which_tree + 1])
541
t010 = SVector(tree_node_coordinates[1, 1, 2, 1, which_tree + 1],
542
tree_node_coordinates[2, 1, 2, 1, which_tree + 1],
543
tree_node_coordinates[3, 1, 2, 1, which_tree + 1])
544
t001 = SVector(tree_node_coordinates[1, 1, 1, 2, which_tree + 1],
545
tree_node_coordinates[2, 1, 1, 2, which_tree + 1],
546
tree_node_coordinates[3, 1, 1, 2, which_tree + 1])
547
548
# Transform the reference coordinates to physical coordinates.
549
# Note: This requires the same assumptions as above (linear hex mesh in 3D)
550
p000 = t000 +
551
r000[1] * (t100 - t000) +
552
r000[2] * (t010 - t000) +
553
r000[3] * (t001 - t000)
554
p100 = t000 +
555
r100[1] * (t100 - t000) +
556
r100[2] * (t010 - t000) +
557
r100[3] * (t001 - t000)
558
p010 = t000 +
559
r010[1] * (t100 - t000) +
560
r010[2] * (t010 - t000) +
561
r010[3] * (t001 - t000)
562
p001 = t000 +
563
r001[1] * (t100 - t000) +
564
r001[2] * (t010 - t000) +
565
r001[3] * (t001 - t000)
566
567
# Get the base point a0 and the basis vectors a1, a2, a3 spanning the
568
# parallelepiped in physical coordinates.
569
# Note: This requires the same assumptions as above.
570
a0 = p000
571
a1 = p100 - p000
572
a2 = p010 - p000
573
a3 = p001 - p000
574
575
# Get the transformation matrix A to compute
576
# the coefficients of the point in the basis of the parallelepiped.
577
A = SMatrix{3, 3}(a1[1], a2[1], a3[1],
578
a1[2], a2[2], a3[2],
579
a1[3], a2[3], a3[3])
580
581
return a0, A
582
end
583
584
function search_points_in_p4est_t8code_mesh_3d(mesh::P4estMesh,
585
curve::Array{Float64, 2})
586
quadrant_fn = @cfunction(search_in_p4est_3d_quadrant_fn,
587
Cint,
588
(Ptr{p8est_t}, p4est_topidx_t,
589
Ptr{p8est_quadrant_t}, p4est_locidx_t,
590
Ptr{Cvoid}))
591
point_fn = @cfunction(search_in_p4est_3d_point_fn,
592
Cint,
593
(Ptr{p8est_t}, p4est_topidx_t,
594
Ptr{p8est_quadrant_t}, p4est_locidx_t,
595
Ptr{Cvoid}))
596
597
data = Vector{SearchPointsInP4estT8codeMesh3DHelper}(undef, size(curve, 2))
598
for i in 1:size(curve, 2)
599
data[i] = SearchPointsInP4estT8codeMesh3DHelper(curve[1, i],
600
curve[2, i],
601
curve[3, i],
602
typemin(Int64),
603
false)
604
end
605
queries = sc_array_new_data(pointer(data),
606
sizeof(eltype(data)),
607
length(data))
608
609
temp_vertex = zeros(Float64, 3)
610
GC.@preserve temp_vertex begin
611
user_data = [pointer(mesh.tree_node_coordinates),
612
pointer(temp_vertex)]
613
614
GC.@preserve user_data begin
615
call_post = 0
616
mesh.p4est.user_pointer = pointer(user_data)
617
p8est_search_local(pointer(mesh.p4est), call_post, quadrant_fn, point_fn,
618
queries)
619
end
620
end
621
622
return data
623
end
624
625
#############################################################################
626
# Code specialized to the T8codeMesh
627
628
function search_points_in_t8code_mesh_3d_callback_element(forest::t8_forest_t,
629
ltreeid::t8_locidx_t,
630
element::Ptr{t8_element_t},
631
is_leaf::Cint,
632
leaf_elements::Ptr{t8_element_array_t},
633
tree_leaf_index::t8_locidx_t)::Cint
634
# Continue the search
635
return Cint(1)
636
end
637
638
function search_points_in_t8code_mesh_3d_callback_query(forest::t8_forest_t,
639
ltreeid::t8_locidx_t,
640
element::Ptr{t8_element_t},
641
is_leaf::Cint,
642
leaf_elements::Ptr{t8_element_array_t},
643
tree_leaf_index::t8_locidx_t,
644
queries_ptr::Ptr{sc_array_t},
645
query_indices_ptr::Ptr{sc_array_t},
646
query_matches::Ptr{Cint},
647
num_active_queries::Csize_t)::Cvoid
648
# It would be nice if we could just use
649
# t8_forest_element_points_inside(forest, ltreeid, element, coords,
650
# num_active_queries, query_matches, tolerance)
651
# However, this does not work since the coordinates of the points
652
# on the curve are given in physical coordinates but t8code only
653
# knows reference coordinates (in [0, 1]).
654
# Currently, we do not store the `mapping` but only the resulting
655
# `tree_node_coordinates` in the mesh. Thus, we need to use them
656
# to convert the reference coordinates of the `element` to physical
657
# coordinates (assuming polydeg == 1) and then check whether the
658
# coordinates of the points are inside the `element`.
659
660
# Get the references coordinates of the element.
661
# Note: This assumes that we are in 3D and that the element is a hexahedron.
662
user_data = Ptr{Ptr{Float64}}(t8_forest_get_user_data(forest))
663
vertex = PtrArray(unsafe_load(user_data, 2), (3,))
664
eclass_scheme = t8_forest_get_eclass_scheme(forest, T8_ECLASS_HEX)
665
t8_element_vertex_reference_coords(eclass_scheme, element, 0, vertex)
666
r000 = SVector(vertex[1], vertex[2], vertex[3])
667
t8_element_vertex_reference_coords(eclass_scheme, element, 1, vertex)
668
r100 = SVector(vertex[1], vertex[2], vertex[3])
669
t8_element_vertex_reference_coords(eclass_scheme, element, 2, vertex)
670
r010 = SVector(vertex[1], vertex[2], vertex[3])
671
t8_element_vertex_reference_coords(eclass_scheme, element, 4, vertex)
672
r001 = SVector(vertex[1], vertex[2], vertex[3])
673
674
# Get the bounding physical coordinates of the tree.
675
# Note: This assumes additionally that the polynomial degree is 1.
676
number_of_trees = t8_forest_get_num_global_trees(forest)
677
tree_node_coordinates = PtrArray(unsafe_load(user_data, 1),
678
(3, 2, 2, 2, number_of_trees))
679
a0, A = get_affine_transformation(r000, r100, r010, r001,
680
tree_node_coordinates,
681
ltreeid)
682
invA = inv(A)
683
684
# Loop over all points that need to be found
685
queries = PointerWrapper(queries_ptr)
686
query_indices = PointerWrapper(query_indices_ptr)
687
for i in 1:num_active_queries
688
# t8code uses 0-based indexing, we use 1-based indexing in Julia.
689
query_index = unsafe_load_sc(Csize_t, query_indices, i) + 1
690
query = unsafe_load_sc(SearchPointsInP4estT8codeMesh3DHelper, queries,
691
query_index)
692
693
# Do nothing if the point has already been found elsewhere.
694
if query.found
695
unsafe_store!(query_matches, 1, i)
696
continue
697
end
698
699
# If the point has not already been found, we check whether it is inside
700
# the parallelepiped defined by the element.
701
point = SVector(query.x, query.y, query.z)
702
703
# Compute coefficients to express the `point` in the basis of the
704
# parallelepiped.
705
coefficients = invA * (point - a0)
706
707
# Check if the point is inside the parallelepiped.
708
tolerance = 1.0e-13
709
is_inside = -tolerance <= coefficients[1] <= 1 + tolerance &&
710
-tolerance <= coefficients[2] <= 1 + tolerance &&
711
-tolerance <= coefficients[3] <= 1 + tolerance
712
713
if is_inside
714
unsafe_store!(query_matches, 1, i)
715
716
if is_leaf == 1
717
# If we are in a valid element (leaf of the tree), we store
718
# the element id and the coefficients of the point in the
719
# query data structure.
720
index = t8_forest_get_tree_element_offset(forest, ltreeid) +
721
tree_leaf_index + 1
722
new_query = SearchPointsInP4estT8codeMesh3DHelper(coefficients[1],
723
coefficients[2],
724
coefficients[3],
725
index,
726
true)
727
unsafe_store_sc!(queries, new_query, query_index)
728
end
729
else
730
unsafe_store!(query_matches, 0, i)
731
end
732
end
733
734
return nothing
735
end
736
737
function search_points_in_p4est_t8code_mesh_3d(mesh::T8codeMesh,
738
curve::Array{Float64, 2})
739
element_fn = @cfunction(search_points_in_t8code_mesh_3d_callback_element,
740
Cint,
741
(t8_forest_t, t8_locidx_t, Ptr{t8_element_t},
742
Cint, Ptr{t8_element_array_t}, t8_locidx_t))
743
query_fn = @cfunction(search_points_in_t8code_mesh_3d_callback_query,
744
Cvoid,
745
(t8_forest_t, t8_locidx_t, Ptr{t8_element_t},
746
Cint, Ptr{t8_element_array_t}, t8_locidx_t,
747
Ptr{sc_array_t}, Ptr{sc_array_t},
748
Ptr{Cint}, Csize_t))
749
750
data = Vector{SearchPointsInP4estT8codeMesh3DHelper}(undef, size(curve, 2))
751
for i in eachindex(data)
752
data[i] = SearchPointsInP4estT8codeMesh3DHelper(curve[1, i],
753
curve[2, i],
754
curve[3, i],
755
typemin(Int64),
756
false)
757
end
758
queries = sc_array_new_data(pointer(data),
759
sizeof(eltype(data)),
760
length(data))
761
762
temp_vertex = zeros(Float64, 3)
763
GC.@preserve temp_vertex begin
764
user_data = [pointer(mesh.tree_node_coordinates),
765
pointer(temp_vertex)]
766
767
GC.@preserve user_data begin
768
t8_forest_set_user_data(pointer(mesh.forest), pointer(user_data))
769
t8_forest_search(pointer(mesh.forest), element_fn, query_fn, queries)
770
end
771
end
772
773
return data
774
end
775
end # @muladd
776
777