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 /*
9    This module "SampleGrid" contains the following operators:
10 
11     samplegrid      Resample current grid with given factor, typically 2 (which will half the resolution);
12                     tested on curvilinear and LCC grids;
13     subgrid         Similar to selindexbox but this operator works for LCC grids (tested on HARMONIE NWP model).
14 */
15 
16 #include <cdi.h>
17 
18 #include "process_int.h"
19 #include "param_conversion.h"
20 #include <mpim_grid.h>
21 #include "grid_define.h"
22 
23 static void
sampleData(const double * array1,int gridID1,double * array2,int gridID2,int resampleFactor)24 sampleData(const double *array1, int gridID1, double *array2, int gridID2, int resampleFactor)
25 {
26   const auto nlon1 = gridInqXsize(gridID1);
27   const auto nlat1 = gridInqYsize(gridID1);
28 
29   const auto nlon2 = gridInqXsize(gridID2);
30   const auto nlat2 = gridInqYsize(gridID2);
31 
32   if (cdoDebugExt >= 100)
33     cdo_print("%s(): (nlon1: %zu; nlat1: %zu) => (nlon2: %zu; nlat2: %zu); "
34               "gridID1: %d; gridID2: %d; resampleFactor: %d)",
35               __func__, nlon1, nlat1, nlon2, nlat2, gridID1, gridID2, resampleFactor);
36 
37   for (size_t ilat1 = 0; ilat1 < nlat1; ilat1 += resampleFactor)
38     for (size_t ilon1 = 0; ilon1 < nlon1; ilon1 += resampleFactor) *array2++ = array1[ilat1 * nlon1 + ilon1];
39 }
40 
41 static void
cropData(double * array1,int gridID1,double * array2,int gridID2,int subI0,int subI1,int subJ0,int subJ1)42 cropData(double *array1, int gridID1, double *array2, int gridID2, int subI0, int subI1, int subJ0, int subJ1)
43 {
44   const long nlon1 = gridInqXsize(gridID1);
45   const long nlon2 = gridInqXsize(gridID2);
46   const long rowLen = subI1 - subI0 + 1;  // must be same as nlon1
47 
48   if (rowLen != nlon2) cdo_abort("cropData() rowLen!= nlon2 [%d != %d]", rowLen, nlon2);
49 
50   if (cdoDebugExt >= 10) cdo_print("cropData(%d,%d,%d,%d) ...", subI0, subI1, subJ0, subJ1);
51 
52   long array2Idx = 0;
53   for (long ilat1 = subJ0; ilat1 <= subJ1; ilat1++)  // copy the last row as well..
54     {
55       array_copy(rowLen, &array1[ilat1 * nlon1 + subI0], &array2[array2Idx]);
56       array2Idx += rowLen;
57     }
58 }
59 
60 void *
Samplegrid(void * process)61 Samplegrid(void *process)
62 {
63   int resampleFactor = 0;
64   int subI0 = 0, subI1 = 0, subJ0 = 0, subJ1 = 0;
65   struct sbox_t
66   {
67     int gridSrcID, gridIDsampled;
68   };
69 
70   cdo_initialize(process);
71 
72   const auto SAMPLEGRID = cdo_operator_add("samplegrid", 0, 0, "resample factor, typically 2 (which will half the resolution)");
73   const auto SUBGRID = cdo_operator_add("subgrid", 0, 0, " sub-grid indices: i0,i1,j0,j1");
74 
75   const auto operatorID = cdo_operator_id();
76 
77   const auto nch = cdo_operator_argc();
78 
79   if (operatorID == SAMPLEGRID)
80     {
81       Debug(cdoDebugExt, "samplegrid operator requested..");
82       if (nch < 1) cdo_abort("Number of input arguments < 1; At least 1 argument needed: resample-factor (2,3,4, .. etc)");
83       resampleFactor = parameter_to_int(cdo_operator_argv(0));
84 
85       Debug(cdoDebugExt, "resampleFactor = %d", resampleFactor);
86     }
87   else if (operatorID == SUBGRID)
88     {
89       Debug(cdoDebugExt, "subgrid operator requested..");
90       if (nch < 4)
91         cdo_abort("Number of input arguments < 4; Must specify sub-grid indices: i0,i1,j0,j1; This works only with LCC grid."
92                   " For other grids use: selindexbox");
93       subI0 = parameter_to_int(cdo_operator_argv(0));
94       subI1 = parameter_to_int(cdo_operator_argv(1));
95       subJ0 = parameter_to_int(cdo_operator_argv(2));
96       subJ1 = parameter_to_int(cdo_operator_argv(3));
97     }
98   else
99     cdo_abort("Unknown operator ...");
100 
101   const auto streamID1 = cdo_open_read(0);
102 
103   const auto vlistID1 = cdo_stream_inq_vlist(streamID1);
104   const auto vlistID2 = vlistDuplicate(vlistID1);
105 
106   const auto taxisID1 = vlistInqTaxis(vlistID1);
107   const auto taxisID2 = taxisDuplicate(taxisID1);
108   vlistDefTaxis(vlistID2, taxisID2);
109 
110   const auto nvars = vlistNvars(vlistID1);
111   std::vector<bool> vars(nvars, false);
112 
113   auto ngrids = vlistNgrids(vlistID1);
114 
115   Debug(cdoDebugExt, "ngrids = %d\n", ngrids);
116 
117   std::vector<sbox_t> sbox(ngrids);
118 
119   for (int index = 0; index < ngrids; index++)
120     {
121       const auto gridSrcID = vlistGrid(vlistID1, index);
122       int gridIDsampled = -1;
123 
124       if (gridInqSize(gridSrcID) <= 1) continue;
125 
126       int gridtype = gridInqType(gridSrcID);
127       if (!(gridtype == GRID_GAUSSIAN || gridtype == GRID_LONLAT || gridtype == GRID_PROJECTION || gridtype == GRID_CURVILINEAR
128             || gridtype == GRID_GENERIC))
129         cdo_abort("Unsupported gridtype: %s", gridNamePtr(gridtype));
130 
131       if (operatorID == SAMPLEGRID)
132         {
133           gridIDsampled = cdo_define_sample_grid(gridSrcID, resampleFactor);
134         }
135       else if (operatorID == SUBGRID)
136         {
137           gridIDsampled = cdo_define_subgrid_grid(gridSrcID, subI0, subI1, subJ0, subJ1);
138         }
139 
140       sbox[index].gridSrcID = gridSrcID;
141       sbox[index].gridIDsampled = gridIDsampled;
142 
143       // if ( cdoDebugExt>=10 ) cdoPrintGrid(gridSrcID, 1);
144       // if ( cdoDebugExt>=10 ) cdoPrintGrid(gridIDsampled, 1);
145 
146       vlistChangeGridIndex(vlistID2, index, gridIDsampled);
147 
148       for (int varID = 0; varID < nvars; varID++)
149         if (gridSrcID == vlistInqVarGrid(vlistID1, varID)) vars[varID] = true;
150     }
151 
152   Debug(cdoDebugExt, [&]() {
153     if (operatorID == SAMPLEGRID) Debug("Resampled grid has been created.");
154     if (operatorID == SUBGRID) Debug("Sub-grid has been created.");
155   });
156 
157   const auto streamID2 = cdo_open_write(1);
158   cdo_def_vlist(streamID2, vlistID2);
159 
160   auto gridsize = vlistGridsizeMax(vlistID1);
161   if (vlistNumber(vlistID1) != CDI_REAL) gridsize *= 2;
162   Varray<double> array1(gridsize);
163 
164   size_t gridsize2 = vlistGridsizeMax(vlistID2);
165   if (vlistNumber(vlistID2) != CDI_REAL) gridsize2 *= 2;
166   Varray<double> array2(gridsize2);
167 
168   Debug(cdoDebugExt, "gridsize = %ld, gridsize2 = %ld", gridsize, gridsize2);
169 
170   int tsID = 0;
171   while (true)
172     {
173       const auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
174       if (nrecs == 0) break;
175 
176       cdo_taxis_copy_timestep(taxisID2, taxisID1);
177       cdo_def_timestep(streamID2, tsID);
178 
179       for (int recID = 0; recID < nrecs; recID++)
180         {
181           int varID, levelID;
182           cdo_inq_record(streamID1, &varID, &levelID);
183           size_t nmiss;
184           cdo_read_record(streamID1, array1.data(), &nmiss);
185 
186           cdo_def_record(streamID2, varID, levelID);
187 
188           Debug(cdoDebugExt >= 20, "Processing record (%d) of %d.", recID, nrecs);
189 
190           if (vars[varID])
191             {
192               const auto gridSrcID = vlistInqVarGrid(vlistID1, varID);
193 
194               int index;
195               for (index = 0; index < ngrids; index++)
196                 if (gridSrcID == sbox[index].gridSrcID) break;
197 
198               if (index == ngrids) cdo_abort("Internal problem, grid not found!");
199 
200               const int gridIDsampled = sbox[index].gridIDsampled;
201               gridsize2 = gridInqSize(gridIDsampled);
202 
203               if (operatorID == SAMPLEGRID)
204                 {
205                   sampleData(array1.data(), gridSrcID, array2.data(), gridIDsampled, resampleFactor);
206                 }
207               else if (operatorID == SUBGRID)
208                 {
209                   cropData(array1.data(), gridSrcID, array2.data(), gridIDsampled, subI0, subI1, subJ0, subJ1);
210                 }
211 
212               if (nmiss)
213                 {
214                   const auto missval = vlistInqVarMissval(vlistID2, varID);
215                   nmiss = varray_num_mv(gridsize2, array2, missval);
216                 }
217 
218               cdo_write_record(streamID2, array2.data(), nmiss);
219             }
220           else
221             {
222               cdo_write_record(streamID2, array1.data(), nmiss);
223             }
224         }
225 
226       tsID++;
227     }
228 
229   cdo_stream_close(streamID2);
230   cdo_stream_close(streamID1);
231 
232   vlistDestroy(vlistID2);
233 
234   cdo_finish();
235 
236   return nullptr;
237 }
238