YAC 3.21.0
Yet Another Coupler
Loading...
Searching...
No Matches
interp_method_avg.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#ifdef HAVE_CONFIG_H
6// Get the definition of the 'restrict' keyword.
7#include "config.h"
8#endif
9
10#include <string.h>
11
18#include "grids/grid_cell.h"
21#include "instrument_internal.h"
22#include "yac_mpi_common.h"
23
24static size_t do_search_avg(struct interp_method * method,
25 struct yac_interp_grid * interp_grid,
26 size_t * tgt_points, size_t count,
27 struct yac_interp_weights * weights,
28 int * interpolation_complete);
29static void delete_avg(struct interp_method * method);
30
31typedef int (*func_compute_weights)(
32 double[3], size_t, yac_const_coordinate_pointer, int*, double*,
33 enum yac_cell_type cell_type);
34
35static struct interp_method_vtable
39
45
47 double * point, size_t field_cell, size_t cell_size,
48 size_t const * vertex_to_cell, size_t const * vertex_to_cell_offsets,
49 yac_const_coordinate_pointer field_coordinates,
50 struct yac_grid_cell * cell_buffer) {
51
52 if (cell_size == 0) return 0;
53
54 cell_buffer->num_corners = cell_size;
55
56 yac_coordinate_pointer coordinates_xyz;
57
58 if (cell_size > cell_buffer->array_size) {
59 cell_buffer->coordinates_xyz =
60 ((coordinates_xyz =
62 cell_buffer->coordinates_xyz,
63 cell_size * sizeof(*coordinates_xyz))));
64 cell_buffer->edge_type =
66 cell_buffer->edge_type, cell_size * sizeof(cell_buffer->edge_type));
67 for (size_t i = 0; i < cell_size; ++i)
68 cell_buffer->edge_type[i] = YAC_GREAT_CIRCLE_EDGE;
69 cell_buffer->array_size = cell_size;
70 } else {
71 coordinates_xyz = cell_buffer->coordinates_xyz;
72 }
73
74 size_t const * field_cell_vertices =
75 vertex_to_cell + vertex_to_cell_offsets[field_cell];
76 for (size_t i = 0; i < cell_size; ++i)
77 memcpy(coordinates_xyz[i], field_coordinates[field_cell_vertices[i]],
78 sizeof(*coordinates_xyz));
79
80 return yac_point_in_cell(point, *cell_buffer);
81}
82
83#define IS_GC(x) (((x) == YAC_GREAT_CIRCLE_EDGE) || ((x) == YAC_LON_CIRCLE_EDGE))
84#define IS_LON_LAT(x) (((x) == YAC_LAT_CIRCLE_EDGE) || ((x) == YAC_LON_CIRCLE_EDGE))
85
87 enum yac_edge_type const * edge_types,
88 size_t const * edge_indices, size_t num_edges) {
89
90 if (edge_types == NULL) return YAC_GREAT_CIRCLE_CELL;
91
92 int edge_type_flag = 0;
93
94 if (num_edges == 4) {
95
96 enum yac_edge_type temp_edges[4] =
97 {edge_types[edge_indices[0]],
98 edge_types[edge_indices[1]],
99 edge_types[edge_indices[2]],
100 edge_types[edge_indices[3]]};
101 if (IS_LON_LAT(temp_edges[0]) &&
102 IS_LON_LAT(temp_edges[1]) &&
103 (temp_edges[0] == temp_edges[2]) &&
104 (temp_edges[1] == temp_edges[3]) &&
105 (temp_edges[0] != temp_edges[1]))
106 return YAC_LON_LAT_CELL;
107 }
108
109 // check all edges
110 // in case of a lat edge, set second bit to 1
111 // in all other cases set first bit to 1
112 for (size_t i = 0; i < num_edges; ++i)
113 edge_type_flag |= (1 << (edge_types[edge_indices[i]] == YAC_LAT_CIRCLE_EDGE));
114
115 enum yac_cell_type cell_types[4] =
116 {YAC_GREAT_CIRCLE_CELL, // if num_edges == 0
117 YAC_GREAT_CIRCLE_CELL, // if there are not lat circle edges
118 YAC_LAT_CELL, // if there are only lat circle edges
119 YAC_MIXED_CELL}; // if there are gc and lat edges
120 return cell_types[edge_type_flag];
121}
122#undef IS_LON_LAT
123#undef IS_GC
124
125static size_t do_search_avg (struct interp_method * method,
126 struct yac_interp_grid * interp_grid,
127 size_t * tgt_points, size_t count,
128 struct yac_interp_weights * weights,
129 int * interpolation_complete) {
130
131 if (*interpolation_complete) return 0;
132
133 YAC_INSTRUMENT_START(YAC_INSTR_INTERP_SEARCH_AVG);
134
135 struct interp_method_avg * method_avg = (struct interp_method_avg *)method;
136 func_compute_weights compute_weights = method_avg->compute_weights;
137
139
140 enum yac_location src_field_location =
142
144
145 // get coordinates of target points
146 yac_coordinate_pointer tgt_coords = xmalloc(count * sizeof(*tgt_coords));
148 interp_grid, tgt_points, count, tgt_coords);
149
150 size_t * size_t_buffer = xmalloc(2 * count * sizeof(*size_t_buffer));
151 size_t * src_field_cells = size_t_buffer;
152 size_t * reorder_idx = size_t_buffer + count;
153
154 // get matching source cells for all target points
156 interp_grid, tgt_coords, count, src_field_cells);
157
158 size_t const * src_field_cell_to_vertex;
159 size_t const * src_field_cell_to_vertex_offsets;
160 size_t const * src_field_cell_to_edge;
161 size_t const * src_field_cell_to_edge_offsets;
162 int const * src_field_num_vertices_per_cell;
163 enum yac_edge_type const * src_edge_types;
164
165 // if the source field is location at cell points
166 if (src_field_location == YAC_LOC_CELL) {
167
168 // generate auxiliary grid for all search result cells
170 interp_grid, src_field_cells, count,
171 (size_t**)&src_field_cell_to_vertex,
172 (size_t**)&src_field_cell_to_vertex_offsets,
173 (int**)&src_field_num_vertices_per_cell);
174
175 struct yac_const_basic_grid_data * src_grid_data =
177 yac_const_coordinate_pointer src_field_coordinates =
179
180 struct yac_grid_cell cell_buffer;
181 yac_init_grid_cell(&cell_buffer);
182
183 // for all target point
184 for (size_t i = 0; i < count; ++i) {
185
186 size_t curr_src_cell = src_field_cells[i];
187 if (curr_src_cell == SIZE_MAX) continue;
188
189 double * curr_tgt_coord = tgt_coords[i];
190 size_t const * curr_vertices =
191 src_grid_data->cell_to_vertex +
192 src_grid_data->cell_to_vertex_offsets[curr_src_cell];
193 size_t curr_num_vertices =
194 src_grid_data->num_vertices_per_cell[curr_src_cell];
195
196 size_t result_src_field_cell = SIZE_MAX;
197
198 // for all auxiliary of the current source result cell
199 for (size_t j = 0; j < curr_num_vertices; ++j) {
200
201 size_t src_field_cell = curr_vertices[j];
202 size_t src_field_cell_size =
203 (size_t)src_field_num_vertices_per_cell[src_field_cell];
204
205 // check whether the target point is in the current source field cell
207 curr_tgt_coord, src_field_cell, src_field_cell_size,
208 src_field_cell_to_vertex, src_field_cell_to_vertex_offsets,
209 src_field_coordinates, &cell_buffer)) {
210 result_src_field_cell = src_field_cell;
211 break;
212 }
213 }
214
215 src_field_cells[i] = result_src_field_cell;
216 }
217 yac_free_grid_cell(&cell_buffer);
218
219 src_edge_types = NULL;
220 src_field_cell_to_edge = NULL;
221 src_field_cell_to_edge_offsets = NULL;
222 } else {
223
226 src_field_cell_to_vertex = grid_data->cell_to_vertex;
227 src_field_cell_to_vertex_offsets = grid_data->cell_to_vertex_offsets;
228 src_field_num_vertices_per_cell = grid_data->num_vertices_per_cell;
229 src_edge_types = grid_data->edge_type;
230 src_field_cell_to_edge = grid_data->cell_to_edge;
231 src_field_cell_to_edge_offsets = grid_data->cell_to_edge_offsets;
232 }
233 yac_const_coordinate_pointer src_field_coordinates =
235 const_yac_int_pointer src_global_ids =
237
238 // sort target points, for which we found a source cell, to the beginning of
239 // the array
240 for (size_t i = 0; i < count; ++i) reorder_idx[i] = i;
241 yac_quicksort_index_size_t_size_t(src_field_cells, count, reorder_idx);
242
243 size_t result_count = 0;
244 for (result_count = 0; result_count < count; ++result_count)
245 if (src_field_cells[result_count] == SIZE_MAX) break;
246
247 size_t total_num_weights = 0;
248 size_t max_num_vertices_per_cell = 0;
249 size_t * num_weights_per_tgt =
250 xmalloc(result_count * sizeof(*num_weights_per_tgt));
251
252 // get the number of vertices per target cell and the total number of vertices
253 // required for the interpolation (may contain duplicated vertices)
254 for (size_t i = 0; i < result_count; ++i) {
255 size_t curr_num_vertices =
256 (size_t)(src_field_num_vertices_per_cell[src_field_cells[i]]);
257 num_weights_per_tgt[i] = curr_num_vertices;
258 total_num_weights += curr_num_vertices;
259 if (curr_num_vertices > max_num_vertices_per_cell)
260 max_num_vertices_per_cell = curr_num_vertices;
261 }
262 if (src_field_location == YAC_LOC_CELL)
263 free((void*)src_field_num_vertices_per_cell);
264
265 const_int_pointer src_field_mask =
267
268 yac_coordinate_pointer src_coord_buffer =
269 xmalloc(max_num_vertices_per_cell * sizeof(*src_coord_buffer));
270 int * mask_buffer =
271 (src_field_mask != NULL)?
272 xmalloc(max_num_vertices_per_cell * sizeof(*mask_buffer)):NULL;
273 double * w = xmalloc(total_num_weights * sizeof(*w));
274 size_t * src_points = xmalloc(total_num_weights * sizeof(*src_points));
275
276 // for each target point, extract relevant source data and compute the weights
277 // based on that
278 total_num_weights = 0;
279 for (size_t i = 0; i < result_count;) {
280
281 size_t curr_num_vertices = num_weights_per_tgt[i];
282
283 const_size_t_pointer curr_cell_to_vertex =
284 src_field_cell_to_vertex +
285 src_field_cell_to_vertex_offsets[src_field_cells[i]];
286 const_size_t_pointer curr_cell_to_edge =
287 (src_edge_types != NULL)?
288 (src_field_cell_to_edge +
289 src_field_cell_to_edge_offsets[src_field_cells[i]]):NULL;
290 double * curr_weights = w + total_num_weights;
291
292 // get the index of the source cell corner with the lowest global id
293 // (this is being used, that the order in which the source corners are
294 // processes is decomposition independent)
295 size_t lowest_global_id_idx = 0;
296 {
297 yac_int lowest_global_id = src_global_ids[curr_cell_to_vertex[0]];
298 for (size_t j = 1; j < curr_num_vertices; ++j) {
299 if (src_global_ids[curr_cell_to_vertex[j]] < lowest_global_id) {
300 lowest_global_id = src_global_ids[curr_cell_to_vertex[j]];
301 lowest_global_id_idx = j;
302 }
303 }
304 }
305
306 // get the mask and the source coordinates of the source vertices required
307 // for the current target point
308 for (size_t j = 0, l = lowest_global_id_idx; j < curr_num_vertices;
309 ++j, ++total_num_weights, ++l) {
310
311 if (l == curr_num_vertices) l = 0;
312
313 size_t curr_vertex_idx = curr_cell_to_vertex[l];
314 src_points[total_num_weights] = curr_vertex_idx;
315 for (size_t k = 0; k < 3; ++k)
316 src_coord_buffer[j][k] = src_field_coordinates[curr_vertex_idx][k];
317 if (src_field_mask != NULL)
318 mask_buffer[j] = src_field_mask[curr_vertex_idx];
319 }
320
321 enum yac_cell_type cell_type =
323 src_edge_types, curr_cell_to_edge, curr_num_vertices);
324
325 // compute the weights
326 if (compute_weights(tgt_coords[reorder_idx[i]], curr_num_vertices,
327 (yac_const_coordinate_pointer)src_coord_buffer,
328 mask_buffer, curr_weights, cell_type)) {
329
330 // if weight computation was successful
331 num_weights_per_tgt[i] = curr_num_vertices;
332 ++i;
333 } else {
334
335 // if no weights could be computed
336 total_num_weights -= curr_num_vertices;
337 result_count--;
338 src_field_cells[i] = src_field_cells[result_count];
339 size_t temp_reorder_idx = reorder_idx[i];
340 reorder_idx[i] = reorder_idx[result_count];
341 reorder_idx[result_count] = temp_reorder_idx;
342 }
343 }
344 if (src_field_location == YAC_LOC_CELL) {
345 free((void*)src_field_cell_to_vertex);
346 free((void*)src_field_cell_to_vertex_offsets);
347 }
348
349 // move the non-interpolated target points to the end
350 for (size_t i = 0; i < count; ++i) src_field_cells[reorder_idx[i]] = i;
351 yac_quicksort_index_size_t_size_t(src_field_cells, count, tgt_points);
352
353 free(tgt_coords);
354 free(mask_buffer);
355 free(src_coord_buffer);
356 free(size_t_buffer);
357
358 struct remote_points tgts = {
359 .data =
361 interp_grid, tgt_points, result_count),
362 .count = result_count};
363 struct remote_point * srcs =
365 interp_grid, 0, src_points, total_num_weights);
366
367 // store weights
369 weights, &tgts, num_weights_per_tgt, srcs, w);
370
371 free(tgts.data);
372 free(srcs);
373 free(w);
374 free(num_weights_per_tgt);
375 free(src_points);
376
377 YAC_INSTRUMENT_STOP(YAC_INSTR_INTERP_SEARCH_AVG);
378
379 return result_count;
380}
381
383 double tgt_coords[3], size_t num_vertices,
384 yac_const_coordinate_pointer src_coords, int * src_mask, double * weights,
385 enum yac_cell_type cell_type) {
386
387 UNUSED(tgt_coords);
388 UNUSED(cell_type);
389 UNUSED(src_coords);
390
391 size_t num_unmasked_points = 0;
392
393 // if we have a source mask, check whether any points is masked
394 if (src_mask != NULL) {
395 for (size_t i = 0; i < num_vertices; ++i)
396 if (src_mask[i]) num_unmasked_points++;
397 if (num_unmasked_points == 0) return 0;
398
399 double weight = 1.0 / (double)num_unmasked_points;
400 for (size_t i = 0; i < num_vertices; ++i)
401 weights[i] = (src_mask[i])?weight:0.0;
402
403 } else {
404 double weight = 1.0 / (double)num_vertices;
405 for (size_t i = 0; i < num_vertices; ++i) weights[i] = weight;
406 }
407 return 1;
408}
409
411 double tgt_coords[3], size_t num_vertices,
412 yac_const_coordinate_pointer src_coords, int * src_mask, double * weights,
413 enum yac_cell_type cell_type) {
414
415 UNUSED(tgt_coords);
416 UNUSED(cell_type);
417 UNUSED(src_coords);
418
419 // if we have a source mask, check whether any points is masked
420 if (src_mask != NULL)
421 for (size_t i = 0; i < num_vertices; ++i)
422 if (!src_mask[i]) return 0;
423
424 double weight = 1.0 / (double)num_vertices;
425 for (size_t i = 0; i < num_vertices; ++i) weights[i] = weight;
426
427 return 1;
428}
429
431 double tgt_coords[3], size_t num_vertices,
432 yac_const_coordinate_pointer src_coords, int * src_mask, double * weights,
433 enum yac_cell_type cell_type) {
434
435 UNUSED(cell_type);
436
437 // if there is a source mask, check if there are unmasked vertices
438 if (src_mask != NULL) {
439 int has_unmasked = 0;
440 for (size_t i = 0; i < num_vertices; ++i) has_unmasked |= src_mask[i];
441 if (!has_unmasked) return 0;
442 }
443
444 if (src_mask != NULL) {
445 for (size_t i = 0; i < num_vertices; ++i) {
446
447 if (src_mask[i]) {
448
449 double distance =
450 get_vector_angle(tgt_coords, (double*)(src_coords[i]));
451
452 // if the target and source point are nearly identical
453 if (distance < yac_angle_tol) {
454 for (size_t j = 0; j < num_vertices; ++j) weights[j] = 0.0;
455 weights[i] = 1.0;
456 return 1;
457 }
458
459 weights[i] = 1.0 / distance;
460 } else {
461 weights[i] = 0.0;
462 }
463 }
464 } else {
465 for (size_t i = 0; i < num_vertices; ++i) {
466
467 double distance =
468 get_vector_angle(tgt_coords, (double*)(src_coords[i]));
469
470 // if the target and source point are nearly identical
471 if (distance < yac_angle_tol) {
472 for (size_t j = 0; j < num_vertices; ++j) weights[j] = 0.0;
473 weights[i] = 1.0;
474 return 1;
475 }
476
477 weights[i] = 1.0 / distance;
478 }
479 }
480
481 // compute scaling factor for the weights
482 double inv_distance_sum = 0.0;
483 for (size_t i = 0; i < num_vertices; ++i)
484 inv_distance_sum += weights[i];
485 double scale = 1.0 / inv_distance_sum;
486
487 for (size_t i = 0; i < num_vertices; ++i) weights[i] *= scale;
488
489 return 1;
490}
491
493 double tgt_coords[3], size_t num_vertices,
494 yac_const_coordinate_pointer src_coords, int * src_mask, double * weights,
495 enum yac_cell_type cell_type) {
496
497 UNUSED(cell_type);
498
499 for (size_t i = 0; i < num_vertices; ++i) {
500
501 double distance =
502 get_vector_angle(tgt_coords, (double*)(src_coords[i]));
503
504 // if the target and source point are nearly identical
505 if (distance < yac_angle_tol) {
506 if ((src_mask != NULL) && !src_mask[i]) return 0;
507 for (size_t j = 0; j < num_vertices; ++j) weights[j] = 0.0;
508 weights[i] = 1.0;
509 return 1;
510 }
511
512 weights[i] = 1.0 / distance;
513 }
514
515 // if there is a source mask, check if there are masked vertices
516 // (we do this here because there may be a matching source point
517 // that is not masked)
518 if (src_mask != NULL)
519 for (size_t i = 0; i < num_vertices; ++i) if(!src_mask[i]) return 0;
520
521 // compute scaling factor for the weights
522 double inv_distance_sum = 0.0;
523 for (size_t i = 0; i < num_vertices; ++i)
524 inv_distance_sum += weights[i];
525 double scale = 1.0 / inv_distance_sum;
526
527 for (size_t i = 0; i < num_vertices; ++i) weights[i] *= scale;
528
529 return 1;
530}
531
532// compute the spherical barycentric coordinates of the given vertices with
533// respect to the given triangle
534// returns 1, if barycentric coordinates were successfully computed, 0 otherwise
536 double * barycentric_coords, size_t triangle_indices[3],
538 char const * caller) {
539
540 double A[3][3];
541 lapack_int n = 3, nrhs = 1, lda = n, ldx = n, ipiv[3];
542 for (int i = 0; i < 3; ++i)
543 memcpy(A[i], grid_coords[triangle_indices[i]], sizeof(*grid_coords));
544
545 // Check whether any two vertices of the triangle are identical -> triangle
546 // is actually a line and computation of barycentric coordinates would fail
547 // unless the search coordinate is exactly on this line. If the search
548 // coordinate is on this line, there should be a triangle that has one edge,
549 // which matches this line and will then provide the approriate result.
550 for (int i = 0, j = 2; i < 3; j = i, ++i) {
551 if (points_are_identically(A[i], A[j])) return 0;
552 }
553
554 // for a vertex v the spherical barycentric coordinates b are defined as
555 // follows
556 // A * b = v
557 // where: A is the matrix consisting of the vertex coordinates of the three
558 // corners of the triangle
559 // we compute b by solving this linear system using LAPACK
561 !LAPACKE_dgesv(
562 LAPACK_COL_MAJOR, n, nrhs, &A[0][0], lda, ipiv, barycentric_coords, ldx),
563 "(called from %s)"
564 "internal error (could not solve linear 3x3 system)\n"
565 "(vector: (% .6e;% .6e;% .6e)\n"
566 " triangle: ((% .6e;% .6e;% .6e),\n"
567 " (% .6e;% .6e;% .6e),\n"
568 " (% .6e;% .6e;% .6e))",
569 caller, barycentric_coords[0], barycentric_coords[1], barycentric_coords[2],
570 grid_coords[triangle_indices[0]][0],
571 grid_coords[triangle_indices[0]][1],
572 grid_coords[triangle_indices[0]][2],
573 grid_coords[triangle_indices[1]][0],
574 grid_coords[triangle_indices[1]][1],
575 grid_coords[triangle_indices[1]][2],
576 grid_coords[triangle_indices[2]][0],
577 grid_coords[triangle_indices[2]][1],
578 grid_coords[triangle_indices[2]][2])
579
580 return 1;
581}
582
583// returns 0 if edge_direction_vec points south; 1 otherwise
584static inline int get_lat_edge_ordering(double const * a, double const * b) {
585
586 return (a[0] * b[1] - a[1] * b[0]) < 0.0;
587}
588
591 double lon[2], double lat[2], int reorder[4]) {
592
593 // determine whether the lat neighbour to vertex 0 is at position 1 or 3
594 int lat_neigh_offset =
595 (fabs(coords[0][2] - coords[1][2]) <
596 fabs(coords[0][2] - coords[3][2]))?1:3;
597 // get the index of the coord which is closest to the equator
598 // (the one with the lower absolut z coordinate)
599 int closest_to_equator_idx =
600 (fabs(coords[0][2]) < fabs(coords[2][2]))?0:2;
601 int upper_edge_idx = ((coords[0][2]) > coords[2][2])?0:2;
602 int lower_edge_idx = upper_edge_idx^2;
603 int upper_edge_is_closer_to_pole =
604 closest_to_equator_idx == upper_edge_idx;
605 int closest_to_equator_edge_ordering =
607 coords[closest_to_equator_idx],
608 coords[(closest_to_equator_idx+lat_neigh_offset)%4]);
609 int cell_ordering =
610 closest_to_equator_edge_ordering ^ upper_edge_is_closer_to_pole;
611
612 XYZtoLL(
613 coords[closest_to_equator_idx], &lon[closest_to_equator_edge_ordering],
614 &lat[upper_edge_is_closer_to_pole]);
615 double dummy;
616 XYZtoLL(
617 coords[(closest_to_equator_idx + lat_neigh_offset)%4],
618 &lon[closest_to_equator_edge_ordering^1], &dummy);
619 lat[!upper_edge_is_closer_to_pole] =
620 M_PI_2 - acos(coords[closest_to_equator_idx ^ 2][2]);
621
622 reorder[cell_ordering] = lower_edge_idx;
623 reorder[cell_ordering^1] = (lower_edge_idx+lat_neigh_offset)%4;
624 reorder[2+cell_ordering] = upper_edge_idx;
625 reorder[2+(cell_ordering^1)] = (upper_edge_idx+lat_neigh_offset)%4;
626
627 if (lon[1] < lon[0]) lon[1] += 2.0 * M_PI;
628
629 YAC_ASSERT_F(((lon[0] - lon[1]) != 0.0) && ((lat[0] - lat[1]) != 0.0),
630 "internal error\n"
631 "lon[0] = %e lon[1] = %e lat[0] = %e lat[1] = %e\n"
632 "cell coords: ((% .4e; % .4e; % .4e),\n"
633 " (% .4e; % .4e; % .4e),\n"
634 " (% .4e; % .4e; % .4e),\n"
635 " (% .4e; % .4e; % .4e))",
636 lon[0], lon[1], lat[0], lat[1],
637 coords[0][0], coords[0][1], coords[0][2],
638 coords[1][0], coords[1][1], coords[1][2],
639 coords[2][0], coords[2][1], coords[2][2],
640 coords[3][0], coords[3][1], coords[3][2]);
641}
642
644 double point_coord[3], double cell_lon[2], double cell_lat[2],
645 double * point_lon, double * point_lat) {
646
648
649 double lon, lat;
650 XYZtoLL(point_coord, &lon, &lat);
651
652 // adjust target point lon to source cell lon
653 if (lon < cell_lon[0]) {
654 while (fabs(cell_lon[0] - lon) > M_PI) lon += 2.0 * M_PI;
655 } else {
656 while (fabs(lon - cell_lon[1]) > M_PI) lon -= 2.0 * M_PI;
657 }
658
659 *point_lon = lon;
660 *point_lat = lat;
661}
662
664 double point_lon, double point_lat, double cell_lon[2], double cell_lat[2]) {
665
666 return ((cell_lat[1] - cell_lat[0]) * (point_lat - cell_lat[0]) -
667 (cell_lat[1] - cell_lat[0]) * (point_lon - cell_lon[0])) > 0.0;
668}
669
671 double tgt_coords[3],
672 yac_const_coordinate_pointer src_coords, double * weights) {
673
674 // get lon lat bounds of the cell and the target point
675 double src_lon[2], src_lat[2], tgt_lon, tgt_lat;
676 int src_reorder[4];
677 get_cell_lon_lat_bounds(src_coords, src_lon, src_lat, src_reorder);
678 get_point_lon_lat(tgt_coords, src_lon, src_lat, &tgt_lon, &tgt_lat);
679
680 int triangle_idx =
681 determine_triangle_idx(tgt_lon, tgt_lat, src_lon, src_lat);
682
683 if (triangle_idx) {
684
685 double w[2];
686 w[0] = (tgt_lat - src_lat[1]) / (src_lat[0] - src_lat[1]);
687 w[1] = ((src_lat[1] - src_lat[0]) * (tgt_lon - src_lon[0])) /
688 ((src_lon[0] - src_lon[1]) * (src_lat[0] - src_lat[1]));
689
690 weights[src_reorder[0]] = w[0];
691 weights[src_reorder[2]] = w[1];
692 weights[src_reorder[3]] = 1.0 - (w[0] + w[1]);
693 weights[src_reorder[1]] = 0.0;
694 } else {
695
696 double w[2];
697 w[0] = (tgt_lon - src_lon[1]) / (src_lon[0] - src_lon[1]);
698 w[1] = ((src_lat[1] - src_lat[0]) * (tgt_lon - src_lon[1]) +
699 (src_lon[0] - src_lon[1]) * (tgt_lat - src_lat[1])) /
700 ((src_lat[0] - src_lat[1]) * (src_lon[0] - src_lon[1]));
701
702 weights[src_reorder[0]] = w[0];
703 weights[src_reorder[1]] = w[1];
704 weights[src_reorder[2]] = 1.0 - (w[0] + w[1]);
705 weights[src_reorder[3]] = 0.0;
706 }
707}
708
710 double * weights, int num_weights, int * src_mask, int partial_coverage) {
711
712 double const weight_sum_tol = 1e-9;
713 double const weight_tol = 1e-4;
714 int ret_value = 1;
715
716 // set irrelevant weights to zero
717 for (int i = 0; i < num_weights; ++i) {
718 if (weights[i] < weight_tol) {
719 weights[i] = 0.0;
720 }
721 }
722
723 // if a source point mask is available
724 if (src_mask) {
725
726 // if partial coverage is enabled
727 if (partial_coverage) {
728
729 // apply mask
730 for (int i = 0; i < num_weights; ++i) {
731 if (!src_mask[i]) {
732 weights[i] = 0.0;
733 }
734 }
735
736 } else {
737
738 // check for required weights that are masked out
739 for (int i = 0; (i < num_weights) && ret_value; ++i) {
740 if (!src_mask[i] && (weights[i] > 0.0)) {
741 ret_value = 0;
742 }
743 }
744 }
745 }
746
747 // if there were no issues until this point, scale the weights
748 if (ret_value) {
749
750 // compute the sum of all weights
751 double weight_sum = 0.0;
752 for (int i = 0; i < num_weights; ++i) {
753 weight_sum += weights[i];
754 }
755
756 // if the weight sum is valid
757 if (weight_sum > weight_sum_tol) {
758
759 double scale = 1.0 / weight_sum;
760 for (int i = 0; i < num_weights; ++i) {
761 weights[i] *= scale;
762 }
763
764 } else {
765 ret_value = 0;
766 }
767 }
768
769 return ret_value;
770}
771
794 double tgt_coords[3], size_t num_vertices,
795 yac_const_coordinate_pointer src_coords, int * src_mask, double * weights,
796 enum yac_cell_type cell_type, int partial_coverage) {
797
798 char const * routine = "compute_weights_bary";
799
800 // if all source points are masked
801 if (src_mask != NULL) {
802 size_t i;
803 for (i = 0; i < num_vertices; ++i) if (src_mask[i] != 0) break;
804 if (i == num_vertices) return 0;
805 }
806
807 int ret_value = 1;
808
809 // check cell type and apply dedicated handling
810 switch (cell_type) {
811
813 "barycentric coordinates "
814 "are only supported for great circle edge cells and lon lat cells");
815
816 // lon/lat cell
817 case (YAC_LON_LAT_CELL) : {
818
819 // compute weights
820 compute_weights_bary_reg(tgt_coords, src_coords, weights);
821
822 // check source mask, partial coverage and scale weights
823 ret_value =
825 weights, 4, src_mask, partial_coverage);
826
827 break;
828 }
829
830 // great circle cell
831 case (YAC_GREAT_CIRCLE_CELL): {
832
833 size_t corner_indices[num_vertices];
834 size_t triangle_indices[num_vertices-2][3];
835
836 for (size_t i = 0; i < num_vertices; ++i) corner_indices[i] = i;
837
838 // triangulate source polygon
839 if (num_vertices > 3) {
841 corner_indices, num_vertices, 0, triangle_indices);
842 } else {
843 for (size_t i = 0; i < 3; ++i) triangle_indices[0][i] = i;
844 }
845
846 // find the best matching triangle
847 double min_barycentric_coord = -DBL_MAX;
848 double barycentric_coords[3];
849 size_t match_index = SIZE_MAX;
850
851 for (size_t i = 0; i < num_vertices - 2; ++i) {
852
853 double temp_barycentric_coords[3];
854 memcpy(temp_barycentric_coords, tgt_coords, 3 * sizeof(double));
855
856 // compute barycentric coordinates for the current triangle
857 // (skip if computation of barycentric coordinates is not possible)
859 temp_barycentric_coords, triangle_indices[i], src_coords,
860 routine)) {
861 continue;
862 }
863
864 double curr_min_barycentric_coord =
865 MIN(temp_barycentric_coords[0],
866 MIN(temp_barycentric_coords[1],
867 temp_barycentric_coords[2]));
868
869 if (curr_min_barycentric_coord > min_barycentric_coord) {
870 min_barycentric_coord = curr_min_barycentric_coord;
871 match_index = i;
872 for (int j = 0; j < 3; ++j)
873 barycentric_coords[j] = MAX(0.0, temp_barycentric_coords[j]);
874 }
875 }
876
878 match_index != SIZE_MAX,
879 "internal error (could not compute barycentric coordinates)\n"
880 "target coordinates: (% .6e;% .6e;% .6e)\n"
881 "first three source cell vertices: (% .6e;% .6e;% .6e),\n"
882 " (% .6e;% .6e;% .6e),\n"
883 " (% .6e;% .6e;% .6e))",
884 tgt_coords[0], tgt_coords[1], tgt_coords[2],
885 src_coords[0][0], src_coords[0][1], src_coords[0][2],
886 src_coords[1][0], src_coords[1][1], src_coords[1][2],
887 src_coords[2][0], src_coords[2][1], src_coords[2][2]
888 );
889
890 // get mask for matching triangle vertices, if source mask is available
891 int triangle_mask[3];
892 if (src_mask) {
893 for (int i = 0; i < 3; ++i) {
894 triangle_mask[i] = src_mask[triangle_indices[match_index][i]];
895 }
896 }
897
898 // check source mask, partial coverage and scale weights
899 ret_value =
901 barycentric_coords, 3, src_mask?triangle_mask:NULL, partial_coverage);
902
903 if (ret_value) {
904 // initialise weights
905 if (num_vertices > 3)
906 for (size_t j = 0; j < num_vertices; ++j) weights[j] = 0.0;
907
908 // set weights
909 for (int j = 0; j < 3; ++j)
910 weights[triangle_indices[match_index][j]] = barycentric_coords[j];
911 }
912
913 break;
914 }
915 } // switch cell type
916
917 return ret_value;
918}
919
940 double tgt_coords[3], size_t num_vertices,
941 yac_const_coordinate_pointer src_coords, int * src_mask, double * weights,
942 enum yac_cell_type cell_type) {
943
944 int const partial_coverage = 1;
945 return
947 tgt_coords, num_vertices, src_coords, src_mask, weights,
948 cell_type, partial_coverage);
949}
950
971 double tgt_coords[3], size_t num_vertices,
972 yac_const_coordinate_pointer src_coords, int * src_mask, double * weights,
973 enum yac_cell_type cell_type) {
974
975 int const partial_coverage = 0;
976 return
978 tgt_coords, num_vertices, src_coords, src_mask, weights,
979 cell_type, partial_coverage);
980}
981
983 enum yac_interp_avg_weight_type weight_type, int partial_coverage) {
984
985 func_compute_weights compute_weights_ptr = NULL;
986
987 switch(weight_type) {
988 YAC_UNREACHABLE_DEFAULT("invalid weight type")
990 compute_weights_ptr =
992 break;
994 compute_weights_ptr =
996 break;
998 compute_weights_ptr =
1000 break;
1001 };
1002 return compute_weights_ptr;
1003}
1004
1007 int partial_coverage) {
1008
1009 struct interp_method_avg * method = xmalloc(1 * sizeof(*method));
1010
1012 method->compute_weights =
1013 select_compute_weight_routine(weight_type, partial_coverage);
1014
1015 return (struct interp_method*)method;
1016}
1017
1018static void delete_avg(struct interp_method * method) {
1019 free(method);
1020}
1021
1022// --- Modular config struct and vtable for AVG method ---
1023
1038
1041 free(config);
1042}
1043
1045 const struct yac_interp_method_config *config) {
1047 struct yac_interp_method_config_avg *copy =
1048 xmalloc(sizeof(*copy));
1049 copy->base.vtable = config_avg->base.vtable;
1050 copy->base.root_param_cache = NULL;
1051 copy->config = config_avg->config;
1052 return (struct yac_interp_method_config *)copy;
1053}
1054
1056 void const *a_, void const *b_) {
1058 // Compare weight_type first
1059 CHECK_ENUM(config.weight_type);
1060 // Then compare partial_coverage flag
1061 CHECK_INT(config.partial_coverage);
1062 return 0;
1063}
1064
1065// Gets interpolation method type
1067 return YAC_AVERAGE;
1068}
1069
1070// Generate interp_method instance from config
1072 struct yac_interp_method_config const *config) {
1075 config_avg->config.weight_type,
1076 config_avg->config.partial_coverage);
1077}
1078
1080 struct yac_interp_method_config const *config) {
1081
1082 struct yac_interp_method_config_avg *config_avg =
1084
1085 enum {
1086 WEIGHTED_HAS_DEFAULT = 1,
1087 WEIGHTED_IS_DEFINED = 1,
1088 PARTIAL_COVERAGE_HAS_DEFAULT = 1,
1089 PARTIAL_COVERAGE_IS_DEFINED = 1,
1090 };
1091
1092 // Enum table for 'weighted' parameter
1094 avg_weighted_enum_table,
1095 DEF_NAME_TYPE_PAIR(distance_weighted, YAC_INTERP_AVG_DIST),
1096 DEF_NAME_TYPE_PAIR(arithmetic_average, YAC_INTERP_AVG_ARITHMETIC),
1097 DEF_NAME_TYPE_PAIR(barycentric_coordinate, YAC_INTERP_AVG_BARY))
1098
1099 struct yac_param * weighted_param =
1101 "weighted",
1102 (int *)&config_avg->config.weight_type,
1104 avg_weighted_enum_table, avg_weighted_enum_table_size,
1105 WEIGHTED_HAS_DEFAULT, WEIGHTED_IS_DEFINED);
1106
1107 struct yac_param * partial_coverage_param =
1109 "partial_coverage",
1110 &config_avg->config.partial_coverage,
1112 PARTIAL_COVERAGE_HAS_DEFAULT,
1113 PARTIAL_COVERAGE_IS_DEFINED);
1114
1115 struct yac_param * root_param_array[] =
1116 {weighted_param, partial_coverage_param};
1117 enum {
1118 ROOT_PARAM_ARRAY_SIZE =
1119 sizeof(root_param_array) / sizeof(root_param_array[0])
1120 };
1121
1122 return yac_param_struct_new(
1123 "average", root_param_array, ROOT_PARAM_ARRAY_SIZE);
1124}
1125
1128 .copy = config_avg_copy,
1129 .compare = config_avg_compare,
1130 .get_type = config_avg_get_type,
1131 .generate = config_avg_generate,
1132 .get_param = config_avg_get_param
1133};
1134
1136 struct yac_interp_method_config_avg *config_avg = xmalloc(sizeof(*config_avg));
1138 config_avg->base.root_param_cache = NULL;
1139 config_avg->config = (struct yac_interp_method_avg_config){
1141 .partial_coverage = YAC_INTERP_AVG_PARTIAL_COVERAGE_DEFAULT,
1142 };
1143 return (struct yac_interp_method_config *)config_avg;
1144}
1145
int yac_point_in_cell(double point_coords[3], struct yac_grid_cell cell)
#define UNUSED(x)
Definition core.h:72
int const * const_int_pointer
size_t const *const const_size_t_pointer
yac_int const * const_yac_int_pointer
void yac_triangulate_cell_indices(size_t const *corner_indices, size_t num_corners, size_t start_corner, size_t(*triangle_indices)[3])
Definition grid_cell.c:144
static int points_are_identically(double const *a, double const *b)
Definition geometry.h:727
#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
void yac_init_grid_cell(struct yac_grid_cell *cell)
Definition grid_cell.c:14
void yac_free_grid_cell(struct yac_grid_cell *cell)
Definition grid_cell.c:44
yac_cell_type
Definition grid_cell.h:26
@ YAC_LAT_CELL
Definition grid_cell.h:28
@ YAC_LON_LAT_CELL
Definition grid_cell.h:27
@ YAC_GREAT_CIRCLE_CELL
Definition grid_cell.h:29
@ YAC_MIXED_CELL
Definition grid_cell.h:30
yac_edge_type
Definition grid_cell.h:12
@ YAC_GREAT_CIRCLE_EDGE
great circle
Definition grid_cell.h:13
@ YAC_LAT_CIRCLE_EDGE
latitude circle
Definition grid_cell.h:14
#define DEF_NAME_TYPE_PAIR(NAME, TYPE)
#define DEF_NAME_TYPE_PAIRS(NAME,...)
#define YAC_INSTRUMENT_START(ID)
Enter an instrumented region.
#define YAC_INSTRUMENT_STOP(ID)
Exit an instrumented region entered with YAC_INSTRUMENT_START(ID).
void yac_interp_grid_do_points_search(struct yac_interp_grid *interp_grid, yac_coordinate_pointer search_coords, size_t count, size_t *src_cells)
const_int_pointer yac_interp_grid_get_src_field_mask(struct yac_interp_grid *interp_grid, size_t src_field_idx)
void yac_interp_grid_get_aux_grid_src(struct yac_interp_grid *interp_grid, size_t *cells, size_t count, size_t **vertex_to_cell, size_t **vertex_to_cell_offsets, int **num_cells_per_vertex)
const_yac_int_pointer yac_interp_grid_get_src_field_global_ids(struct yac_interp_grid *interp_grid, size_t src_field_idx)
struct remote_point * yac_interp_grid_get_tgt_remote_points(struct yac_interp_grid *interp_grid, size_t *tgt_points, size_t count)
enum yac_location yac_interp_grid_get_src_field_location(struct yac_interp_grid *interp_grid, size_t src_field_idx)
struct remote_point * yac_interp_grid_get_src_remote_points(struct yac_interp_grid *interp_grid, size_t src_field_idx, size_t *src_points, size_t count)
yac_const_coordinate_pointer yac_interp_grid_get_src_field_coords(struct yac_interp_grid *interp_grid, size_t src_field_idx)
void yac_interp_grid_get_tgt_coordinates(struct yac_interp_grid *interp_grid, size_t *tgt_points, size_t count, yac_coordinate_pointer tgt_coordinates)
struct yac_const_basic_grid_data * yac_interp_grid_get_basic_grid_data_src(struct yac_interp_grid *interp_grid)
yac_interpolation_list
@ YAC_AVERAGE
Simple averaging (or linear) interpolation.
static int compute_weights_avg_no(double tgt_coords[3], size_t num_vertices, yac_const_coordinate_pointer src_coords, int *src_mask, double *weights, enum yac_cell_type cell_type)
static int compute_weights_avg_yes(double tgt_coords[3], size_t num_vertices, yac_const_coordinate_pointer src_coords, int *src_mask, double *weights, enum yac_cell_type cell_type)
static int check_src_field_cell(double *point, size_t field_cell, size_t cell_size, size_t const *vertex_to_cell, size_t const *vertex_to_cell_offsets, yac_const_coordinate_pointer field_coordinates, struct yac_grid_cell *cell_buffer)
static struct interp_method_vtable interp_method_avg_vtable
static void get_point_lon_lat(double point_coord[3], double cell_lon[2], double cell_lat[2], double *point_lon, double *point_lat)
static int compute_weights_bary(double tgt_coords[3], size_t num_vertices, yac_const_coordinate_pointer src_coords, int *src_mask, double *weights, enum yac_cell_type cell_type, int partial_coverage)
Computes barycentric weights for interpolation.
static int config_avg_compare(void const *a_, void const *b_)
static enum yac_interpolation_list config_avg_get_type()
int(* func_compute_weights)(double[3], size_t, yac_const_coordinate_pointer, int *, double *, enum yac_cell_type cell_type)
static struct yac_param * config_avg_get_param(struct yac_interp_method_config const *config)
struct interp_method * yac_interp_method_avg_new(enum yac_interp_avg_weight_type weight_type, int partial_coverage)
static int compute_barycentric_coords(double *barycentric_coords, size_t triangle_indices[3], yac_const_coordinate_pointer grid_coords, char const *caller)
static func_compute_weights select_compute_weight_routine(enum yac_interp_avg_weight_type weight_type, int partial_coverage)
struct yac_interp_method_config * yac_interp_method_config_default_avg_new(void)
Creates an average interpolation method configuration with default parameters.
static int compute_weights_dist_no(double tgt_coords[3], size_t num_vertices, yac_const_coordinate_pointer src_coords, int *src_mask, double *weights, enum yac_cell_type cell_type)
static int compute_weights_bary_no(double tgt_coords[3], size_t num_vertices, yac_const_coordinate_pointer src_coords, int *src_mask, double *weights, enum yac_cell_type cell_type)
Computes barycentric weights for interpolation not allowing partial coverage.
static int determine_triangle_idx(double point_lon, double point_lat, double cell_lon[2], double cell_lat[2])
static struct yac_interp_method_config_vtable yac_interp_method_config_vtable_avg
static void config_avg_delete(struct yac_interp_method_config *config)
static struct yac_interp_method_config * config_avg_copy(const struct yac_interp_method_config *config)
static int compute_weights_bary_check_weights(double *weights, int num_weights, int *src_mask, int partial_coverage)
#define IS_LON_LAT(x)
static int compute_weights_dist_yes(double tgt_coords[3], size_t num_vertices, yac_const_coordinate_pointer src_coords, int *src_mask, double *weights, enum yac_cell_type cell_type)
static void delete_avg(struct interp_method *method)
static int get_lat_edge_ordering(double const *a, double const *b)
static struct interp_method * config_avg_generate(struct yac_interp_method_config const *config)
static size_t do_search_avg(struct interp_method *method, struct yac_interp_grid *interp_grid, size_t *tgt_points, size_t count, struct yac_interp_weights *weights, int *interpolation_complete)
static void get_cell_lon_lat_bounds(yac_const_coordinate_pointer coords, double lon[2], double lat[2], int reorder[4])
static enum yac_cell_type determine_cell_type(enum yac_edge_type const *edge_types, size_t const *edge_indices, size_t num_edges)
static int compute_weights_bary_yes(double tgt_coords[3], size_t num_vertices, yac_const_coordinate_pointer src_coords, int *src_mask, double *weights, enum yac_cell_type cell_type)
Computes barycentric weights for interpolation allowing partial coverage.
static void compute_weights_bary_reg(double tgt_coords[3], yac_const_coordinate_pointer src_coords, double *weights)
#define YAC_INTERP_AVG_PARTIAL_COVERAGE_DEFAULT
yac_interp_avg_weight_type
@ YAC_INTERP_AVG_DIST
@ YAC_INTERP_AVG_ARITHMETIC
@ YAC_INTERP_AVG_BARY
#define YAC_INTERP_AVG_WEIGHT_TYPE_DEFAULT
#define DEF_INTERP_METHOD_CONFIG_COMPARE_TYPE(TYPE)
#define DEF_INTERP_METHOD_CONFIG_TYPE(TYPE)
static void compute_weights(struct tgt_point_search_data *tgt_point_data, size_t num_tgt_points, struct edge_interp_data *edge_data, size_t num_edges, struct triangle_interp_data *triangle_data, size_t num_triangles, struct weight_vector_data **weights, size_t **num_weights_per_tgt, size_t *total_num_weights)
#define CHECK_SRC_FIELD_COUNT_SINGLE(INTERP_GRID)
#define CHECK_SRC_FIELD_LOCATION_CORNER_OR_CELL(INTERP_GRID)
void yac_interp_weights_add_wsum(struct yac_interp_weights *weights, struct remote_points *tgts, size_t *num_src_per_tgt, struct remote_point *srcs, double *w)
yac_location
Definition location.h:12
@ YAC_LOC_CELL
Definition location.h:14
struct yac_param * yac_param_bool_new(const char *name, int *value_ptr, int default_value, int has_default, int is_defined)
Create a bool parameter (backed by int)
Definition param_bool.c:159
struct yac_param * yac_param_enum_new(const char *name, int *value_ptr, int default_value, struct yac_name_type_pair const *enum_table, size_t enum_table_size, int has_default, int is_defined)
Create a new enum parameter for configuration.
Definition param_enum.c:148
struct yac_param * yac_param_struct_new(const char *name, struct yac_param **subparams, size_t subparam_count)
Create a new struct parameter with a given name and subparameters.
#define xrealloc(ptr, size)
Definition ppm_xfuncs.h:67
#define xmalloc(size)
Definition ppm_xfuncs.h:66
struct interp_method_vtable * vtable
func_compute_weights compute_weights
size_t(* do_search)(struct interp_method *method, struct yac_interp_grid *grid, size_t *tgt_points, size_t count, struct yac_interp_weights *weights, int *interpolation_complete)
information (global id and location) about a point that
structure containing the information (global id and location)
struct remote_point * data
const_size_t_pointer cell_to_vertex_offsets
const_size_t_pointer cell_to_vertex
const const_int_pointer num_vertices_per_cell
size_t num_corners
Definition grid_cell.h:21
enum yac_edge_type * edge_type
Definition grid_cell.h:20
size_t array_size
Definition grid_cell.h:22
double(* coordinates_xyz)[3]
Definition grid_cell.h:19
enum yac_interp_avg_weight_type weight_type
Concrete implementation of yac_interp_method_config for the AVG method.
struct yac_interp_method_config base
‍base config object, must be first entry
struct yac_interp_method_avg_config config
‍method-specific configuration
void(* delete)(struct yac_interp_method_config *config)
struct yac_interp_method_config_vtable const * vtable
double * cell_lat
double * cell_lon
static struct yac_interp_method_config * config
#define MIN(a, b)
Definition toy_common.h:29
static void XYZtoLL(double const p_in[], double *lon, double *lat)
Definition toy_common.h:23
#define MAX(a, b)
void yac_quicksort_index_size_t_size_t(size_t *a, size_t n, size_t *idx)
#define YAC_ASSERT_F(exp, format,...)
Definition yac_assert.h:39
#define YAC_UNREACHABLE_DEFAULT(msg)
Definition yac_assert.h:56
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