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 "process_int.h"
11 #include "percentiles.h"
12
13 using funcType1 = double(size_t, const Varray<double> &);
14 using funcTypeMV1 = double(size_t, const Varray<double> &, double);
15 using funcType2 = double(size_t, const Varray<double> &, const Varray<double> &, double);
16 using funcTypeMV2 = double(size_t, const Varray<double> &, const Varray<double> &, double);
17 using funcType3 = double(size_t, const Varray<double> &, const Varray<double> &, size_t, double);
18 using funcType4 = double(size_t, const Varray<double> &, size_t, double);
19
20 template <typename T>
21 static void
varray_copy_meridional(size_t i,size_t nx,size_t ny,const Varray<T> & v1,Varray<double> & v2)22 varray_copy_meridional(size_t i, size_t nx, size_t ny, const Varray<T> &v1, Varray<double> &v2)
23 {
24 for (size_t j = 0; j < ny; j++) v2[j] = v1[j * nx + i];
25 }
26
27 static void
meridional_kernel_1(const Field & field1,Field & field2,funcType1 func,funcTypeMV1 funcMV)28 meridional_kernel_1(const Field &field1, Field &field2, funcType1 func, funcTypeMV1 funcMV)
29 {
30 size_t rnmiss = 0;
31 const auto nmiss = field1.nmiss;
32 const auto missval = field1.missval;
33 const auto nx = gridInqXsize(field1.grid);
34 const auto ny = gridInqYsize(field1.grid);
35
36 Varray<double> v(ny);
37
38 for (size_t i = 0; i < nx; ++i)
39 {
40 if (field1.memType == MemType::Float)
41 varray_copy_meridional(i, nx, ny, field1.vec_f, v);
42 else
43 varray_copy_meridional(i, nx, ny, field1.vec_d, v);
44
45 const auto result = nmiss ? funcMV(ny, v, missval) : func(ny, v);
46 if (DBL_IS_EQUAL(result, missval)) rnmiss++;
47 field2.vec_d[i] = result;
48 }
49
50 field2.nmiss = rnmiss;
51 }
52
53 static void
meridional_kernel_2(const Field & field1,Field & field2,funcType2 func,funcTypeMV2 funcMV)54 meridional_kernel_2(const Field &field1, Field &field2, funcType2 func, funcTypeMV2 funcMV)
55 {
56 size_t rnmiss = 0;
57 const auto nmiss = field1.nmiss;
58 const auto missval = field1.missval;
59 const auto nx = gridInqXsize(field1.grid);
60 const auto ny = gridInqYsize(field1.grid);
61
62 Varray<double> v(ny), w(ny);
63
64 for (size_t i = 0; i < nx; ++i)
65 {
66 varray_copy_meridional(i, nx, ny, field1.weightv, w);
67 if (field1.memType == MemType::Float)
68 varray_copy_meridional(i, nx, ny, field1.vec_f, v);
69 else
70 varray_copy_meridional(i, nx, ny, field1.vec_d, v);
71
72 const auto result = nmiss ? funcMV(ny, v, w, missval) : func(ny, v, w, missval);
73 if (DBL_IS_EQUAL(result, missval)) rnmiss++;
74 field2.vec_d[i] = result;
75 }
76
77 field2.nmiss = rnmiss;
78 }
79
80 static void
meridional_kernel_3(const Field & field1,Field & field2,funcType3 func)81 meridional_kernel_3(const Field &field1, Field &field2, funcType3 func)
82 {
83 size_t rnmiss = 0;
84 const auto nmiss = field1.nmiss;
85 const auto missval = field1.missval;
86 const auto nx = gridInqXsize(field1.grid);
87 const auto ny = gridInqYsize(field1.grid);
88
89 Varray<double> v(ny), w(ny);
90
91 for (size_t i = 0; i < nx; ++i)
92 {
93 varray_copy_meridional(i, nx, ny, field1.weightv, w);
94 if (field1.memType == MemType::Float)
95 varray_copy_meridional(i, nx, ny, field1.vec_f, v);
96 else
97 varray_copy_meridional(i, nx, ny, field1.vec_d, v);
98
99 const auto result = func(ny, v, w, nmiss, missval);
100 if (DBL_IS_EQUAL(result, missval)) rnmiss++;
101 field2.vec_d[i] = result;
102 }
103
104 field2.nmiss = rnmiss;
105 }
106
107 static void
meridional_kernel_4(const Field & field1,Field & field2,funcType4 func)108 meridional_kernel_4(const Field &field1, Field &field2, funcType4 func)
109 {
110 size_t rnmiss = 0;
111 const auto nmiss = field1.nmiss;
112 const auto missval = field1.missval;
113 const auto nx = gridInqXsize(field1.grid);
114 const auto ny = gridInqYsize(field1.grid);
115
116 Varray<double> v(ny);
117
118 for (size_t i = 0; i < nx; ++i)
119 {
120 if (field1.memType == MemType::Float)
121 varray_copy_meridional(i, nx, ny, field1.vec_f, v);
122 else
123 varray_copy_meridional(i, nx, ny, field1.vec_d, v);
124
125 const auto result = func(ny, v, nmiss, missval);
126 if (DBL_IS_EQUAL(result, missval)) rnmiss++;
127 field2.vec_d[i] = result;
128 }
129
130 field2.nmiss = rnmiss;
131 }
132
133 static void
meridional_min(const Field & field1,Field & field2)134 meridional_min(const Field &field1, Field &field2)
135 {
136 meridional_kernel_1(field1, field2, varray_min, varray_min_mv);
137 }
138
139 static void
meridional_max(const Field & field1,Field & field2)140 meridional_max(const Field &field1, Field &field2)
141 {
142 meridional_kernel_1(field1, field2, varray_max, varray_max_mv);
143 }
144
145 static void
meridional_range(const Field & field1,Field & field2)146 meridional_range(const Field &field1, Field &field2)
147 {
148 meridional_kernel_1(field1, field2, varray_range, varray_range_mv);
149 }
150
151 static void
meridional_sum(const Field & field1,Field & field2)152 meridional_sum(const Field &field1, Field &field2)
153 {
154 meridional_kernel_1(field1, field2, varray_sum, varray_sum_mv);
155 }
156
157 static void
meridional_meanw(const Field & field1,Field & field2)158 meridional_meanw(const Field &field1, Field &field2)
159 {
160 meridional_kernel_2(field1, field2, varray_weighted_mean, varray_weighted_mean_mv);
161 }
162
163 static void
meridional_avgw(const Field & field1,Field & field2)164 meridional_avgw(const Field &field1, Field &field2)
165 {
166 meridional_kernel_2(field1, field2, varray_weighted_mean, varray_weighted_avg_mv);
167 }
168
169 static void
meridional_varw(const Field & field1,Field & field2)170 meridional_varw(const Field &field1, Field &field2)
171 {
172 meridional_kernel_3(field1, field2, varray_weighted_var);
173 }
174
175 static void
meridional_var1w(const Field & field1,Field & field2)176 meridional_var1w(const Field &field1, Field &field2)
177 {
178 meridional_kernel_3(field1, field2, varray_weighted_var_1);
179 }
180
181 static void
meridional_stdw(const Field & field1,Field & field2)182 meridional_stdw(const Field &field1, Field &field2)
183 {
184 size_t rnmiss = 0;
185 const auto missval = field1.missval;
186
187 const auto nx = gridInqXsize(field1.grid);
188
189 meridional_varw(field1, field2);
190
191 for (size_t i = 0; i < nx; i++)
192 {
193 const auto rstd = var_to_std(field2.vec_d[i], missval);
194 if (DBL_IS_EQUAL(rstd, missval)) rnmiss++;
195 field2.vec_d[i] = rstd;
196 }
197
198 field2.nmiss = rnmiss;
199 }
200
201 static void
meridional_std1w(const Field & field1,Field & field2)202 meridional_std1w(const Field &field1, Field &field2)
203 {
204 size_t rnmiss = 0;
205 const auto missval = field1.missval;
206
207 const auto nx = gridInqXsize(field1.grid);
208
209 meridional_var1w(field1, field2);
210
211 for (size_t i = 0; i < nx; i++)
212 {
213 const auto rstd = var_to_std(field2.vec_d[i], missval);
214 if (DBL_IS_EQUAL(rstd, missval)) rnmiss++;
215 field2.vec_d[i] = rstd;
216 }
217
218 field2.nmiss = rnmiss;
219 }
220
221 static void
meridional_skew(const Field & field1,Field & field2)222 meridional_skew(const Field &field1, Field &field2)
223 {
224 meridional_kernel_4(field1, field2, varray_skew);
225 }
226
227 static void
meridional_kurt(const Field & field1,Field & field2)228 meridional_kurt(const Field &field1, Field &field2)
229 {
230 meridional_kernel_4(field1, field2, varray_kurt);
231 }
232
233 static void
meridional_median(const Field & field1,Field & field2)234 meridional_median(const Field &field1, Field &field2)
235 {
236 meridional_kernel_4(field1, field2, varray_median);
237 }
238
239 void
meridional_pctl(const Field & field1,Field & field2,const double pn)240 meridional_pctl(const Field &field1, Field &field2, const double pn)
241 {
242 size_t rnmiss = 0;
243 const auto missval = field1.missval;
244
245 const auto nx = gridInqXsize(field1.grid);
246 const auto ny = gridInqYsize(field1.grid);
247
248 Varray<double> v(ny);
249
250 if (field1.nmiss)
251 {
252 for (size_t i = 0; i < nx; i++)
253 {
254 size_t k = 0;
255 if (field1.memType == MemType::Float)
256 {
257 for (size_t j = 0; j < ny; j++)
258 if (!DBL_IS_EQUAL(field1.vec_d[j * nx + i], missval)) v[k++] = field1.vec_f[j * nx + i];
259 }
260 else
261 {
262 for (size_t j = 0; j < ny; j++)
263 if (!DBL_IS_EQUAL(field1.vec_d[j * nx + i], missval)) v[k++] = field1.vec_d[j * nx + i];
264 }
265
266 if (k > 0)
267 {
268 field2.vec_d[i] = percentile(v.data(), k, pn);
269 }
270 else
271 {
272 field2.vec_d[i] = missval;
273 rnmiss++;
274 }
275 }
276 }
277 else
278 {
279 for (size_t i = 0; i < nx; i++)
280 {
281 if (ny > 0)
282 {
283 if (field1.memType == MemType::Float)
284 varray_copy_meridional(i, nx, ny, field1.vec_f, v);
285 else
286 varray_copy_meridional(i, nx, ny, field1.vec_d, v);
287
288 field2.vec_d[i] = percentile(v.data(), ny, pn);
289 }
290 else
291 {
292 field2.vec_d[i] = missval;
293 rnmiss++;
294 }
295 }
296 }
297
298 field2.nmiss = rnmiss;
299 }
300
301 void
meridional_function(const Field & field1,Field & field2,int function)302 meridional_function(const Field &field1, Field &field2, int function)
303 {
304 // clang-format off
305 switch (function)
306 {
307 case FieldFunc_Min: return meridional_min(field1, field2);
308 case FieldFunc_Max: return meridional_max(field1, field2);
309 case FieldFunc_Range: return meridional_range(field1, field2);
310 case FieldFunc_Sum: return meridional_sum(field1, field2);
311 case FieldFunc_Meanw: return meridional_meanw(field1, field2);
312 case FieldFunc_Avgw: return meridional_avgw(field1, field2);
313 case FieldFunc_Stdw: return meridional_stdw(field1, field2);
314 case FieldFunc_Std1w: return meridional_std1w(field1, field2);
315 case FieldFunc_Varw: return meridional_varw(field1, field2);
316 case FieldFunc_Var1w: return meridional_var1w(field1, field2);
317 case FieldFunc_Skew: return meridional_skew(field1, field2);
318 case FieldFunc_Kurt: return meridional_kurt(field1, field2);
319 case FieldFunc_Median: return meridional_median(field1, field2);
320 default: cdo_abort("%s: function %d not implemented!", __func__, function);
321 }
322 // clang-format on
323 }
324