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