1 #include <petscviewer.h>
2 #include <petscis.h>
3 #include <petsc/private/petscimpl.h>
4
5 // For accessing bitwise Boolean values in are_handles_leaves
6 #define GREATER_BIT 0
7 #define LESS_EQUAL_BIT 1
8
9 typedef struct {
10 uint8_t axis; // Coordinate direction that stem splits on
11 PetscByte are_handles_leaves; // Bit-wise boolean for whether the greater_handle and less_equal_handle index kdtree->stems or kstree->leaves
12 PetscReal split; // Coordinate value that stem splits on
13 PetscCount greater_handle, less_equal_handle; // Handle (index) of the child nodes of the tree
14 } KDStem;
15
16 typedef struct {
17 PetscInt count; // Number of points in leaf
18 PetscCount indices_handle, coords_handle; // Index into the indices or coordinates array for the points in leaf
19 } KDLeaf;
20
21 struct _n_PetscKDTree {
22 PetscInt dim; // Coordinate dimension of the tree
23 PetscInt max_bucket_size; // Maximum number of points stored at each leaf
24
25 PetscBool is_root_leaf;
26 PetscCount root_handle;
27
28 PetscCount num_coords, num_leaves, num_stems, num_bucket_indices;
29 const PetscReal *coords, *coords_owned; // Only free owned on Destroy
30 KDLeaf *leaves;
31 KDStem *stems;
32 PetscCount *bucket_indices;
33 };
34
35 /*@C
36 PetscKDTreeDestroy - destroy a `PetscKDTree`
37
38 Not Collective, No Fortran Support
39
40 Input Parameters:
41 . tree - tree to destroy
42
43 Level: advanced
44
45 .seealso: `PetscKDTree`, `PetscKDTreeCreate()`
46 @*/
PetscKDTreeDestroy(PetscKDTree * tree)47 PetscErrorCode PetscKDTreeDestroy(PetscKDTree *tree)
48 {
49 PetscFunctionBeginUser;
50 if (*tree == NULL) PetscFunctionReturn(PETSC_SUCCESS);
51 PetscCall(PetscFree((*tree)->stems));
52 PetscCall(PetscFree((*tree)->leaves));
53 PetscCall(PetscFree((*tree)->bucket_indices));
54 PetscCall(PetscFree((*tree)->coords_owned));
55 PetscCall(PetscFree(*tree));
56 PetscFunctionReturn(PETSC_SUCCESS);
57 }
58
59 PetscLogEvent PetscKDTree_Build, PetscKDTree_Query;
PetscKDTreeRegisterLogEvents(void)60 static PetscErrorCode PetscKDTreeRegisterLogEvents(void)
61 {
62 static PetscBool is_initialized = PETSC_FALSE;
63
64 PetscFunctionBeginUser;
65 if (is_initialized) PetscFunctionReturn(PETSC_SUCCESS);
66 PetscCall(PetscLogEventRegister("KDTreeBuild", IS_CLASSID, &PetscKDTree_Build));
67 PetscCall(PetscLogEventRegister("KDTreeQuery", IS_CLASSID, &PetscKDTree_Query));
68 PetscFunctionReturn(PETSC_SUCCESS);
69 }
70
71 // From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
RoundToNextPowerOfTwo(uint32_t v)72 static inline uint32_t RoundToNextPowerOfTwo(uint32_t v)
73 {
74 v--;
75 v |= v >> 1;
76 v |= v >> 2;
77 v |= v >> 4;
78 v |= v >> 8;
79 v |= v >> 16;
80 v++;
81 return v;
82 }
83
84 typedef struct {
85 uint8_t initial_axis;
86 PetscKDTree tree;
87 } *KDTreeSortContext;
88
89 // Sort nodes based on "superkey"
90 // See "Building a Balanced k-d Tree in O(kn log n) Time" https://jcgt.org/published/0004/01/03/
PetscKDTreeSortFunc(PetscCount left,PetscCount right,PetscKDTree tree,uint8_t axis)91 static inline int PetscKDTreeSortFunc(PetscCount left, PetscCount right, PetscKDTree tree, uint8_t axis)
92 {
93 const PetscReal *coords = tree->coords;
94 const PetscInt dim = tree->dim;
95
96 for (PetscInt i = 0; i < dim; i++) {
97 PetscReal diff = coords[left * dim + axis] - coords[right * dim + axis];
98 if (PetscUnlikely(diff == 0)) {
99 axis = (axis + 1) % dim;
100 continue;
101 } else return PetscSign(diff);
102 }
103 return 0; // All components are the same
104 }
105
PetscKDTreeTimSort(const void * l,const void * r,PetscCtx ctx)106 static int PetscKDTreeTimSort(const void *l, const void *r, PetscCtx ctx)
107 {
108 KDTreeSortContext kd_ctx = (KDTreeSortContext)ctx;
109 return PetscKDTreeSortFunc(*(PetscCount *)l, *(PetscCount *)r, kd_ctx->tree, kd_ctx->initial_axis);
110 }
111
PetscKDTreeVerifySortedIndices(PetscKDTree tree,PetscCount sorted_indices[],PetscCount temp[],PetscCount start,PetscCount end)112 static PetscErrorCode PetscKDTreeVerifySortedIndices(PetscKDTree tree, PetscCount sorted_indices[], PetscCount temp[], PetscCount start, PetscCount end)
113 {
114 PetscCount num_coords = tree->num_coords, range_size = end - start, location;
115 PetscBool has_duplicates;
116
117 PetscFunctionBeginUser;
118 PetscCall(PetscArraycpy(temp, &sorted_indices[0 * num_coords + start], range_size));
119 PetscCall(PetscSortCount(range_size, temp));
120 PetscCall(PetscSortedCheckDupsCount(range_size, temp, &has_duplicates));
121 PetscCheck(has_duplicates == PETSC_FALSE, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Sorted indices must have unique entries, but found duplicates");
122 for (PetscInt d = 1; d < tree->dim; d++) {
123 for (PetscCount i = start; i < end; i++) {
124 PetscCall(PetscFindCount(sorted_indices[d * num_coords + i], range_size, temp, &location));
125 PetscCheck(location > -1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Sorted indices are not consistent. Could not find %" PetscCount_FMT " from %" PetscInt_FMT " dimensional index in 0th dimension", sorted_indices[d * num_coords + i], d);
126 }
127 }
128 PetscFunctionReturn(PETSC_SUCCESS);
129 }
130
131 typedef struct {
132 PetscKDTree tree;
133 PetscSegBuffer stems, leaves, bucket_indices, bucket_coords;
134 PetscBool debug_build, copy_coords;
135 } *KDTreeBuild;
136
137 // The range is end exclusive, so [start,end).
PetscKDTreeBuildStemAndLeaves(KDTreeBuild kd_build,PetscCount sorted_indices[],PetscCount temp[],PetscCount start,PetscCount end,PetscInt depth,PetscBool * is_node_leaf,PetscCount * node_handle)138 static PetscErrorCode PetscKDTreeBuildStemAndLeaves(KDTreeBuild kd_build, PetscCount sorted_indices[], PetscCount temp[], PetscCount start, PetscCount end, PetscInt depth, PetscBool *is_node_leaf, PetscCount *node_handle)
139 {
140 PetscKDTree tree = kd_build->tree;
141 PetscInt dim = tree->dim;
142
143 PetscFunctionBeginUser;
144 PetscCheck(start <= end, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Start %" PetscCount_FMT " must be less than or equal to end %" PetscCount_FMT, start, end);
145 if (kd_build->debug_build) PetscCall(PetscKDTreeVerifySortedIndices(tree, sorted_indices, temp, start, end));
146 if (end - start <= tree->max_bucket_size) {
147 KDLeaf *leaf;
148 PetscCount *bucket_indices;
149
150 PetscCall(PetscSegBufferGetSize(kd_build->leaves, node_handle));
151 PetscCall(PetscSegBufferGet(kd_build->leaves, 1, &leaf));
152 PetscCall(PetscMemzero(leaf, sizeof(KDLeaf)));
153 *is_node_leaf = PETSC_TRUE;
154
155 PetscCall(PetscIntCast(end - start, &leaf->count));
156 PetscCall(PetscSegBufferGetSize(kd_build->bucket_indices, &leaf->indices_handle));
157 PetscCall(PetscSegBufferGet(kd_build->bucket_indices, leaf->count, &bucket_indices));
158 PetscCall(PetscArraycpy(bucket_indices, &sorted_indices[start], leaf->count));
159 if (kd_build->copy_coords) {
160 PetscReal *bucket_coords;
161 PetscCall(PetscSegBufferGetSize(kd_build->bucket_coords, &leaf->coords_handle));
162 PetscCall(PetscSegBufferGet(kd_build->bucket_coords, leaf->count * dim, &bucket_coords));
163 // Coords are saved in axis-major ordering for better vectorization
164 for (PetscCount i = 0; i < leaf->count; i++) {
165 for (PetscInt d = 0; d < dim; d++) bucket_coords[d * leaf->count + i] = tree->coords[bucket_indices[i] * dim + d];
166 }
167 } else leaf->coords_handle = -1;
168 } else {
169 KDStem *stem;
170 PetscCount num_coords = tree->num_coords;
171 uint8_t axis = (uint8_t)(depth % dim);
172 PetscBool is_greater_leaf, is_less_equal_leaf;
173 PetscCount median = start + PetscCeilInt64(end - start, 2) - 1, lower;
174 PetscCount median_idx = sorted_indices[median], medianp1_idx = sorted_indices[median + 1];
175
176 PetscCall(PetscSegBufferGetSize(kd_build->stems, node_handle));
177 PetscCall(PetscSegBufferGet(kd_build->stems, 1, &stem));
178 PetscCall(PetscMemzero(stem, sizeof(KDStem)));
179 *is_node_leaf = PETSC_FALSE;
180
181 stem->axis = axis;
182 // Place split halfway between the "boundary" nodes of the partitioning
183 stem->split = (tree->coords[tree->dim * median_idx + axis] + tree->coords[tree->dim * medianp1_idx + axis]) / 2;
184 PetscCall(PetscArraycpy(temp, &sorted_indices[0 * num_coords + start], end - start));
185 lower = median; // Set lower in case dim == 1
186 for (PetscInt d = 1; d < tree->dim; d++) {
187 PetscCount upper = median;
188 lower = start - 1;
189 for (PetscCount i = start; i < end; i++) {
190 // In case of duplicate coord point equal to the median coord point, limit lower partition to median, ensuring balanced tree
191 if (lower < median && PetscKDTreeSortFunc(sorted_indices[d * num_coords + i], median_idx, tree, axis) <= 0) {
192 sorted_indices[(d - 1) * num_coords + (++lower)] = sorted_indices[d * num_coords + i];
193 } else {
194 sorted_indices[(d - 1) * num_coords + (++upper)] = sorted_indices[d * num_coords + i];
195 }
196 }
197 PetscCheck(lower == median, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Partitioning into less_equal bin failed. Range upper bound should be %" PetscCount_FMT " but partitioning resulted in %" PetscCount_FMT, median, lower);
198 PetscCheck(upper == end - 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Partitioning into greater bin failed. Range upper bound should be %" PetscCount_FMT " but partitioning resulted in %" PetscCount_FMT, upper, end - 1);
199 }
200 PetscCall(PetscArraycpy(&sorted_indices[(tree->dim - 1) * num_coords + start], temp, end - start));
201
202 PetscCall(PetscKDTreeBuildStemAndLeaves(kd_build, sorted_indices, temp, start, lower + 1, depth + 1, &is_less_equal_leaf, &stem->less_equal_handle));
203 if (is_less_equal_leaf) PetscCall(PetscBTSet(&stem->are_handles_leaves, LESS_EQUAL_BIT));
204 PetscCall(PetscKDTreeBuildStemAndLeaves(kd_build, sorted_indices, temp, lower + 1, end, depth + 1, &is_greater_leaf, &stem->greater_handle));
205 if (is_greater_leaf) PetscCall(PetscBTSet(&stem->are_handles_leaves, GREATER_BIT));
206 }
207 PetscFunctionReturn(PETSC_SUCCESS);
208 }
209
210 /*@C
211 PetscKDTreeCreate - create a `PetscKDTree`
212
213 Not Collective, No Fortran Support
214
215 Input Parameters:
216 + num_coords - number of coordinate points to build the `PetscKDTree`
217 . dim - the dimension of the coordinates
218 . coords - array of the coordinates, in point-major order
219 . copy_mode - behavior handling `coords`, `PETSC_COPY_VALUES` generally more performant
220 - max_bucket_size - maximum number of points stored at each leaf
221
222 Output Parameter:
223 . new_tree - the resulting `PetscKDTree`
224
225 Level: advanced
226
227 Note:
228 When `copy_mode == PETSC_COPY_VALUES`, the coordinates are copied and organized to optimize vectorization and cache-coherency.
229 It is recommended to run this way if the extra memory use is not a concern and it has very little impact on the `PetscKDTree` creation time.
230
231 Developer Note:
232 Building algorithm detailed in 'Building a Balanced k-d Tree in O(kn log n) Time' Brown, 2015
233
234 .seealso: `PetscKDTree`, `PetscKDTreeDestroy()`, `PetscKDTreeQueryPointsNearestNeighbor()`
235 @*/
PetscKDTreeCreate(PetscCount num_coords,PetscInt dim,const PetscReal coords[],PetscCopyMode copy_mode,PetscInt max_bucket_size,PetscKDTree * new_tree)236 PetscErrorCode PetscKDTreeCreate(PetscCount num_coords, PetscInt dim, const PetscReal coords[], PetscCopyMode copy_mode, PetscInt max_bucket_size, PetscKDTree *new_tree)
237 {
238 PetscKDTree tree;
239 PetscCount *sorted_indices, *temp;
240
241 PetscFunctionBeginUser;
242 PetscCall(PetscKDTreeRegisterLogEvents());
243 PetscCall(PetscLogEventBegin(PetscKDTree_Build, 0, 0, 0, 0));
244 PetscCheck(dim > 0, PETSC_COMM_SELF, PETSC_ERR_USER_INPUT, "Dimension of PetscKDTree must be greater than 0, received %" PetscInt_FMT, dim);
245 PetscCheck(num_coords > -1, PETSC_COMM_SELF, PETSC_ERR_USER_INPUT, "Number of coordinates may not be negative, received %" PetscCount_FMT, num_coords);
246 if (num_coords == 0) {
247 *new_tree = NULL;
248 PetscFunctionReturn(PETSC_SUCCESS);
249 }
250 PetscAssertPointer(coords, 3);
251 PetscAssertPointer(new_tree, 6);
252 PetscCall(PetscNew(&tree));
253 tree->dim = dim;
254 tree->max_bucket_size = max_bucket_size == PETSC_DECIDE ? 32 : max_bucket_size;
255 tree->num_coords = num_coords;
256
257 switch (copy_mode) {
258 case PETSC_OWN_POINTER:
259 tree->coords_owned = coords; // fallthrough
260 case PETSC_USE_POINTER:
261 tree->coords = coords;
262 break;
263 case PETSC_COPY_VALUES:
264 PetscCall(PetscMalloc1(num_coords * dim, &tree->coords_owned));
265 PetscCall(PetscArraycpy((PetscReal *)tree->coords_owned, coords, num_coords * dim));
266 tree->coords = tree->coords_owned;
267 break;
268 }
269
270 KDTreeSortContext kd_ctx;
271 PetscCall(PetscMalloc2(num_coords * dim, &sorted_indices, num_coords, &temp));
272 PetscCall(PetscNew(&kd_ctx));
273 kd_ctx->tree = tree;
274 for (PetscInt j = 0; j < dim; j++) {
275 for (PetscCount i = 0; i < num_coords; i++) sorted_indices[num_coords * j + i] = i;
276 kd_ctx->initial_axis = (uint8_t)j;
277 PetscCall(PetscTimSort((PetscInt)num_coords, &sorted_indices[num_coords * j], sizeof(*sorted_indices), PetscKDTreeTimSort, kd_ctx));
278 }
279 PetscCall(PetscFree(kd_ctx));
280
281 PetscInt num_leaves = (PetscInt)PetscCeilInt64(num_coords, tree->max_bucket_size);
282 PetscInt num_stems = RoundToNextPowerOfTwo((uint32_t)num_leaves);
283 KDTreeBuild kd_build;
284 PetscCall(PetscNew(&kd_build));
285 kd_build->tree = tree;
286 kd_build->copy_coords = copy_mode == PETSC_COPY_VALUES ? PETSC_TRUE : PETSC_FALSE;
287 PetscCall(PetscOptionsGetBool(NULL, NULL, "-kdtree_debug", &kd_build->debug_build, NULL));
288 PetscCall(PetscSegBufferCreate(sizeof(KDStem), num_stems, &kd_build->stems));
289 PetscCall(PetscSegBufferCreate(sizeof(KDLeaf), num_leaves, &kd_build->leaves));
290 PetscCall(PetscSegBufferCreate(sizeof(PetscCount), num_coords, &kd_build->bucket_indices));
291 if (kd_build->copy_coords) PetscCall(PetscSegBufferCreate(sizeof(PetscReal), num_coords * dim, &kd_build->bucket_coords));
292
293 PetscCall(PetscKDTreeBuildStemAndLeaves(kd_build, sorted_indices, temp, 0, num_coords, 0, &tree->is_root_leaf, &tree->root_handle));
294
295 PetscCall(PetscSegBufferGetSize(kd_build->stems, &tree->num_stems));
296 PetscCall(PetscSegBufferGetSize(kd_build->leaves, &tree->num_leaves));
297 PetscCall(PetscSegBufferGetSize(kd_build->bucket_indices, &tree->num_bucket_indices));
298 PetscCall(PetscSegBufferExtractAlloc(kd_build->stems, &tree->stems));
299 PetscCall(PetscSegBufferExtractAlloc(kd_build->leaves, &tree->leaves));
300 PetscCall(PetscSegBufferExtractAlloc(kd_build->bucket_indices, &tree->bucket_indices));
301 if (kd_build->copy_coords) {
302 PetscCall(PetscFree(tree->coords_owned));
303 PetscCall(PetscSegBufferExtractAlloc(kd_build->bucket_coords, &tree->coords_owned));
304 tree->coords = tree->coords_owned;
305 PetscCall(PetscSegBufferDestroy(&kd_build->bucket_coords));
306 }
307 PetscCall(PetscSegBufferDestroy(&kd_build->stems));
308 PetscCall(PetscSegBufferDestroy(&kd_build->leaves));
309 PetscCall(PetscSegBufferDestroy(&kd_build->bucket_indices));
310 PetscCall(PetscFree(kd_build));
311 PetscCall(PetscFree2(sorted_indices, temp));
312 *new_tree = tree;
313 PetscCall(PetscLogEventEnd(PetscKDTree_Build, 0, 0, 0, 0));
314 PetscFunctionReturn(PETSC_SUCCESS);
315 }
316
PetscSquareDistance(PetscInt dim,const PetscReal * PETSC_RESTRICT x,const PetscReal * PETSC_RESTRICT y)317 static inline PetscReal PetscSquareDistance(PetscInt dim, const PetscReal *PETSC_RESTRICT x, const PetscReal *PETSC_RESTRICT y)
318 {
319 PetscReal dist = 0;
320 for (PetscInt j = 0; j < dim; j++) dist += PetscSqr(x[j] - y[j]);
321 return dist;
322 }
323
PetscKDTreeQueryLeaf(PetscKDTree tree,KDLeaf leaf,const PetscReal point[],PetscCount * index,PetscReal * distance_sqr)324 static inline PetscErrorCode PetscKDTreeQueryLeaf(PetscKDTree tree, KDLeaf leaf, const PetscReal point[], PetscCount *index, PetscReal *distance_sqr)
325 {
326 PetscInt dim = tree->dim;
327
328 PetscFunctionBeginUser;
329 *distance_sqr = PETSC_MAX_REAL;
330 *index = -1;
331 for (PetscInt i = 0; i < leaf.count; i++) {
332 PetscCount point_index = tree->bucket_indices[leaf.indices_handle + i];
333 PetscReal dist = PetscSquareDistance(dim, point, &tree->coords[point_index * dim]);
334 if (dist < *distance_sqr) {
335 *distance_sqr = dist;
336 *index = point_index;
337 }
338 }
339 PetscFunctionReturn(PETSC_SUCCESS);
340 }
341
PetscKDTreeQueryLeaf_CopyCoords(PetscKDTree tree,KDLeaf leaf,const PetscReal point[],PetscCount * index,PetscReal * distance_sqr)342 static inline PetscErrorCode PetscKDTreeQueryLeaf_CopyCoords(PetscKDTree tree, KDLeaf leaf, const PetscReal point[], PetscCount *index, PetscReal *distance_sqr)
343 {
344 PetscInt dim = tree->dim;
345
346 PetscFunctionBeginUser;
347 *distance_sqr = PETSC_MAX_REAL;
348 *index = -1;
349 for (PetscInt i = 0; i < leaf.count; i++) {
350 // Coord data saved in axis-major ordering for vectorization
351 PetscReal dist = 0.;
352 for (PetscInt d = 0; d < dim; d++) dist += PetscSqr(point[d] - tree->coords[leaf.coords_handle + d * leaf.count + i]);
353 if (dist < *distance_sqr) {
354 *distance_sqr = dist;
355 *index = tree->bucket_indices[leaf.indices_handle + i];
356 }
357 }
358 PetscFunctionReturn(PETSC_SUCCESS);
359 }
360
361 // Recursive point query from 'Algorithms for Fast Vector Quantization' by Sunil Arya and David Mount
362 // Variant also implemented in pykdtree
PetscKDTreeQuery_Recurse(PetscKDTree tree,const PetscReal point[],PetscCount node_handle,char is_node_leaf,PetscReal offset[],PetscReal rd,PetscReal tol_sqr,PetscCount * index,PetscReal * dist_sqr)363 static PetscErrorCode PetscKDTreeQuery_Recurse(PetscKDTree tree, const PetscReal point[], PetscCount node_handle, char is_node_leaf, PetscReal offset[], PetscReal rd, PetscReal tol_sqr, PetscCount *index, PetscReal *dist_sqr)
364 {
365 PetscFunctionBeginUser;
366 if (*dist_sqr < tol_sqr) PetscFunctionReturn(PETSC_SUCCESS);
367 if (is_node_leaf) {
368 KDLeaf leaf = tree->leaves[node_handle];
369 PetscReal dist;
370 PetscCount point_index;
371
372 if (leaf.coords_handle > -1) PetscCall(PetscKDTreeQueryLeaf_CopyCoords(tree, leaf, point, &point_index, &dist));
373 else PetscCall(PetscKDTreeQueryLeaf(tree, leaf, point, &point_index, &dist));
374 if (dist < *dist_sqr) {
375 *dist_sqr = dist;
376 *index = point_index;
377 }
378 PetscFunctionReturn(PETSC_SUCCESS);
379 }
380
381 KDStem stem = tree->stems[node_handle];
382 PetscReal old_offset = offset[stem.axis], new_offset = point[stem.axis] - stem.split;
383 if (new_offset <= 0) {
384 PetscCall(PetscKDTreeQuery_Recurse(tree, point, stem.less_equal_handle, PetscBTLookup(&stem.are_handles_leaves, LESS_EQUAL_BIT), offset, rd, tol_sqr, index, dist_sqr));
385 rd += -PetscSqr(old_offset) + PetscSqr(new_offset);
386 if (rd < *dist_sqr) {
387 offset[stem.axis] = new_offset;
388 PetscCall(PetscKDTreeQuery_Recurse(tree, point, stem.greater_handle, PetscBTLookup(&stem.are_handles_leaves, GREATER_BIT), offset, rd, tol_sqr, index, dist_sqr));
389 offset[stem.axis] = old_offset;
390 }
391 } else {
392 PetscCall(PetscKDTreeQuery_Recurse(tree, point, stem.greater_handle, PetscBTLookup(&stem.are_handles_leaves, GREATER_BIT), offset, rd, tol_sqr, index, dist_sqr));
393 rd += -PetscSqr(old_offset) + PetscSqr(new_offset);
394 if (rd < *dist_sqr) {
395 offset[stem.axis] = new_offset;
396 PetscCall(PetscKDTreeQuery_Recurse(tree, point, stem.less_equal_handle, PetscBTLookup(&stem.are_handles_leaves, LESS_EQUAL_BIT), offset, rd, tol_sqr, index, dist_sqr));
397 offset[stem.axis] = old_offset;
398 }
399 }
400 PetscFunctionReturn(PETSC_SUCCESS);
401 }
402
403 /*@C
404 PetscKDTreeQueryPointsNearestNeighbor - find the nearest neighbor in a `PetscKDTree`
405
406 Not Collective, No Fortran Support
407
408 Input Parameters:
409 + tree - tree to query
410 . num_points - number of points to query
411 . points - array of the coordinates, in point-major order
412 - tolerance - tolerance for nearest neighbor
413
414 Output Parameters:
415 + indices - indices of the nearest neighbor to the query point
416 - distances - distance between the queried point and the nearest neighbor
417
418 Level: advanced
419
420 Notes:
421 When traversing the tree, if a point has been found to be closer than the `tolerance`, the function short circuits and doesn't check for any closer points.
422
423 The `indices` and `distances` arrays should be at least of size `num_points`.
424
425 .seealso: `PetscKDTree`, `PetscKDTreeCreate()`
426 @*/
PetscKDTreeQueryPointsNearestNeighbor(PetscKDTree tree,PetscCount num_points,const PetscReal points[],PetscReal tolerance,PetscCount indices[],PetscReal distances[])427 PetscErrorCode PetscKDTreeQueryPointsNearestNeighbor(PetscKDTree tree, PetscCount num_points, const PetscReal points[], PetscReal tolerance, PetscCount indices[], PetscReal distances[])
428 {
429 PetscReal *offsets, rd;
430
431 PetscFunctionBeginUser;
432 PetscCall(PetscLogEventBegin(PetscKDTree_Query, 0, 0, 0, 0));
433 if (tree == NULL) {
434 PetscCheck(num_points == 0, PETSC_COMM_SELF, PETSC_ERR_USER_INPUT, "num_points may only be zero, if tree is NULL");
435 PetscFunctionReturn(PETSC_SUCCESS);
436 }
437 PetscAssertPointer(points, 3);
438 PetscAssertPointer(indices, 5);
439 PetscAssertPointer(distances, 6);
440 PetscCall(PetscCalloc1(tree->dim, &offsets));
441
442 for (PetscCount p = 0; p < num_points; p++) {
443 rd = 0.;
444 distances[p] = PETSC_MAX_REAL;
445 indices[p] = -1;
446 PetscCall(PetscKDTreeQuery_Recurse(tree, &points[p * tree->dim], tree->root_handle, (char)tree->is_root_leaf, offsets, rd, PetscSqr(tolerance), &indices[p], &distances[p]));
447 distances[p] = PetscSqrtReal(distances[p]);
448 }
449 PetscCall(PetscFree(offsets));
450 PetscCall(PetscLogEventEnd(PetscKDTree_Query, 0, 0, 0, 0));
451 PetscFunctionReturn(PETSC_SUCCESS);
452 }
453
454 /*@C
455 PetscKDTreeView - view a `PetscKDTree`
456
457 Not Collective, No Fortran Support
458
459 Input Parameters:
460 + tree - tree to view
461 - viewer - visualization context
462
463 Level: advanced
464
465 .seealso: `PetscKDTree`, `PetscKDTreeCreate()`, `PetscViewer`
466 @*/
PetscKDTreeView(PetscKDTree tree,PetscViewer viewer)467 PetscErrorCode PetscKDTreeView(PetscKDTree tree, PetscViewer viewer)
468 {
469 PetscFunctionBeginUser;
470 if (viewer) PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
471 else PetscCall(PetscViewerASCIIGetStdout(PETSC_COMM_SELF, &viewer));
472 if (tree == NULL) PetscFunctionReturn(PETSC_SUCCESS);
473
474 PetscCall(PetscViewerASCIIPrintf(viewer, "KDTree:\n"));
475 PetscCall(PetscViewerASCIIPushTab(viewer)); // KDTree:
476 PetscCall(PetscViewerASCIIPrintf(viewer, "Stems:\n"));
477 PetscCall(PetscViewerASCIIPushTab(viewer)); // Stems:
478 for (PetscCount i = 0; i < tree->num_stems; i++) {
479 KDStem stem = tree->stems[i];
480 PetscCall(PetscViewerASCIIPrintf(viewer, "Stem %" PetscCount_FMT ": Axis=%" PetscInt_FMT " Split=%g Greater_%s=%" PetscCount_FMT " Lesser_Equal_%s=%" PetscCount_FMT "\n", i, (PetscInt)stem.axis, (double)stem.split, PetscBTLookup(&stem.are_handles_leaves, GREATER_BIT) ? "Leaf" : "Stem",
481 stem.greater_handle, PetscBTLookup(&stem.are_handles_leaves, LESS_EQUAL_BIT) ? "Leaf" : "Stem", stem.less_equal_handle));
482 }
483 PetscCall(PetscViewerASCIIPopTab(viewer)); // Stems:
484
485 PetscCall(PetscViewerASCIIPrintf(viewer, "Leaves:\n"));
486 PetscCall(PetscViewerASCIIPushTab(viewer)); // Leaves:
487 for (PetscCount i = 0; i < tree->num_leaves; i++) {
488 KDLeaf leaf = tree->leaves[i];
489 PetscCall(PetscViewerASCIIPrintf(viewer, "Leaf %" PetscCount_FMT ": Count=%" PetscInt_FMT, i, leaf.count));
490 PetscCall(PetscViewerASCIIPushTab(viewer)); // Coords:
491 for (PetscInt j = 0; j < leaf.count; j++) {
492 PetscInt tabs;
493 PetscCount bucket_index = tree->bucket_indices[leaf.indices_handle + j];
494 PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
495 PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscCount_FMT ": ", bucket_index));
496
497 PetscCall(PetscViewerASCIIGetTab(viewer, &tabs));
498 PetscCall(PetscViewerASCIISetTab(viewer, 0));
499 if (leaf.coords_handle > -1) {
500 for (PetscInt k = 0; k < tree->dim; k++) PetscCall(PetscViewerASCIIPrintf(viewer, "%g ", (double)tree->coords[leaf.coords_handle + leaf.count * k + j]));
501 PetscCall(PetscViewerASCIIPrintf(viewer, " (stored at leaf)"));
502 } else {
503 for (PetscInt k = 0; k < tree->dim; k++) PetscCall(PetscViewerASCIIPrintf(viewer, "%g ", (double)tree->coords[bucket_index * tree->dim + k]));
504 }
505 PetscCall(PetscViewerASCIISetTab(viewer, tabs));
506 }
507 PetscCall(PetscViewerASCIIPopTab(viewer)); // Coords:
508 PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
509 }
510 PetscCall(PetscViewerASCIIPopTab(viewer)); // Leaves:
511 PetscCall(PetscViewerASCIIPopTab(viewer)); // KDTree:
512 PetscFunctionReturn(PETSC_SUCCESS);
513 }
514