YAC 3.21.0
Yet Another Coupler
Loading...
Searching...
No Matches
test_point_sphere_part.c
Go to the documentation of this file.
1// Copyright (c) 2024 The YAC Authors
2//
3// SPDX-License-Identifier: BSD-3-Clause
4
5#include <stdlib.h>
6#include <stdio.h>
7#include <float.h>
8#include <string.h>
9
10#include "tests.h"
11#include "test_common.h"
12#include "grids/basic_grid.h"
13#include "sphere_part.h"
14#include "geometry.h"
15
21static void utest_random_test(size_t const num_points);
22static void utest_zero_point_test();
23static void utest_single_point_test();
24static void utest_empty_branch_test(size_t num_points);
25static void utest_zero_balance_point();
26static void utest_duplicated_point_test();
27static void utest_bnd_circle_search();
28static void utest_bnd_circle_random_test(size_t num_points, size_t num_circles);
29
30int main(void) {
31
32 srand(1365441);
33
34 for (size_t i = 0; i < 10; ++i) {
35 utest_random_test(1);
36 utest_random_test(10);
37 utest_random_test(100);
38 utest_random_test(650);
39 }
40 utest_zero_point_test();
41 utest_single_point_test();
42 utest_empty_branch_test(4);
43 utest_empty_branch_test(128);
44 utest_zero_balance_point();
45 utest_duplicated_point_test();
46 utest_bnd_circle_search();
47 for (size_t i = 0; i < 10; ++i) {
48 utest_bnd_circle_random_test(10, 5);
49 utest_bnd_circle_random_test(100, 20);
50 utest_bnd_circle_random_test(500, 50);
51 }
52
53 return TEST_EXIT_CODE;
54}
55
57 double distance;
58 size_t index;
59};
60
61static int utest_compare_distance_index(const void * a, const void * b) {
62
63 int ret = (((struct distance_index*)a)->distance >
64 ((struct distance_index*)b)->distance) -
65 (((struct distance_index*)a)->distance <
66 ((struct distance_index*)b)->distance);
67 if (ret == 0)
68 ret = (((struct distance_index*)a)->index >
69 ((struct distance_index*)b)->index) -
70 (((struct distance_index*)a)->index <
71 ((struct distance_index*)b)->index);
72
73 return ret;
74}
75
76static void utest_point_sphere_test_NNN(
77 struct point_sphere_part_search * search, size_t n,
78 double (*sphere_part_coordinates_xyz)[3],
79 double (*search_coordinates_xyz)[3], size_t num_points,
80 size_t ** local_point_ids, size_t * local_point_ids_array_size,
81 size_t * num_local_point_ids, struct distance_index * distances) {
82
83 for (int with_result_coords = 0; with_result_coords < 2;
84 ++with_result_coords) {
85
86 double (*result_coordinates_xyz)[3] = NULL;
87 size_t result_coordinates_xyz_array_size = 0;
88
90 search, num_points, search_coordinates_xyz, n, NULL, NULL,
91 with_result_coords?&result_coordinates_xyz:NULL,
92 with_result_coords?&result_coordinates_xyz_array_size:NULL,
93 local_point_ids, local_point_ids_array_size, num_local_point_ids);
94
95 // check results
96 for (size_t j = 0, offset = 0; j < num_points; ++j) {
97
98 struct distance_index * curr_distances = distances + j * num_points;
99
100 size_t ref_num_local_points = 1, m = 0;
101 for (; (ref_num_local_points < num_points); ++ref_num_local_points)
102 if (utest_double_are_unequal(
103 curr_distances[ref_num_local_points-1].distance,
104 curr_distances[ref_num_local_points].distance))
105 if (++m == n) break;
106
107 if (num_local_point_ids[j] != ref_num_local_points) {
108 PUT_ERR("wrong number of local points found\n");
109 continue;
110 }
111
112 size_t num_matching_points = 0;
113 for (size_t k = 0; k < ref_num_local_points; ++k) {
114 for (size_t l = 0; l < ref_num_local_points; ++l) {
115 if ((curr_distances[k].index == (*local_point_ids)[offset+l]) &&
116 (!with_result_coords ||
118 sphere_part_coordinates_xyz[curr_distances[k].index],
119 result_coordinates_xyz[offset+l])))) {
120 ++num_matching_points;
121 }
122 }
123 }
124
125 if (num_matching_points != ref_num_local_points)
126 PUT_ERR("wrong results\n");
127
128 offset += ref_num_local_points;
129 }
130
131 free(result_coordinates_xyz);
132 }
133}
134
135static void utest_point_sphere_test_NNN_ubound(
136 struct point_sphere_part_search * search, size_t num_points,
137 yac_coordinate_pointer xyz_coordinates, size_t n,
138 struct distance_index * distances) {
139
140 struct sin_cos_angle angles[num_points];
141
143 search, num_points, xyz_coordinates, n, angles);
144
145 for (size_t i = 0; i < num_points; ++i) {
146 double ref_ubound =
147 distances[i * num_points + n - 1].distance;
148
149 if (compare_angles(
150 sin_cos_angle_new(sin(ref_ubound), cos(ref_ubound)),
151 angles[i]) > 0)
152 PUT_ERR("wrong result");
153 }
154}
155
156static void utest_point_sphere_test_NNN_bnd_circle(
157 struct point_sphere_part_search * search, size_t num_points,
158 yac_coordinate_pointer xyz_coordinates, size_t n,
159 struct distance_index * distances) {
160
161 if (n == 0) return;
162
163 struct bounding_circle bnd_circles[num_points];
164
165 size_t cut_off = n / 2;
166
167 for (size_t i = 0; i < num_points; ++i) {
168
169 memcpy(
170 bnd_circles[i].base_vector, xyz_coordinates[i],
171 sizeof(*xyz_coordinates));
172 double distance =
173 distances[
174 i * num_points + ((i < num_points / 2)?cut_off:(n-1))].distance +
176 bnd_circles[i].inc_angle =
177 sin_cos_angle_new(sin(distance), cos(distance));
178 }
179
180 size_t * local_point_ids = NULL;
181 size_t local_point_ids_array_size = 0;
182 size_t * num_local_point_ids =
183 malloc(num_points * sizeof(*num_local_point_ids));
184
186 search, num_points, bnd_circles, n, &local_point_ids,
187 &local_point_ids_array_size, num_local_point_ids);
188
189 size_t offset = 0;
190 for (size_t i = 0; i < num_points; ++i) {
191
192 size_t ref_num_local_point_ids = (i < num_points / 2)?(n / 2 + 1):n;
193 double ref_distance =
194 distances[i * num_points + ref_num_local_point_ids - 1].distance;
195 for (; (ref_num_local_point_ids < MIN(n, num_points)) &&
196 (distances[
197 i * num_points + ref_num_local_point_ids].distance == ref_distance);
198 ++ref_num_local_point_ids);
199
200 if (ref_num_local_point_ids != num_local_point_ids[i])
201 PUT_ERR("wrong result");
202
203 for (size_t j = 0; j < num_local_point_ids[i]; ++j) {
204 int match_flag = 0;
205 for (size_t k = 0; (k < num_local_point_ids[i]) && !match_flag; ++k)
206 match_flag =
207 distances[i * num_points + k].index == local_point_ids[offset + j];
208 if (!match_flag) PUT_ERR("wrong result");
209 }
210 offset += num_local_point_ids[i];
211 }
212
213 free(local_point_ids);
214 free(num_local_point_ids);
215}
216
217static void utest_gen_random_point(double * point) {
218
219 double x_coordinate = 2.0 * M_PI * (((double)rand()) / ((double)RAND_MAX));
220 double y_coordinate = M_PI * (((double)rand()) / ((double)RAND_MAX)) - M_PI_2;
221 LLtoXYZ(x_coordinate, y_coordinate, point);
222}
223
224static void utest_gen_random_bnd_circle(struct bounding_circle * circle) {
225
226 utest_gen_random_point(circle->base_vector);
227 double angle = M_PI_2 * (((double)rand()) / ((double)RAND_MAX));
228 circle->inc_angle = sin_cos_angle_new(sin(angle), cos(angle));
229 circle->sq_crd = DBL_MAX;
230}
231
232static void utest_random_test(size_t const num_points) {
233
234 double (*coordinates_xyz)[3];
235 double (*coordinates_xyz_a)[3];
236 double (*coordinates_xyz_b)[3];
237 struct bounding_circle * bnd_circles =
238 xmalloc(num_points * sizeof(*bnd_circles));
239 yac_int * global_ids = xmalloc(num_points * sizeof(*global_ids));
240
241 coordinates_xyz = xmalloc(2 * num_points * sizeof(*coordinates_xyz));
242 coordinates_xyz_a = coordinates_xyz;
243 coordinates_xyz_b = coordinates_xyz + num_points;
244
245 size_t * local_point_ids = NULL;
246 size_t local_point_ids_array_size = 0;
247 size_t * num_local_point_ids =
248 xmalloc(num_points * sizeof(*num_local_point_ids));
249 struct distance_index * distances =
250 xmalloc(num_points * num_points * sizeof(*distances));
251
252 for (size_t i = 0; i < num_points; ++i) {
253 utest_gen_random_bnd_circle(bnd_circles + i);
254 global_ids[i] = (yac_int)i;
255 }
256 for (size_t k = 0; k < 2 * num_points; ++k)
257 utest_gen_random_point(coordinates_xyz[k]);
258
259 for (size_t j = 0; j < num_points; ++j) {
260 for (size_t k = 0; k < num_points; ++k) {
261
262 distances[j * num_points + k].distance =
263 get_vector_angle(coordinates_xyz_a[k], coordinates_xyz_b[j]);
264 distances[j * num_points + k].index = k;
265 }
266 qsort(
267 distances + j * num_points, num_points, sizeof(*distances),
268 utest_compare_distance_index);
269 }
270
271 struct point_sphere_part_search * search =
273 num_points, (yac_const_coordinate_pointer)coordinates_xyz_a,
274 global_ids);
275
276 for (size_t n = 1; n < 16; ++n)
277 utest_point_sphere_test_NNN(
278 search, n, coordinates_xyz_a, coordinates_xyz_b, num_points,
279 &local_point_ids, &local_point_ids_array_size, num_local_point_ids,
280 distances);
281
282 for (size_t n = 1; n < MIN(num_points,16); ++n)
283 utest_point_sphere_test_NNN_ubound(
284 search, num_points, coordinates_xyz_b, n, distances);
285
286 for (size_t n = 1; n < MIN(num_points,16); ++n)
287 utest_point_sphere_test_NNN_bnd_circle(
288 search, num_points, coordinates_xyz_b, n, distances);
289
291
292 free(distances);
293 free(local_point_ids);
294 free(num_local_point_ids);
295 free(global_ids);
296 free(bnd_circles);
297 free(coordinates_xyz);
298}
299
300static void utest_single_point_test() {
301
302 struct point_sphere_part_search * search;
303 double xyz_coordinate[1][3];
304 {
305 double x_coordinate = 0, y_coordinate = 0;
306 yac_int global_id[1] = {0};
307 LLtoXYZ(x_coordinate, y_coordinate, xyz_coordinate[0]);
308 search =
310 1, (yac_const_coordinate_pointer)xyz_coordinate, global_id);
311 }
312
313 double x_coordinates[] = {0.0,90.0,180.0,-90.0, 0.0, 0.0};
314 double y_coordinates[] = {0.0, 0.0, 0.0, 0.0,90.0,-90.0};
315 size_t num_points = sizeof(x_coordinates) / sizeof(x_coordinates[0]);
316 double xyz_coordinates[num_points][3];
317 for (size_t i = 0; i < num_points; ++i)
318 LLtoXYZ_deg(x_coordinates[i], y_coordinates[i], xyz_coordinates[i]);
319
320 size_t * local_point_ids = NULL;
321 size_t local_point_ids_array_size = 0;
322 size_t * num_local_point_ids =
323 xmalloc(num_points * sizeof(*num_local_point_ids));
324
326 search, num_points, xyz_coordinates, NULL, NULL, NULL, &local_point_ids,
327 &local_point_ids_array_size, num_local_point_ids);
328
329 for (size_t i = 0; i < num_points; ++i)
330 if ((num_local_point_ids[i] != 1) || (local_point_ids[i] != 0))
331 PUT_ERR("utest_single_point_test: wrong result\n");
332
333 struct sin_cos_angle angles[num_points];
335 search, num_points, xyz_coordinates, 1, angles);
336
337 for (size_t i = 0; i < num_points; ++i)
338 if (compare_angles(
339 angles[i],
341 xyz_coordinate[0], xyz_coordinates[i])) != 0)
342 PUT_ERR("wrong ubound");
343
344 free(local_point_ids);
345 free(num_local_point_ids);
347}
348
349static void utest_zero_point_test() {
350
351 struct point_sphere_part_search * search =
353
354 double x_coordinates[] = {0.0,90.0,180.0,-90.0, 0.0, 0.0};
355 double y_coordinates[] = {0.0, 0.0, 0.0, 0.0,90.0,-90.0};
356 enum {NUM_POINTS = sizeof(x_coordinates) / sizeof(x_coordinates[0])};
357 double xyz_coordinates[NUM_POINTS][3];
358 for (size_t i = 0; i < NUM_POINTS; ++i)
359 LLtoXYZ_deg(x_coordinates[i], y_coordinates[i], xyz_coordinates[i]);
360
361 struct bounding_circle bnd_circles[NUM_POINTS];
362 for (size_t i = 0; i < NUM_POINTS; ++i) {
364 x_coordinates[i], y_coordinates[i], bnd_circles[i].base_vector);
365 bnd_circles[i].inc_angle = sin_cos_angle_new(sin(0.1), cos(0.1));
366 }
367
368 size_t * local_point_ids = NULL;
369 size_t local_point_ids_array_size = 0;
370 size_t num_local_point_ids[NUM_POINTS];
371
372 for (size_t i = 0; i < NUM_POINTS; ++i)
373 num_local_point_ids[i] = (size_t)-1;
375 search, NUM_POINTS, xyz_coordinates, NULL, NULL, NULL, &local_point_ids,
376 &local_point_ids_array_size, num_local_point_ids);
377
378 for (size_t i = 0; i < NUM_POINTS; ++i)
379 if (num_local_point_ids[i] != 0) PUT_ERR("utest_zero_point_test: wrong result\n");
380
381 for (size_t i = 0; i < NUM_POINTS; ++i)
382 num_local_point_ids[i] = (size_t)-1;
384 search, NUM_POINTS, xyz_coordinates, 16, NULL, NULL, NULL, NULL,
385 &local_point_ids, &local_point_ids_array_size, num_local_point_ids);
386
387 for (size_t i = 0; i < NUM_POINTS; ++i)
388 if (num_local_point_ids[i] != 0) PUT_ERR("utest_zero_point_test: wrong result\n");
389
390 for (size_t i = 0; i < NUM_POINTS; ++i)
391 num_local_point_ids[i] = (size_t)-1;
393 search, NUM_POINTS, bnd_circles, 1, &local_point_ids,
394 &local_point_ids_array_size, num_local_point_ids);
395
396 for (size_t i = 0; i < NUM_POINTS; ++i)
397 if (num_local_point_ids[i] != 0) PUT_ERR("utest_zero_point_test: wrong result\n");
398
399 free(local_point_ids);
401}
402
403static void utest_empty_branch_test(size_t num_points_a) {
404
405 yac_coordinate_pointer xyz_coordinates_a =
406 malloc(num_points_a * sizeof(*xyz_coordinates_a));
407 yac_int * global_ids = malloc(num_points_a * sizeof(*global_ids));
408 for (size_t i = 0; i < num_points_a; ++i) {
409 LLtoXYZ_deg(0.0, -5.0 + (10.0 / (double)num_points_a) * (double)i,
410 xyz_coordinates_a[i]);
411 global_ids[i] = (yac_int)i;
412 }
413
414 struct point_sphere_part_search * search =
416 num_points_a, (yac_const_coordinate_pointer)xyz_coordinates_a,
417 global_ids);
418 free(global_ids);
419
420 double x_coordinates_b[] = {-1.0,-1.0, 1.0,1.0};
421 double y_coordinates_b[] = {-1.0, 1.0,-1.0,1.0};
422 size_t num_points_b = sizeof(x_coordinates_b) / sizeof(x_coordinates_b[0]);
423 double xyz_coordinates_b[num_points_b][3];
424 for (size_t i = 0; i < num_points_b; ++i)
425 LLtoXYZ_deg(x_coordinates_b[i], y_coordinates_b[i], xyz_coordinates_b[i]);
426
427 size_t * local_point_ids = NULL;
428 size_t local_point_ids_array_size = 0;
429 size_t * num_local_point_ids =
430 xmalloc(num_points_b * sizeof(*num_local_point_ids));
431
432 struct distance_index distances[num_points_a * num_points_b];
433
434 for (size_t i = 0; i < num_points_b; ++i) {
435
436 for (size_t j = 0; j < num_points_a; ++j) {
437
438 distances[i * num_points_a + j].distance =
439 get_vector_angle(xyz_coordinates_a[j], xyz_coordinates_b[i]);
440 distances[i * num_points_a + j].index = j;
441 }
442 qsort(distances + i * num_points_a, num_points_a, sizeof(*distances),
443 utest_compare_distance_index);
444 }
445
446 for (size_t i = 0; i < num_points_b; ++i)
447 num_local_point_ids[i] = (size_t)-1;
449 search, num_points_b, xyz_coordinates_b, NULL, NULL, NULL,
450 &local_point_ids, &local_point_ids_array_size, num_local_point_ids);
451
452 // check results
453 for (size_t j = 0, offset = 0; j < num_points_b; ++j) {
454
455 struct distance_index * curr_distances = distances + j * num_points_a;
456
457 size_t ref_num_local_points;
458 for (ref_num_local_points = 1;
459 (ref_num_local_points < num_points_a); ++ref_num_local_points)
460 if (utest_double_are_unequal(
461 curr_distances[ref_num_local_points-1].distance,
462 curr_distances[ref_num_local_points].distance)) break;
463
464 if (num_local_point_ids[j] != ref_num_local_points) {
465 PUT_ERR("wrong number of local points found\n");
466 continue;
467 }
468
469 size_t num_matching_points = 0;
470 for (size_t k = 0; k < ref_num_local_points; ++k)
471 for (size_t l = 0; l < ref_num_local_points; ++l)
472 if (curr_distances[k].index == local_point_ids[offset+l])
473 ++num_matching_points;
474
475 if (num_matching_points != ref_num_local_points)
476 PUT_ERR("wrong results\n");
477
478 offset += ref_num_local_points;
479 }
480 free(local_point_ids);
481 free(num_local_point_ids);
482
483 struct sin_cos_angle angles[num_points_b];
484
486 search, num_points_b, xyz_coordinates_b, 1, angles);
487
488 // check results
489 for (size_t j = 0; j < num_points_b; ++j) {
490
491 struct sin_cos_angle min_angle = SIN_COS_M_PI;
492
493 for (size_t i = 0; i < num_points_a; ++i) {
494
495 struct sin_cos_angle curr_angle =
497 xyz_coordinates_a[i], xyz_coordinates_b[j]);
498
499 if (compare_angles(curr_angle, min_angle) < 0)
500 min_angle = curr_angle;
501 }
502
503 if (compare_angles(min_angle, angles[j]))
504 PUT_ERR("wrong results");
505 }
506
507 free(xyz_coordinates_a);
508
510}
511
512static void utest_zero_balance_point() {
513
514 struct point_sphere_part_search * search;
515 {
516 double x_coordinate = 0, y_coordinate = 0;
517 double xyz_coordinate[6][3] =
518 {{1,0,0}, {-1,0,0},
519 {0,1,0}, {0,-1,0},
520 {0,0,1}, {0,0,-1}};
521 yac_int global_ids[6] = {0,1,2,3,4,5};
522 LLtoXYZ(x_coordinate, y_coordinate, xyz_coordinate[0]);
523 search =
525 6, (yac_const_coordinate_pointer)xyz_coordinate, global_ids);
526 }
528}
529
530static void utest_duplicated_point_test() {
531
532 struct point_sphere_part_search * search;
533 {
534 double x_coordinates[] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,
535 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,
536 2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,
537 3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,
538 4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,
539 2.0,2.0,2.0};
540 double y_coordinates[] = {0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,
541 0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,
542 0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,
543 0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,
544 0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,
545 4.0,4.0,4.0};
546 enum {NUM_POINTS = sizeof(x_coordinates) / sizeof(x_coordinates[0])};
547 double xyz_coordinates[NUM_POINTS][3];
548 yac_int global_ids[NUM_POINTS] = {
549 0,1,2,3,4,5,6,7,8,9,
550 10,11,12,13,14,15,16,17,18,19,
551 20,21,22,23,24,25,26,27,28,29,
552 30,31,32,33,34,35,36,37,38,39,
553 40,41,42,43,44,45,46,47,48,49,
554 100,100,101};
555 for (size_t i = 0; i < NUM_POINTS; ++i) {
556 LLtoXYZ_deg(x_coordinates[i], y_coordinates[i], xyz_coordinates[i]);
557 }
558 search =
561 global_ids);
562 }
563
564 double x_coordinates[] = {1.0,1.0,2.0,2.0,2.0,3.0,3.0,2.1,0.1,2.5};
565 double y_coordinates[] = {1.0,8.0,3.0,4.0,5.0,1.0,8.0,4.0,0.0,3.0};
566 enum {
567 NUM_POINTS = sizeof(x_coordinates) / sizeof(x_coordinates[0]),
568 MAX_NUM_RESULTS = 2};
569 double xyz_coordinates[NUM_POINTS][3];
570 for (size_t i = 0; i < NUM_POINTS; ++i)
571 LLtoXYZ_deg(x_coordinates[i], y_coordinates[i], xyz_coordinates[i]);
572
573 size_t * local_point_ids = NULL;
574 size_t local_point_ids_array_size = 0;
575 size_t num_local_point_ids[NUM_POINTS];
576
577 double (*result_coordinates_xyz)[3] = NULL;
578 size_t result_coordinates_xyz_array_size = 0;
579
581 search, NUM_POINTS, xyz_coordinates, NULL,
582 &result_coordinates_xyz, &result_coordinates_xyz_array_size,
583 &local_point_ids, &local_point_ids_array_size, num_local_point_ids);
584
585 size_t ref_local_point_ids[NUM_POINTS][MAX_NUM_RESULTS] =
586 {{11}, {18}, {23}, {52}, {25}, {31}, {38}, {52}, {0}, {23,33}};
587 size_t ref_num_local_point_ids[NUM_POINTS] = {1,1,1,1,1,1,1,1,1,2};
588 double ref_x_coordinates[NUM_POINTS][MAX_NUM_RESULTS] =
589 {{1.0},{1.0},{2.0},{2.0},{2.0},{3.0},{3.0},{2.0},{0.0},{2.0,3.0}};
590 double ref_y_coordinates[NUM_POINTS][MAX_NUM_RESULTS] =
591 {{1.0},{8.0},{3.0},{4.0},{5.0},{1.0},{8.0},{4.0},{0.0},{3.0,3.0}};
592 double ref_result_coordinates_xyz[NUM_POINTS][MAX_NUM_RESULTS][3];
593
594 for (size_t i = 0; i < NUM_POINTS; ++i)
595 for (size_t j = 0; j < ref_num_local_point_ids[i]; ++j)
597 ref_x_coordinates[i][j], ref_y_coordinates[i][j],
598 ref_result_coordinates_xyz[i][j]);
599
600 for (size_t i = 0, offset = 0; i < NUM_POINTS;
601 offset += num_local_point_ids[i++]) {
602
603 if (num_local_point_ids[i] != ref_num_local_point_ids[i]) {
604 PUT_ERR("ERROR in yac_point_sphere_part_search_NN (num_local_point_ids)");
605 } else {
606
607 int match_flag[ref_num_local_point_ids[i]];
608 size_t match_count = 0;
609 memset(match_flag, 0, ref_num_local_point_ids[i] * sizeof(match_flag[0]));
610
611 for (size_t j = 0; j < ref_num_local_point_ids[i]; ++j) {
612
613 for (size_t k = 0; k < ref_num_local_point_ids[i]; ++k) {
614
616 result_coordinates_xyz[offset+j],
617 ref_result_coordinates_xyz[i][k]) &&
618 (local_point_ids[offset+j] == ref_local_point_ids[i][k])) {
619
620 if (match_flag[j]) {
621 PUT_ERR("ERROR in yac_point_sphere_part_search_NN "
622 "duplicated result");
623 } else {
624 match_flag[j] = 1;
625 match_count++;
626 }
627 }
628 }
629 }
630 if (match_count != ref_num_local_point_ids[i]) {
631 PUT_ERR("ERROR in yac_point_sphere_part_search_NN result mismatch");
632 }
633 }
634 }
635
636 free(local_point_ids);
637 free(result_coordinates_xyz);
639}
640
641static void utest_bnd_circle_search() {
642
643 // Test 1: basic bounding circle search with known points
644 {
645 double x_coordinates[] = {0.0, 10.0, 20.0, 30.0, 40.0};
646 double y_coordinates[] = {0.0, 0.0, 0.0, 0.0, 0.0};
647 enum {NUM_POINTS = sizeof(x_coordinates) / sizeof(x_coordinates[0])};
648 double xyz_coordinates[NUM_POINTS][3];
649 yac_int global_ids[NUM_POINTS];
650 for (size_t i = 0; i < NUM_POINTS; ++i) {
651 LLtoXYZ_deg(x_coordinates[i], y_coordinates[i], xyz_coordinates[i]);
652 global_ids[i] = (yac_int)i;
653 }
654
655 struct point_sphere_part_search * search =
657 NUM_POINTS, (yac_const_coordinate_pointer)xyz_coordinates, global_ids);
658
659 // Create a bounding circle centered at (15, 0)/(35, 0) with angle
660 // covering points at (10, 0)/(30, 0) and (20, 0)/(40, 0)
661 struct bounding_circle bnd_circles[2];
662 double inc_angle = 6.0 * YAC_RAD; // ~6 degrees
663 LLtoXYZ_deg(15.0, 0.0, bnd_circles[0].base_vector);
664 bnd_circles[0].inc_angle =
666 bnd_circles[0].sq_crd = DBL_MAX;
667 LLtoXYZ_deg(35.0, 0.0, bnd_circles[1].base_vector);
668 bnd_circles[1].inc_angle =
670 bnd_circles[1].sq_crd = DBL_MAX;
671
672 size_t * local_point_ids = NULL;
673 size_t local_point_ids_array_size = 0;
674 size_t num_local_point_ids[2];
675
676 // Perform search
678 search, 2, bnd_circles,
679 &local_point_ids, &local_point_ids_array_size, num_local_point_ids);
680
681 struct {
682 size_t count;
683 size_t * ids;
684 } ref_results[2] =
685 {{.count = 2, .ids = (size_t[]){1,2}},
686 {.count = 2, .ids = (size_t[]){3,4}}};
687
688 // Check against reference data
689 for (size_t i = 0, offset = 0; i < 2; ++i) {
690
691 if (num_local_point_ids[i] != ref_results[i].count){
692 PUT_ERR("utest_bnd_circle_search: wrong number of points");
693 }
694
695 int match_flag[ref_results[i].count];
696 size_t match_count = 0;
697 memset(match_flag, 0, ref_results[i].count * sizeof(match_flag[0]));
698
699 size_t * curr_local_point_ids = local_point_ids + offset;
700 for (size_t j = 0; j < num_local_point_ids[i]; ++j) {
701 for (size_t k = 0; k < ref_results[i].count; ++k) {
702 if (curr_local_point_ids[j] == ref_results[i].ids[k]) {
703
704 if (match_flag[k]) {
705 PUT_ERR("utest_bnd_circle_search: multiple matches");
706 } else {
707 match_count++;
708 }
709 }
710 }
711 }
712
713 if (match_count != ref_results[i].count) {
714 PUT_ERR("utest_bnd_circle_search: wrong points");
715 }
716
717 offset += num_local_point_ids[i];
718 }
719
720 free(local_point_ids);
722 }
723
724 // Test 2: empty search (null search)
725 {
726 struct point_sphere_part_search * search =
728
729 struct bounding_circle bnd_circle;
730 LLtoXYZ_deg(0.0, 0.0, bnd_circle.base_vector);
731 double inc_angle = 10.0 * YAC_RAD;
732 bnd_circle.inc_angle = sin_cos_angle_new(sin(inc_angle), cos(inc_angle));
733 bnd_circle.sq_crd = DBL_MAX;
734
735 size_t * local_point_ids = NULL;
736 size_t local_point_ids_array_size = 0;
737 size_t num_local_point_ids[1] = {(size_t)-1};
738
740 search, 1, &bnd_circle,
741 &local_point_ids, &local_point_ids_array_size, num_local_point_ids);
742
743 if (num_local_point_ids[0] != 0)
744 PUT_ERR("utest_bnd_circle_search: empty search should find 0 points");
745
746 free(local_point_ids);
748 }
749
750 // Test 3: zero bounding circles
751 {
752 double xyz_coordinates[3][3] = {{1,0,0}, {0,1,0}, {0,0,1}};
753 yac_int global_ids[3] = {0, 1, 2};
754
755 struct point_sphere_part_search * search =
757 3, (yac_const_coordinate_pointer)xyz_coordinates, global_ids);
758
759 size_t * local_point_ids = NULL;
760 size_t local_point_ids_array_size = 0;
761
762 // Should handle zero circles gracefully
764 search, 0, NULL,
765 &local_point_ids, &local_point_ids_array_size, NULL);
766
767 free(local_point_ids);
769 }
770
771 // Test 4: single point within bounding circle
772 {
773 double xyz_coordinate[1][3];
774 LLtoXYZ_deg(45.0, 30.0, xyz_coordinate[0]);
775 yac_int global_id[1] = {0};
776
777 struct point_sphere_part_search * search =
779 1, (yac_const_coordinate_pointer)xyz_coordinate, global_id);
780
781 // Bounding circle centered at same location
782 struct bounding_circle bnd_circle;
783 memcpy(bnd_circle.base_vector, xyz_coordinate[0], sizeof(xyz_coordinate[0]));
784 double inc_angle = 1.0 * YAC_RAD;
785 bnd_circle.inc_angle = sin_cos_angle_new(sin(inc_angle), cos(inc_angle));
786 bnd_circle.sq_crd = DBL_MAX;
787
788 size_t * local_point_ids = NULL;
789 size_t local_point_ids_array_size = 0;
790 size_t num_local_point_ids[1];
791
793 search, 1, &bnd_circle,
794 &local_point_ids, &local_point_ids_array_size, num_local_point_ids);
795
796 if (num_local_point_ids[0] != 1)
797 PUT_ERR("utest_bnd_circle_search: single point search failed (count)");
798 if (local_point_ids[0] != 0)
799 PUT_ERR("utest_bnd_circle_search: single point search failed (id)");
800
801 free(local_point_ids);
803 }
804
805 // Test 5: no points within bounding circle
806 {
807 double x_coordinates[] = {0.0, 90.0, 180.0, -90.0};
808 double y_coordinates[] = {0.0, 0.0, 0.0, 0.0};
809 enum {NUM_POINTS = sizeof(x_coordinates) / sizeof(x_coordinates[0])};
810 double xyz_coordinates[NUM_POINTS][3];
811 yac_int global_ids[NUM_POINTS];
812 for (size_t i = 0; i < NUM_POINTS; ++i) {
813 LLtoXYZ_deg(x_coordinates[i], y_coordinates[i], xyz_coordinates[i]);
814 global_ids[i] = (yac_int)i;
815 }
816
817 struct point_sphere_part_search * search =
819 NUM_POINTS, (yac_const_coordinate_pointer)xyz_coordinates, global_ids);
820
821 // Bounding circle at north pole with small angle
822 struct bounding_circle bnd_circle;
823 LLtoXYZ_deg(0.0, 90.0, bnd_circle.base_vector);
824 double inc_angle = 0.5 * YAC_RAD; // Very small, won't contain any point
825 bnd_circle.inc_angle = sin_cos_angle_new(sin(inc_angle), cos(inc_angle));
826 bnd_circle.sq_crd = DBL_MAX;
827
828 size_t * local_point_ids = NULL;
829 size_t local_point_ids_array_size = 0;
830 size_t num_local_point_ids[1];
831
833 search, 1, &bnd_circle,
834 &local_point_ids, &local_point_ids_array_size, num_local_point_ids);
835
836 if (num_local_point_ids[0] != 0)
837 PUT_ERR("utest_bnd_circle_search: should find 0 points");
838
839 free(local_point_ids);
841 }
842
843 // Test 6: large bounding circle containing all points
844 {
845 double x_coordinates[] = {0.0, 1.0, 2.0, 3.0, 4.0};
846 double y_coordinates[] = {0.0, 0.0, 0.0, 0.0, 0.0};
847 enum {NUM_POINTS = sizeof(x_coordinates) / sizeof(x_coordinates[0])};
848 double xyz_coordinates[NUM_POINTS][3];
849 yac_int global_ids[NUM_POINTS];
850 for (size_t i = 0; i < NUM_POINTS; ++i) {
851 LLtoXYZ_deg(x_coordinates[i], y_coordinates[i], xyz_coordinates[i]);
852 global_ids[i] = (yac_int)i;
853 }
854
855 struct point_sphere_part_search * search =
857 NUM_POINTS, (yac_const_coordinate_pointer)xyz_coordinates, global_ids);
858
859 // Large bounding circle centered at (2, 0) containing all points
860 struct bounding_circle bnd_circle;
861 LLtoXYZ_deg(2.0, 0.0, bnd_circle.base_vector);
862 double inc_angle = 10.0 * YAC_RAD; // Large enough to contain all
863 bnd_circle.inc_angle = sin_cos_angle_new(sin(inc_angle), cos(inc_angle));
864 bnd_circle.sq_crd = DBL_MAX;
865
866 size_t * local_point_ids = NULL;
867 size_t local_point_ids_array_size = 0;
868 size_t num_local_point_ids[1];
869
871 search, 1, &bnd_circle,
872 &local_point_ids, &local_point_ids_array_size, num_local_point_ids);
873
874 if (num_local_point_ids[0] != NUM_POINTS)
875 PUT_ERR("utest_bnd_circle_search: should find all points");
876
877 free(local_point_ids);
879 }
880}
881
882static void utest_bnd_circle_random_test(
883 size_t num_points, size_t num_circles) {
884
885 double (*xyz_coordinates)[3] = xmalloc(num_points * sizeof(*xyz_coordinates));
886 yac_int * global_ids = xmalloc(num_points * sizeof(*global_ids));
887 struct bounding_circle * bnd_circles =
888 xmalloc(num_circles * sizeof(*bnd_circles));
889
890 // Generate random points
891 for (size_t i = 0; i < num_points; ++i) {
892 utest_gen_random_point(xyz_coordinates[i]);
893 global_ids[i] = (yac_int)i;
894 }
895
896 // Generate random bounding circles
897 for (size_t i = 0; i < num_circles; ++i)
898 utest_gen_random_bnd_circle(&bnd_circles[i]);
899
900 struct point_sphere_part_search * search =
902 num_points, (yac_const_coordinate_pointer)xyz_coordinates, global_ids);
903
904 size_t * local_point_ids = NULL;
905 size_t local_point_ids_array_size = 0;
906 size_t * num_local_point_ids = xmalloc(num_circles * sizeof(*num_local_point_ids));
907
909 search, num_circles, bnd_circles,
910 &local_point_ids, &local_point_ids_array_size, num_local_point_ids);
911
912 // Verify results by brute-force checking
913 size_t offset = 0;
914 for (size_t i = 0; i < num_circles; ++i) {
915
916 // Compute reference: find all points within bounding circle
917 size_t ref_count = 0;
918 size_t * ref_ids = xmalloc(num_points * sizeof(*ref_ids));
919
920 for (size_t j = 0; j < num_points; ++j) {
921 struct sin_cos_angle angle_to_point =
922 get_vector_angle_2(bnd_circles[i].base_vector, xyz_coordinates[j]);
923 if (compare_angles(angle_to_point, bnd_circles[i].inc_angle) <= 0) {
924 ref_ids[ref_count++] = j;
925 }
926 }
927
928 // Check count matches
929 if (num_local_point_ids[i] != ref_count) {
930 PUT_ERR("utest_bnd_circle_random_test: wrong number of points found");
931 } else {
932 // Check all returned points are in reference set
933 for (size_t j = 0; j < num_local_point_ids[i]; ++j) {
934 int found = 0;
935 for (size_t k = 0; k < ref_count; ++k) {
936 if (local_point_ids[offset + j] == ref_ids[k]) {
937 found = 1;
938 break;
939 }
940 }
941 if (!found)
942 PUT_ERR("utest_bnd_circle_random_test: unexpected point in result");
943 }
944
945 // Check all reference points are in returned set
946 for (size_t k = 0; k < ref_count; ++k) {
947 int found = 0;
948 for (size_t j = 0; j < num_local_point_ids[i]; ++j) {
949 if (local_point_ids[offset + j] == ref_ids[k]) {
950 found = 1;
951 break;
952 }
953 }
954 if (!found)
955 PUT_ERR("utest_bnd_circle_random_test: missing point in result");
956 }
957 }
958
959 offset += num_local_point_ids[i];
960 free(ref_ids);
961 }
962
963 free(local_point_ids);
964 free(num_local_point_ids);
965 free(bnd_circles);
966 free(global_ids);
967 free(xyz_coordinates);
969}
#define YAC_RAD
static struct sin_cos_angle get_vector_angle_2(double const a[3], double const b[3])
Definition geometry.h:493
static int points_are_identically(double const *a, double const *b)
Definition geometry.h:727
static const struct sin_cos_angle SIN_COS_M_PI
Definition geometry.h:41
static void LLtoXYZ_deg(double lon, double lat, double p_out[])
Definition geometry.h:278
static int compare_angles(struct sin_cos_angle a, struct sin_cos_angle b)
Definition geometry.h:506
#define yac_angle_tol
Definition geometry.h:26
static double get_vector_angle(double const a[3], double const b[3])
Definition geometry.h:472
static struct sin_cos_angle sin_cos_angle_new(double sin, double cos)
Definition geometry.h:483
#define xmalloc(size)
Definition ppm_xfuncs.h:66
void yac_point_sphere_part_search_NNN_ubound(struct point_sphere_part_search *search, size_t num_points, yac_coordinate_pointer coordinates_xyz, size_t n, struct sin_cos_angle *angles)
void yac_point_sphere_part_search_NNN_bnd_circle(struct point_sphere_part_search *search, size_t num_bnd_circles, struct bounding_circle *bnd_circles, size_t n, size_t **local_point_ids, size_t *local_point_ids_array_size, size_t *num_local_point_ids)
void yac_delete_point_sphere_part_search(struct point_sphere_part_search *search)
struct point_sphere_part_search * yac_point_sphere_part_search_new(size_t num_points, yac_const_coordinate_pointer coordinates_xyz, yac_int const *ids)
void yac_point_sphere_part_search_bnd_circle(struct point_sphere_part_search *search, size_t num_bnd_circles, const_bounding_circle_pointer bnd_circles, size_t **local_point_ids, size_t *local_point_ids_array_size, size_t *num_local_point_ids)
void yac_point_sphere_part_search_NN(struct point_sphere_part_search *search, size_t num_points, double(*coordinates_xyz)[3], double *cos_angles, double(**result_coordinates_xyz)[3], size_t *result_coordinates_xyz_array_size, size_t **local_point_ids, size_t *local_point_ids_array_size, size_t *num_local_point_ids)
void yac_point_sphere_part_search_NNN(struct point_sphere_part_search *search, size_t num_points, double(*coordinates_xyz)[3], size_t n, double **cos_angles, size_t *cos_angles_array_size, double(**result_coordinates_xyz)[3], size_t *result_coordinates_xyz_array_size, size_t **local_point_ids, size_t *local_point_ids_array_size, size_t *num_local_point_ids)
algorithm for searching cells and points on a grid
struct sin_cos_angle inc_angle
angle between the middle point and the boundary of the spherical cap
Definition geometry.h:53
double base_vector[3]
Definition geometry.h:51
double sq_crd
Definition geometry.h:56
double sin
Definition geometry.h:33
double cos
Definition geometry.h:33
#define TEST_EXIT_CODE
Definition tests.h:15
#define PUT_ERR(string)
Definition tests.h:10
#define MIN(a, b)
Definition toy_common.h:29
static void LLtoXYZ(double lon, double lat, double p_out[])
Definition toy_scrip.c:587
double const (* yac_const_coordinate_pointer)[3]
Definition yac_types.h:22
YAC_INT yac_int
Definition yac_types.h:15
double(* yac_coordinate_pointer)[3]
Definition yac_types.h:21