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 <atomic>
9 
10 #include "process_int.h"
11 #include "cdo_wtime.h"
12 #include "remap.h"
13 #include "remap_store_link.h"
14 #include "cdo_options.h"
15 #include "progress.h"
16 #include "cimdOmp.h"
17 
18 // Interpolation using a distance-weighted average
19 
20 // This routine computes the inverse-distance weights for a nearest-neighbor interpolation
21 void
remap_distwgt_weights(size_t numNeighbors,RemapSearch & rsearch,RemapVars & rv)22 remap_distwgt_weights(size_t numNeighbors, RemapSearch &rsearch, RemapVars &rv)
23 {
24   auto src_grid = rsearch.srcGrid;
25   auto tgt_grid = rsearch.tgtGrid;
26 
27   if (Options::cdoVerbose) cdo_print("Called %s()", __func__);
28 
29   progress::init();
30 
31   // Compute mappings from source to target grid
32 
33   auto tgt_grid_size = tgt_grid->size;
34 
35   std::vector<WeightLinks> weightLinks(tgt_grid_size);
36   weight_links_alloc(numNeighbors, tgt_grid_size, weightLinks);
37 
38   std::vector<knnWeightsType> knnWeights;
39   for (int i = 0; i < Threading::ompNumThreads; ++i) knnWeights.push_back(knnWeightsType(numNeighbors));
40 
41   const auto start = Options::cdoVerbose ? cdo_get_wtime() : 0.0;
42 
43   // Loop over destination grid
44 
45   std::atomic<size_t> atomicCount{0};
46 
47 #ifdef _OPENMP
48 #pragma omp parallel for default(none) \
49     shared(atomicCount, rsearch, weightLinks, numNeighbors, src_grid, tgt_grid, tgt_grid_size, knnWeights)
50 #endif
51   for (size_t tgt_cell_add = 0; tgt_cell_add < tgt_grid_size; ++tgt_cell_add)
52     {
53       const auto ompthID = cdo_omp_get_thread_num();
54 
55       atomicCount++;
56       if (ompthID == 0) progress::update(0, 1, (double)atomicCount / tgt_grid_size);
57 
58       weightLinks[tgt_cell_add].nlinks = 0;
59 
60       if (!tgt_grid->mask[tgt_cell_add]) continue;
61 
62       const auto llpoint = remapgrid_get_lonlat(tgt_grid, tgt_cell_add);
63 
64       // Find nearest grid points on source grid and distances to each point
65       remap_search_points(rsearch, llpoint, knnWeights[ompthID]);
66 
67       // Compute weights based on inverse distance if mask is false, eliminate those points
68       const auto nadds = knnWeights[ompthID].computeWeights(src_grid->mask);
69 
70       for (size_t n = 0; n < nadds; ++n)
71         if (knnWeights[ompthID].m_mask[n]) tgt_grid->cell_frac[tgt_cell_add] = 1.0;
72 
73       // Store the link
74       store_weightlinks(0, nadds, knnWeights[ompthID].m_addr.data(), knnWeights[ompthID].m_dist.data(), tgt_cell_add, weightLinks);
75     }
76 
77   progress::update(0, 1, 1);
78 
79   grid_point_search_delete(rsearch.gps);
80 
81   weight_links_to_remap_links(0, tgt_grid_size, weightLinks, rv);
82 
83   if (numNeighbors == 1) rv.links_per_value = numNeighbors;
84 
85   if (Options::cdoVerbose) cdo_print("Point search nearest: %.2f seconds", cdo_get_wtime() - start);
86 }  // remap_distwgt_weights
87 
88 template <typename T>
89 static void
remap_dist_wgt(size_t numNeighbors,RemapSearch & rsearch,const Varray<T> & src_array,Varray<T> & tgt_array,T missval)90 remap_dist_wgt(size_t numNeighbors, RemapSearch &rsearch, const Varray<T> &src_array, Varray<T> &tgt_array, T missval)
91 {
92   auto src_grid = rsearch.srcGrid;
93   auto tgt_grid = rsearch.tgtGrid;
94 
95   if (Options::cdoVerbose) cdo_print("Called %s()", __func__);
96 
97   progress::init();
98 
99   // Compute mappings from source to target grid
100 
101   auto tgt_grid_size = tgt_grid->size;
102   auto src_grid_size = src_grid->size;
103 
104   Varray<short> src_grid_mask(src_grid_size);
105 #ifdef _OPENMP
106 #pragma omp parallel for default(none) schedule(static) shared(src_grid_size, src_array, src_grid_mask, missval)
107 #endif
108   for (size_t i = 0; i < src_grid_size; ++i) src_grid_mask[i] = !DBL_IS_EQUAL(src_array[i], missval);
109 
110   std::vector<knnWeightsType> knnWeights;
111   for (int i = 0; i < Threading::ompNumThreads; ++i) knnWeights.push_back(knnWeightsType(numNeighbors));
112 
113   const auto start = Options::cdoVerbose ? cdo_get_wtime() : 0.0;
114 
115   // Loop over destination grid
116 
117   std::atomic<size_t> atomicCount{0};
118 
119 #ifdef _OPENMP
120 #pragma omp parallel for default(none) shared(atomicCount, rsearch, numNeighbors, src_grid, tgt_grid, tgt_grid_size, src_array, \
121                                               tgt_array, missval, src_grid_mask, knnWeights)
122 #endif
123   for (size_t tgt_cell_add = 0; tgt_cell_add < tgt_grid_size; ++tgt_cell_add)
124     {
125       const auto ompthID = cdo_omp_get_thread_num();
126 
127       atomicCount++;
128       if (ompthID == 0) progress::update(0, 1, (double)atomicCount / tgt_grid_size);
129 
130       tgt_array[tgt_cell_add] = missval;
131 
132       if (!tgt_grid->mask[tgt_cell_add]) continue;
133 
134       const auto llpoint = remapgrid_get_lonlat(tgt_grid, tgt_cell_add);
135 
136       // Find nearest grid points on source grid and distances to each point
137       remap_search_points(rsearch, llpoint, knnWeights[ompthID]);
138 
139       // Compute weights based on inverse distance if mask is false, eliminate those points
140       const auto nadds = knnWeights[ompthID].computeWeights(src_grid_mask);
141       if (nadds) tgt_array[tgt_cell_add] = knnWeights[ompthID].arrayWeightsSum(src_array);
142     }
143 
144   progress::update(0, 1, 1);
145 
146   if (Options::cdoVerbose) cdo_print("Point search nearest: %.2f seconds", cdo_get_wtime() - start);
147 }  // remap_dist_wgt
148 
149 void
remap_dist_wgt(size_t numNeighbors,RemapSearch & rsearch,const Field & field1,Field & field2)150 remap_dist_wgt(size_t numNeighbors, RemapSearch &rsearch, const Field &field1, Field &field2)
151 {
152   if (field1.memType == MemType::Float)
153     remap_dist_wgt(numNeighbors, rsearch, field1.vec_f, field2.vec_f, (float) field1.missval);
154   else
155     remap_dist_wgt(numNeighbors, rsearch, field1.vec_d, field2.vec_d, field1.missval);
156 }
157 
158 void remapInit(RemapType &remap);
159 
160 void
intgriddis(Field & field1,Field & field2,size_t numNeighbors)161 intgriddis(Field &field1, Field &field2, size_t numNeighbors)
162 {
163   auto mapType = RemapMethod::DISTWGT;
164   auto gridID1 = field1.grid;
165   auto gridID2 = field2.grid;
166   auto src_missval = field1.missval;
167   auto tgt_missval = field2.missval;
168   auto src_array = field1.vec_d.data();
169   auto tgt_array = field2.vec_d.data();
170 
171   if (Options::cdoVerbose) cdo_print("Called %s()", __func__);
172 
173   progress::init();
174 
175   // Interpolate from source to target grid
176 
177   RemapType remap;
178   remapInit(remap);
179 
180   bool remap_extrapolate = false;
181   remap_init_grids(mapType, remap_extrapolate, gridID1, remap.src_grid, gridID2, remap.tgt_grid);
182 
183   auto src_grid_size = remap.src_grid.size;
184   auto tgt_grid_size = remap.tgt_grid.size;
185 
186   Varray<short> src_mask(src_grid_size);
187   for (size_t i = 0; i < src_grid_size; ++i) src_mask[i] = !DBL_IS_EQUAL(src_array[i], src_missval);
188   // Varray<short> tgt_mask(tgt_grid_size, 1);
189 
190   std::vector<knnWeightsType> knnWeights;
191   for (int i = 0; i < Threading::ompNumThreads; ++i) knnWeights.push_back(knnWeightsType(numNeighbors));
192 
193   remap_search_init(mapType, remap.search, remap.src_grid, remap.tgt_grid);
194 
195   const auto start = Options::cdoVerbose ? cdo_get_wtime() : 0.0;
196 
197   // Loop over destination grid
198 
199   size_t nmiss = 0;
200   double findex = 0.0;
201 
202   for (size_t tgt_cell_add = 0; tgt_cell_add < tgt_grid_size; ++tgt_cell_add)
203     {
204       const auto ompthID = cdo_omp_get_thread_num();
205 
206       findex++;
207       if (ompthID == 0) progress::update(0, 1, findex / tgt_grid_size);
208 
209       tgt_array[tgt_cell_add] = tgt_missval;
210 
211       // if (!tgt_mask[tgt_cell_add]) continue;
212 
213       const auto llpoint = remapgrid_get_lonlat(&remap.tgt_grid, tgt_cell_add);
214 
215       // Find nearest grid points on source grid and distances to each point
216       remap_search_points(remap.search, llpoint, knnWeights[ompthID]);
217 
218       // Compute weights based on inverse distance if mask is false, eliminate those points
219       const auto nadds = knnWeights[ompthID].computeWeights(src_mask);
220       if (nadds)
221         tgt_array[tgt_cell_add] = knnWeights[ompthID].arrayWeightsSum(src_array);
222       else
223         nmiss++;
224     }
225 
226   progress::update(0, 1, 1);
227 
228   field2.nmiss = nmiss;
229 
230   remap_grid_free(remap.src_grid);
231   remap_grid_free(remap.tgt_grid);
232   remap_search_free(remap.search);
233 
234   if (Options::cdoVerbose) cdo_print("Point search nearest: %.2f seconds", cdo_get_wtime() - start);
235 }  // intgriddis
236