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