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 <cdi.h>
9
10 #include <vector>
11 #include <algorithm>
12
13 #include "process_int.h"
14 #include "percentiles.h"
15
16 template <typename T>
17 static void
varray_copy_zonal(size_t offset,size_t nx,const Varray<T> & v1,Varray<double> & v2)18 varray_copy_zonal(size_t offset, size_t nx, const Varray<T> &v1, Varray<double> &v2)
19 {
20 std::copy(v1.begin() + offset, v1.begin() + offset + nx, v2.begin());
21 }
22
23 static size_t
fill_reduced_points(const int gridID,const size_t ny,std::vector<int> & reducedPoints,std::vector<int> & cumreducedPoints)24 fill_reduced_points(const int gridID, const size_t ny, std::vector<int> &reducedPoints, std::vector<int> &cumreducedPoints)
25 {
26 reducedPoints.resize(ny);
27 cumreducedPoints.resize(ny);
28 gridInqReducedPoints(gridID, reducedPoints.data());
29 cumreducedPoints[0] = 0;
30 for (size_t j = 1; j < ny; j++) cumreducedPoints[j] = cumreducedPoints[j - 1] + reducedPoints[j - 1];
31 size_t nx = reducedPoints[0];
32 for (size_t j = 1; j < ny; j++)
33 if (reducedPoints[j] > (int) nx) nx = reducedPoints[j];
34 return nx;
35 }
36
37 using funcType = double(size_t, const Varray<double> &);
38 using funcTypeMV = double(size_t, const Varray<double> &, double);
39 using funcTypeNmissMV = double(size_t, const Varray<double> &, size_t, double);
40
41 static void
zonal_kernel_1(const Field & field1,Field & field2,funcTypeNmissMV funcNmissMV)42 zonal_kernel_1(const Field &field1, Field &field2, funcTypeNmissMV funcNmissMV)
43 {
44 size_t rnmiss = 0;
45 const auto nmiss = field1.nmiss;
46 const auto missval = field1.missval;
47
48 const auto ny = gridInqYsize(field1.grid);
49 const auto lreduced = (gridInqType(field1.grid) == GRID_GAUSSIAN_REDUCED);
50 std::vector<int> reducedPoints, cumreducedPoints;
51 auto nx = lreduced ? fill_reduced_points(field1.grid, ny, reducedPoints, cumreducedPoints) : gridInqXsize(field1.grid);
52
53 Varray<double> v(nx);
54
55 for (size_t j = 0; j < ny; ++j)
56 {
57 if (lreduced) nx = reducedPoints[j];
58 const size_t offset = lreduced ? cumreducedPoints[j] : j * nx;
59 if (field1.memType == MemType::Float)
60 varray_copy_zonal(offset, nx, field1.vec_f, v);
61 else
62 varray_copy_zonal(offset, nx, field1.vec_d, v);
63
64 const auto result = funcNmissMV(nx, v, nmiss, missval);
65 if (DBL_IS_EQUAL(result, missval)) rnmiss++;
66 field2.vec_d[j] = result;
67 }
68
69 field2.nmiss = rnmiss;
70 }
71
72 static void
zonal_kernel_2(const Field & field1,Field & field2,funcType func,funcTypeMV funcMV)73 zonal_kernel_2(const Field &field1, Field &field2, funcType func, funcTypeMV funcMV)
74 {
75 size_t rnmiss = 0;
76 const auto nmiss = field1.nmiss;
77 const auto missval = field1.missval;
78
79 const auto ny = gridInqYsize(field1.grid);
80 const auto lreduced = (gridInqType(field1.grid) == GRID_GAUSSIAN_REDUCED);
81 std::vector<int> reducedPoints, cumreducedPoints;
82 auto nx = lreduced ? fill_reduced_points(field1.grid, ny, reducedPoints, cumreducedPoints) : gridInqXsize(field1.grid);
83
84 Varray<double> v(nx);
85
86 for (size_t j = 0; j < ny; ++j)
87 {
88 if (lreduced) nx = reducedPoints[j];
89 const size_t offset = lreduced ? cumreducedPoints[j] : j * nx;
90 if (field1.memType == MemType::Float)
91 varray_copy_zonal(offset, nx, field1.vec_f, v);
92 else
93 varray_copy_zonal(offset, nx, field1.vec_d, v);
94
95 const auto result = nmiss ? funcMV(nx, v, missval) : func(nx, v);
96 if (DBL_IS_EQUAL(result, missval)) rnmiss++;
97 field2.vec_d[j] = result;
98 }
99
100 field2.nmiss = rnmiss;
101 }
102
103 void
zonal_min(const Field & field1,Field & field2)104 zonal_min(const Field &field1, Field &field2)
105 {
106 zonal_kernel_2(field1, field2, varray_min, varray_min_mv);
107 }
108
109 void
zonal_max(const Field & field1,Field & field2)110 zonal_max(const Field &field1, Field &field2)
111 {
112 zonal_kernel_2(field1, field2, varray_max, varray_max_mv);
113 }
114
115 void
zonal_range(const Field & field1,Field & field2)116 zonal_range(const Field &field1, Field &field2)
117 {
118 zonal_kernel_2(field1, field2, varray_range, varray_range_mv);
119 }
120
121 void
zonal_sum(const Field & field1,Field & field2)122 zonal_sum(const Field &field1, Field &field2)
123 {
124 zonal_kernel_2(field1, field2, varray_sum, varray_sum_mv);
125 }
126
127 void
zonal_mean(const Field & field1,Field & field2)128 zonal_mean(const Field &field1, Field &field2)
129 {
130 zonal_kernel_2(field1, field2, varray_mean, varray_mean_mv);
131 }
132
133 void
zonal_avg(const Field & field1,Field & field2)134 zonal_avg(const Field &field1, Field &field2)
135 {
136 zonal_kernel_2(field1, field2, varray_mean, varray_avg_mv);
137 }
138
139 void
zonal_var(const Field & field1,Field & field2)140 zonal_var(const Field &field1, Field &field2)
141 {
142 zonal_kernel_1(field1, field2, varray_var);
143 }
144
145 void
zonal_var1(const Field & field1,Field & field2)146 zonal_var1(const Field &field1, Field &field2)
147 {
148 zonal_kernel_1(field1, field2, varray_var_1);
149 }
150
151 void
zonal_std(const Field & field1,Field & field2)152 zonal_std(const Field &field1, Field &field2)
153 {
154 size_t rnmiss = 0;
155 const auto missval = field1.missval;
156 const auto ny = gridInqYsize(field1.grid);
157
158 zonal_var(field1, field2);
159
160 for (size_t j = 0; j < ny; j++)
161 {
162 const auto rstd = var_to_std(field2.vec_d[j], missval);
163 if (DBL_IS_EQUAL(rstd, missval)) rnmiss++;
164 field2.vec_d[j] = rstd;
165 }
166
167 field2.nmiss = rnmiss;
168 }
169
170 void
zonal_std1(const Field & field1,Field & field2)171 zonal_std1(const Field &field1, Field &field2)
172 {
173 size_t rnmiss = 0;
174 const auto missval = field1.missval;
175 const auto ny = gridInqYsize(field1.grid);
176
177 zonal_var1(field1, field2);
178
179 for (size_t j = 0; j < ny; j++)
180 {
181 const auto rstd = var_to_std(field2.vec_d[j], missval);
182 if (DBL_IS_EQUAL(rstd, missval)) rnmiss++;
183 field2.vec_d[j] = rstd;
184 }
185
186 field2.nmiss = rnmiss;
187 }
188
189 void
zonal_skew(const Field & field1,Field & field2)190 zonal_skew(const Field &field1, Field &field2)
191 {
192 zonal_kernel_1(field1, field2, varray_skew);
193 }
194
195 void
zonal_kurt(const Field & field1,Field & field2)196 zonal_kurt(const Field &field1, Field &field2)
197 {
198 zonal_kernel_1(field1, field2, varray_kurt);
199 }
200
201 void
zonal_median(const Field & field1,Field & field2)202 zonal_median(const Field &field1, Field &field2)
203 {
204 zonal_kernel_1(field1, field2, varray_median);
205 }
206
207 void
zonal_pctl(const Field & field1,Field & field2,const double pn)208 zonal_pctl(const Field &field1, Field &field2, const double pn)
209 {
210 size_t rnmiss = 0;
211 const auto missval = field1.missval;
212
213 const auto ny = gridInqYsize(field1.grid);
214 const auto lreduced = (gridInqType(field1.grid) == GRID_GAUSSIAN_REDUCED);
215 std::vector<int> reducedPoints, cumreducedPoints;
216 auto nx = lreduced ? fill_reduced_points(field1.grid, ny, reducedPoints, cumreducedPoints) : gridInqXsize(field1.grid);
217
218 Varray<double> v(nx);
219
220 if (field1.nmiss)
221 {
222 for (size_t j = 0; j < ny; j++)
223 {
224 if (lreduced) nx = reducedPoints[j];
225 const size_t offset = lreduced ? cumreducedPoints[j] : j * nx;
226 if (field1.memType == MemType::Float)
227 varray_copy_zonal(offset, nx, field1.vec_f, v);
228 else
229 varray_copy_zonal(offset, nx, field1.vec_d, v);
230
231 size_t k = 0;
232 for (size_t i = 0; i < nx; i++)
233 if (!DBL_IS_EQUAL(v[i], missval)) v[k++] = v[i];
234
235 if (k > 0)
236 {
237 field2.vec_d[j] = percentile(v.data(), k, pn);
238 }
239 else
240 {
241 field2.vec_d[j] = missval;
242 rnmiss++;
243 }
244 }
245 }
246 else
247 {
248 for (size_t j = 0; j < ny; j++)
249 {
250 if (lreduced) nx = reducedPoints[j];
251 const size_t offset = lreduced ? cumreducedPoints[j] : j * nx;
252 if (field1.memType == MemType::Float)
253 varray_copy_zonal(offset, nx, field1.vec_f, v);
254 else
255 varray_copy_zonal(offset, nx, field1.vec_d, v);
256
257 if (nx > 0)
258 {
259 field2.vec_d[j] = percentile(v.data(), nx, pn);
260 }
261 else
262 {
263 field2.vec_d[j] = missval;
264 rnmiss++;
265 }
266 }
267 }
268
269 field2.nmiss = rnmiss;
270 }
271
272 void
zonal_function(const Field & field1,Field & field2,int function)273 zonal_function(const Field &field1, Field &field2, int function)
274 {
275 // clang-format off
276 switch (function)
277 {
278 case FieldFunc_Min: return zonal_min(field1, field2);
279 case FieldFunc_Max: return zonal_max(field1, field2);
280 case FieldFunc_Range: return zonal_range(field1, field2);
281 case FieldFunc_Sum: return zonal_sum(field1, field2);
282 case FieldFunc_Mean: return zonal_mean(field1, field2);
283 case FieldFunc_Avg: return zonal_avg(field1, field2);
284 case FieldFunc_Std: return zonal_std(field1, field2);
285 case FieldFunc_Std1: return zonal_std1(field1, field2);
286 case FieldFunc_Var: return zonal_var(field1, field2);
287 case FieldFunc_Var1: return zonal_var1(field1, field2);
288 case FieldFunc_Skew: return zonal_skew(field1, field2);
289 case FieldFunc_Kurt: return zonal_kurt(field1, field2);
290 case FieldFunc_Median: return zonal_median(field1, field2);
291 default: cdo_abort("%s: function %d not implemented!", __func__, function);
292 }
293 // clang-format on
294 }
295