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