1 /*
2   This file is part of CDO. CDO is a collection of Operators to manipulate and analyse Climate model Data.
3 
4   Author: Uwe Schulzweida
5 
6 */
7 
8 #include "remap_grid_cell_search.h"
9 #include "cdo_output.h"
10 #include "compare.h"
11 #include "varray.h"
12 #include "cdo_options.h"
13 #include "cimdOmp.h"
14 #include <mpim_grid.h>
15 
16 extern "C"
17 {
18 #include "lib/yac/sphere_part.h"
19 }
20 
21 CellSearchMethod cellSearchMethod(CellSearchMethod::spherepart);
22 
23 void
set_cell_search_method(const char * methodstr)24 set_cell_search_method(const char *methodstr)
25 {
26   // clang-format off
27   if      (cdo_cmpstr(methodstr, "spherepart")) cellSearchMethod = CellSearchMethod::spherepart;
28   else if (cdo_cmpstr(methodstr, "latbins"))    cellSearchMethod = CellSearchMethod::latbins;
29   else cdo_abort("Grid cell search method %s not available!", methodstr);
30   // clang-format on
31 }
32 
33 static void
gridBoundboxReg2d(size_t nx,size_t ny,const Varray<double> & reg2d_corner_lon,const Varray<double> & reg2d_corner_lat,double * grid_bound_box)34 gridBoundboxReg2d(size_t nx, size_t ny, const Varray<double> &reg2d_corner_lon, const Varray<double> &reg2d_corner_lat,
35                   double *grid_bound_box)
36 {
37   grid_bound_box[0] = reg2d_corner_lat[0];
38   grid_bound_box[1] = reg2d_corner_lat[ny];
39   if (grid_bound_box[0] > grid_bound_box[1])
40     {
41       grid_bound_box[0] = reg2d_corner_lat[ny];
42       grid_bound_box[1] = reg2d_corner_lat[0];
43     }
44   grid_bound_box[2] = reg2d_corner_lon[0];
45   grid_bound_box[3] = reg2d_corner_lon[nx];
46 }
47 
48 void
grid_cell_search_create_reg_2d(GridCellSearch & gcs,size_t dims[2],const Varray<double> & reg2d_corner_lon,const Varray<double> & reg2d_corner_lat)49 grid_cell_search_create_reg_2d(GridCellSearch &gcs, size_t dims[2], const Varray<double> &reg2d_corner_lon,
50                                const Varray<double> &reg2d_corner_lat)
51 {
52   gcs.is_reg2d = true;
53   gcs.dims[0] = dims[0];
54   gcs.dims[1] = dims[1];
55   const auto nx = dims[0];
56   const auto ny = dims[1];
57   const auto nxp1 = nx + 1;
58   const auto nyp1 = ny + 1;
59 
60   gcs.reg2d_corner_lon.resize(nxp1);
61   gcs.reg2d_corner_lat.resize(nyp1);
62 
63   varray_copy(nxp1, reg2d_corner_lon, gcs.reg2d_corner_lon);
64   varray_copy(nyp1, reg2d_corner_lat, gcs.reg2d_corner_lat);
65 
66   gridBoundboxReg2d(nx, ny, gcs.reg2d_corner_lon, gcs.reg2d_corner_lat, gcs.gridBoundboxReg2d);
67 
68   gcs.in_use = true;
69 }
70 
71 void
grid_cell_search_create(GridCellSearch & gcs,size_t numCells,size_t numCellCorners,Varray<double> & cellCornerLon,Varray<double> & cellCornerLat)72 grid_cell_search_create(GridCellSearch &gcs, size_t numCells, size_t numCellCorners, Varray<double> &cellCornerLon,
73                         Varray<double> &cellCornerLat)
74 {
75   gcs.method = cellSearchMethod;
76 
77   std::vector<struct grid_cell> cells(Threading::ompNumThreads);
78   for (int i = 0; i < Threading::ompNumThreads; ++i)
79     {
80       cells[i].coordinates_xyz = (double(*)[3]) malloc(numCellCorners * sizeof(*(cells[i].coordinates_xyz)));
81       cells[i].edge_type = (enum yac_edge_type *) malloc(numCellCorners * sizeof(*(cells[i].edge_type)));
82       cells[i].num_corners = numCellCorners;
83       cells[i].array_size = numCellCorners;
84       for (size_t k = 0; k < numCellCorners; ++k) cells[i].edge_type[k] = GREAT_CIRCLE;
85     }
86 
87   struct bounding_circle *bnd_circles = (struct bounding_circle *) malloc(numCells * sizeof(struct bounding_circle));
88 
89 #ifdef _OPENMP
90 #pragma omp parallel for default(shared)
91 #endif
92   for (size_t i = 0; i < numCells; ++i)
93     {
94       const auto ompthID = cdo_omp_get_thread_num();
95       auto &cell = cells[ompthID];
96       auto xyz = cell.coordinates_xyz;
97 
98       for (size_t k = 0; k < numCellCorners; ++k)
99         gcLLtoXYZ(cellCornerLon[i * numCellCorners + k], cellCornerLat[i * numCellCorners + k], xyz[k]);
100 
101       if (numCellCorners == 3)
102         yac_get_cell_bounding_circle_unstruct_triangle(xyz[0], xyz[1], xyz[2], &bnd_circles[i]);
103       else
104         yac_get_cell_bounding_circle(cell, &bnd_circles[i]);
105     }
106 
107   gcs.yacSearch = yac_bnd_sphere_part_search_new(bnd_circles, numCells);
108   gcs.yacBndCircles = bnd_circles;
109 
110   for (int i = 0; i < Threading::ompNumThreads; ++i)
111     {
112       free(cells[i].edge_type);
113       free(cells[i].coordinates_xyz);
114     }
115 
116   gcs.in_use = true;
117 }
118 
119 void
grid_cell_search_delete(GridCellSearch & gcs)120 grid_cell_search_delete(GridCellSearch &gcs)
121 {
122   if (gcs.in_use)
123     {
124       varray_free(gcs.reg2d_corner_lon);
125       varray_free(gcs.reg2d_corner_lat);
126 
127       if (gcs.yacSearch) yac_bnd_sphere_part_search_delete((bnd_sphere_part_search *) gcs.yacSearch);
128       if (gcs.yacBndCircles) free(gcs.yacBndCircles);
129 
130       gcs.in_use = false;
131     }
132 }
133 
134 static size_t
doGridCellSearchYac(bnd_sphere_part_search * yacSearch,bounding_circle * yacBndCircles,bool isReg2dCell,const grid_cell & gridCell,Varray<size_t> & srchAddr)135 doGridCellSearchYac(bnd_sphere_part_search *yacSearch, bounding_circle *yacBndCircles, bool isReg2dCell, const grid_cell &gridCell,
136                     Varray<size_t> &srchAddr)
137 {
138   size_t numCellCorners = gridCell.num_corners;
139   bounding_circle bndCircle;
140   double(*xyz)[3] = gridCell.coordinates_xyz;
141 
142   if (numCellCorners == 4 && isReg2dCell)
143     yac_get_cell_bounding_circle_reg_quad(xyz[0], xyz[1], xyz[2], &bndCircle);
144   else if (numCellCorners == 3)
145     yac_get_cell_bounding_circle_unstruct_triangle(xyz[0], xyz[1], xyz[2], &bndCircle);
146   else
147     yac_get_cell_bounding_circle(gridCell, &bndCircle);
148 
149   size_t numSrchCells;
150   size_t *currNeighs;
151   yac_bnd_sphere_part_search_do_bnd_circle_search(yacSearch, &bndCircle, 1, &currNeighs, &numSrchCells);
152 
153   if (srchAddr.size() < numSrchCells) srchAddr.resize(numSrchCells);
154 
155   size_t k = 0;
156   // for (size_t i = 0; i < numSrchCells; ++i) srchAddr[i] = currNeighs[i];
157   for (size_t i = 0; i < numSrchCells; ++i)
158     {
159       if (yac_extents_overlap(&bndCircle, &yacBndCircles[currNeighs[i]])) srchAddr[k++] = currNeighs[i];
160     }
161   numSrchCells = k;
162   free(currNeighs);
163 
164   return numSrchCells;
165 }
166 
167 size_t
do_grid_cell_search(GridCellSearch & gcs,bool isReg2dCell,grid_cell & gridCell,Varray<size_t> & srchAddr)168 do_grid_cell_search(GridCellSearch &gcs, bool isReg2dCell, grid_cell &gridCell, Varray<size_t> &srchAddr)
169 {
170   if (gcs.in_use)
171     {
172       // clang-format off
173       if (gcs.method == CellSearchMethod::spherepart)
174         return doGridCellSearchYac((bnd_sphere_part_search*)gcs.yacSearch, (bounding_circle *)gcs.yacBndCircles,
175                                    isReg2dCell, gridCell, srchAddr);
176       else
177         cdo_abort("%s::method undefined!", __func__);
178       // clang-format on
179     }
180 
181   return 0;
182 }
183