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 contains the following operators:
10
11 Change chcode Change code number
12 Change chtabnum Change GRIB1 parameter table number
13 Change chparam Change parameter identifier
14 Change chname Change variable or coordinate name
15 Change chlevel Change level
16 Change chlevelc Change level of one code
17 Change chlevelv Change level of one variable
18 Change chltype Change GRIB level type
19 */
20
21 #include <cdi.h>
22
23 #include "cdo_options.h"
24 #include "process_int.h"
25 #include "param_conversion.h"
26
27 static void
changeCode(const int vlistID2,const int nch,const std::vector<int> & chints)28 changeCode(const int vlistID2, const int nch, const std::vector<int> &chints)
29 {
30 const auto nvars = vlistNvars(vlistID2);
31 for (int varID = 0; varID < nvars; varID++)
32 {
33 const auto code = vlistInqVarCode(vlistID2, varID);
34 for (int i = 0; i < nch; i += 2)
35 if (code == chints[i]) vlistDefVarCode(vlistID2, varID, chints[i + 1]);
36 }
37 }
38
39 static void
changeTabnum(const int vlistID2,const int nch,const std::vector<int> & chints)40 changeTabnum(const int vlistID2, const int nch, const std::vector<int> &chints)
41 {
42 const auto nvars = vlistNvars(vlistID2);
43 for (int varID = 0; varID < nvars; varID++)
44 {
45 const auto tabnum = tableInqNum(vlistInqVarTable(vlistID2, varID));
46 for (int i = 0; i < nch; i += 2)
47 if (tabnum == chints[i])
48 {
49 const auto tableID = tableDef(-1, chints[i + 1], nullptr);
50 vlistDefVarTable(vlistID2, varID, tableID);
51 }
52 }
53 }
54
55 static void
changeParam(const int vlistID2,const int nch,const std::vector<const char * > & chnames)56 changeParam(const int vlistID2, const int nch, const std::vector<const char *> &chnames)
57 {
58 const auto nvars = vlistNvars(vlistID2);
59 for (int varID = 0; varID < nvars; varID++)
60 {
61 const auto param = vlistInqVarParam(vlistID2, varID);
62 if (Options::cdoVerbose)
63 {
64 int pnum, pcat, pdis;
65 cdiDecodeParam(param, &pnum, &pcat, &pdis);
66 cdo_print("pnum, pcat, pdis: %d.%d.%d", pnum, pcat, pdis);
67 }
68 for (int i = 0; i < nch; i += 2)
69 if (param == string_to_param(chnames[i])) vlistDefVarParam(vlistID2, varID, string_to_param(chnames[i + 1]));
70 }
71 }
72
73 static void
changeName(const int vlistID2,const int nch,const std::vector<const char * > & chnames)74 changeName(const int vlistID2, const int nch, const std::vector<const char *> &chnames)
75 {
76 char varname[CDI_MAX_NAME], varname2[CDI_MAX_NAME];
77 const auto npairs = nch / 2;
78 std::vector<std::pair<const char *, const char *>> vpairs(npairs);
79 for (int i = 0; i < npairs; ++i) vpairs[i].first = chnames[i * 2];
80 for (int i = 0; i < npairs; ++i) vpairs[i].second = chnames[i * 2 + 1];
81
82 const auto nvars = vlistNvars(vlistID2);
83 std::vector<bool> namefound(npairs, false);
84 for (int varID = 0; varID < nvars; varID++)
85 {
86 vlistInqVarName(vlistID2, varID, varname);
87 for (int i = 0; i < npairs; ++i)
88 if (strcmp(varname, vpairs[i].first) == 0)
89 {
90 namefound[i] = true;
91 cdiDefKeyString(vlistID2, varID, CDI_KEY_NAME, vpairs[i].second);
92 break;
93 }
94 }
95
96 auto searchForGridName = false;
97 for (int i = 0; i < npairs; ++i)
98 if (!namefound[i])
99 {
100 searchForGridName = true;
101 break;
102 }
103
104 if (searchForGridName)
105 {
106 const auto ngrids = vlistNgrids(vlistID2);
107 for (int index = 0; index < ngrids; ++index)
108 {
109 int gridID2 = -1;
110 auto gridID1 = vlistGrid(vlistID2, index);
111 int length = CDI_MAX_NAME;
112 cdiInqKeyString(gridID1, CDI_XAXIS, CDI_KEY_NAME, varname, &length);
113 length = CDI_MAX_NAME;
114 cdiInqKeyString(gridID1, CDI_YAXIS, CDI_KEY_NAME, varname2, &length);
115 auto xfound = false, yfound = false;
116 for (int i = 0; i < npairs; ++i)
117 {
118 if (!namefound[i])
119 {
120 if (strcmp(varname, vpairs[i].first) == 0)
121 {
122 xfound = true;
123 namefound[i] = true;
124 if (gridID2 == -1) gridID2 = gridDuplicate(gridID1);
125 cdiDefKeyString(gridID2, CDI_XAXIS, CDI_KEY_NAME, vpairs[i].second);
126 }
127 }
128 if (!namefound[i])
129 {
130 if (strcmp(varname2, vpairs[i].first) == 0)
131 {
132 yfound = true;
133 namefound[i] = true;
134 if (gridID2 == -1) gridID2 = gridDuplicate(gridID1);
135 cdiDefKeyString(gridID2, CDI_YAXIS, CDI_KEY_NAME, vpairs[i].second);
136 }
137 }
138
139 if (xfound && yfound) break;
140 }
141
142 if (gridID2 != -1) vlistChangeGrid(vlistID2, gridID1, gridID2);
143 }
144 }
145
146 auto searchForZaxisName = false;
147 for (int i = 0; i < npairs; ++i)
148 if (!namefound[i])
149 {
150 searchForZaxisName = true;
151 break;
152 }
153
154 if (searchForZaxisName)
155 {
156 const auto nzaxis = vlistNzaxis(vlistID2);
157 for (int index = 0; index < nzaxis; ++index)
158 {
159 const auto zaxisID1 = vlistZaxis(vlistID2, index);
160 int length = CDI_MAX_NAME;
161 cdiInqKeyString(zaxisID1, CDI_GLOBAL, CDI_KEY_NAME, varname, &length);
162 for (int i = 0; i < npairs; ++i)
163 {
164 if (!namefound[i])
165 {
166 if (strcmp(varname, vpairs[i].first) == 0)
167 {
168 namefound[i] = true;
169 const auto zaxisID2 = zaxisDuplicate(zaxisID1);
170 cdiDefKeyString(zaxisID2, CDI_GLOBAL, CDI_KEY_NAME, vpairs[i].second);
171 vlistChangeZaxis(vlistID2, zaxisID1, zaxisID2);
172 break;
173 }
174 }
175 }
176 }
177 }
178
179 for (int i = 0; i < npairs; ++i)
180 if (!namefound[i]) cdo_warning("Variable name %s not found!", vpairs[i].first);
181 }
182
183 static void
changeUnit(const int vlistID2,const int nch,const std::vector<const char * > & chnames)184 changeUnit(const int vlistID2, const int nch, const std::vector<const char *> &chnames)
185 {
186 char units[CDI_MAX_NAME];
187 const auto nvars = vlistNvars(vlistID2);
188 for (int varID = 0; varID < nvars; varID++)
189 {
190 vlistInqVarUnits(vlistID2, varID, units);
191 for (int i = 0; i < nch; i += 2)
192 if (strcmp(units, chnames[i]) == 0) cdiDefKeyString(vlistID2, varID, CDI_KEY_UNITS, chnames[i + 1]);
193 }
194 }
195
196 static void
changeLevel(const int vlistID2,const int nch,const std::vector<double> & chlevels)197 changeLevel(const int vlistID2, const int nch, const std::vector<double> &chlevels)
198 {
199 const auto nzaxis = vlistNzaxis(vlistID2);
200 for (int index = 0; index < nzaxis; index++)
201 {
202 const auto zaxisID1 = vlistZaxis(vlistID2, index);
203 if (zaxisInqLevels(zaxisID1, nullptr))
204 {
205 const auto nlevs = zaxisInqSize(zaxisID1);
206 Varray<double> levels(nlevs);
207 zaxisInqLevels(zaxisID1, &levels[0]);
208
209 int nfound = 0;
210 for (int i = 0; i < nch; i += 2)
211 for (int k = 0; k < nlevs; k++)
212 if (std::fabs(levels[k] - chlevels[i]) < 0.0001) nfound++;
213
214 if (nfound)
215 {
216 Varray<double> newlevels = levels;
217 const auto zaxisID2 = zaxisDuplicate(zaxisID1);
218 for (int i = 0; i < nch; i += 2)
219 for (int k = 0; k < nlevs; k++)
220 if (std::fabs(levels[k] - chlevels[i]) < 0.001) newlevels[k] = chlevels[i + 1];
221
222 zaxisDefLevels(zaxisID2, &newlevels[0]);
223 vlistChangeZaxis(vlistID2, zaxisID1, zaxisID2);
224 }
225 }
226 }
227 }
228
229 static void
changeVarLevel(int varID,const int vlistID2,const std::vector<double> & chlevels)230 changeVarLevel(int varID, const int vlistID2, const std::vector<double> &chlevels)
231 {
232 const auto zaxisID1 = vlistInqVarZaxis(vlistID2, varID);
233 if (zaxisInqLevels(zaxisID1, nullptr))
234 {
235 const auto nlevs = zaxisInqSize(zaxisID1);
236 Varray<double> levels(nlevs);
237 zaxisInqLevels(zaxisID1, &levels[0]);
238
239 int nfound = 0;
240 for (int k = 0; k < nlevs; k++)
241 if (std::fabs(levels[k] - chlevels[0]) < 0.0001) nfound++;
242
243 if (nfound)
244 {
245 const auto zaxisID2 = zaxisDuplicate(zaxisID1);
246 for (int k = 0; k < nlevs; k++)
247 if (std::fabs(levels[k] - chlevels[0]) < 0.001) levels[k] = chlevels[1];
248
249 zaxisDefLevels(zaxisID2, &levels[0]);
250 vlistChangeVarZaxis(vlistID2, varID, zaxisID2);
251 }
252 else
253 cdo_abort("Level %g not found!", chlevels[0]);
254 }
255 }
256
257 static void
changeLevelByCode(int chcode,const int vlistID2,const std::vector<double> & chlevels)258 changeLevelByCode(int chcode, const int vlistID2, const std::vector<double> &chlevels)
259 {
260 int varID;
261 const auto nvars = vlistNvars(vlistID2);
262 for (varID = 0; varID < nvars; varID++)
263 {
264 const auto code = vlistInqVarCode(vlistID2, varID);
265 if (code == chcode) break;
266 }
267 if (varID == nvars) cdo_abort("Code %d not found!", chcode);
268
269 changeVarLevel(varID, vlistID2, chlevels);
270 }
271
272 static void
changeLevelByName(const char * chname,const int vlistID2,const std::vector<double> & chlevels)273 changeLevelByName(const char *chname, const int vlistID2, const std::vector<double> &chlevels)
274 {
275 char varname[CDI_MAX_NAME];
276 int varID;
277 const auto nvars = vlistNvars(vlistID2);
278 for (varID = 0; varID < nvars; varID++)
279 {
280 vlistInqVarName(vlistID2, varID, varname);
281 if (strcmp(varname, chname) == 0) break;
282 }
283 if (varID == nvars) cdo_abort("Variable name %s not found!", chname);
284
285 changeVarLevel(varID, vlistID2, chlevels);
286 }
287
288 static void
changeLtype(const int vlistID2,const int nch,const std::vector<int> & chltypes)289 changeLtype(const int vlistID2, const int nch, const std::vector<int> &chltypes)
290 {
291 const auto nzaxis = vlistNzaxis(vlistID2);
292 for (int index = 0; index < nzaxis; index++)
293 {
294 const auto zaxisID1 = vlistZaxis(vlistID2, index);
295 const auto zaxisID2 = zaxisDuplicate(zaxisID1);
296 int ltype = 0;
297 cdiInqKeyInt(zaxisID1, CDI_GLOBAL, CDI_KEY_TYPEOFFIRSTFIXEDSURFACE, <ype);
298
299 for (int i = 0; i < nch; i += 2)
300 {
301 const auto ltype1 = chltypes[i];
302 const auto ltype2 = chltypes[i + 1];
303 if (ltype1 == ltype)
304 {
305 zaxisChangeType(zaxisID2, ZAXIS_GENERIC);
306 cdiDefKeyInt(zaxisID2, CDI_GLOBAL, CDI_KEY_TYPEOFFIRSTFIXEDSURFACE, ltype2);
307 vlistChangeZaxis(vlistID2, zaxisID1, zaxisID2);
308 }
309 }
310 }
311 }
312
313 void *
Change(void * process)314 Change(void *process)
315 {
316 const char *chname = nullptr;
317 int chcode = 0;
318 std::vector<const char *> chnames;
319 std::vector<int> chints, chltypes;
320 std::vector<double> chlevels;
321
322 cdo_initialize(process);
323
324 // clang-format off
325 const auto CHCODE = cdo_operator_add("chcode", 0, 0, "pairs of old and new code numbers");
326 const auto CHTABNUM = cdo_operator_add("chtabnum", 0, 0, "pairs of old and new GRIB1 table numbers");
327 const auto CHPARAM = cdo_operator_add("chparam", 0, 0, "pairs of old and new parameter identifiers");
328 const auto CHNAME = cdo_operator_add("chname", 0, 0, "pairs of old and new variable names");
329 const auto CHUNIT = cdo_operator_add("chunit", 0, 0, "pairs of old and new variable units");
330 const auto CHLEVEL = cdo_operator_add("chlevel", 0, 0, "pairs of old and new levels");
331 const auto CHLEVELC = cdo_operator_add("chlevelc", 0, 0, "code number, old and new level");
332 const auto CHLEVELV = cdo_operator_add("chlevelv", 0, 0, "variable name, old and new level");
333 const auto CHLTYPE = cdo_operator_add("chltype", 0, 0, "pairs of old and new type");
334 // clang-format on
335
336 const auto operatorID = cdo_operator_id();
337
338 operator_input_arg(cdo_operator_enter(operatorID));
339
340 const auto nch = cdo_operator_argc();
341
342 if (operatorID == CHCODE || operatorID == CHTABNUM)
343 {
344 if (nch % 2) cdo_abort("Odd number of input arguments!");
345 chints.resize(nch);
346 for (int i = 0; i < nch; i++) chints[i] = parameter_to_int(cdo_operator_argv(i));
347 }
348 else if (operatorID == CHPARAM || operatorID == CHNAME || operatorID == CHUNIT)
349 {
350 if (nch % 2) cdo_abort("Odd number of input arguments!");
351 chnames.resize(nch);
352 for (int i = 0; i < nch; i++) chnames[i] = &cdo_operator_argv(i)[0];
353 }
354 else if (operatorID == CHLEVEL)
355 {
356 if (nch % 2) cdo_abort("Odd number of input arguments!");
357 chlevels.resize(nch);
358 for (int i = 0; i < nch; i++) chlevels[i] = parameter_to_double(cdo_operator_argv(i));
359 }
360 else if (operatorID == CHLEVELC)
361 {
362 operator_check_argc(3);
363
364 chcode = parameter_to_int(cdo_operator_argv(0));
365 chlevels.resize(2);
366 chlevels[0] = parameter_to_double(cdo_operator_argv(1));
367 chlevels[1] = parameter_to_double(cdo_operator_argv(2));
368 }
369 else if (operatorID == CHLEVELV)
370 {
371 operator_check_argc(3);
372
373 chname = cdo_operator_argv(0).c_str();
374 chlevels.resize(2);
375 chlevels[0] = parameter_to_double(cdo_operator_argv(1));
376 chlevels[1] = parameter_to_double(cdo_operator_argv(2));
377 }
378 else if (operatorID == CHLTYPE)
379 {
380 if (nch % 2) cdo_abort("Odd number of input arguments!");
381 chltypes.resize(nch);
382 for (int i = 0; i < nch; i++) chltypes[i] = parameter_to_int(cdo_operator_argv(i));
383 }
384
385 const auto streamID1 = cdo_open_read(0);
386
387 const auto vlistID1 = cdo_stream_inq_vlist(streamID1);
388 const auto vlistID2 = vlistDuplicate(vlistID1);
389
390 const auto taxisID1 = vlistInqTaxis(vlistID1);
391 const auto taxisID2 = taxisDuplicate(taxisID1);
392 vlistDefTaxis(vlistID2, taxisID2);
393
394 // clang-format off
395 if (operatorID == CHCODE) changeCode(vlistID2, nch, chints);
396 else if (operatorID == CHTABNUM) changeTabnum(vlistID2, nch, chints);
397 else if (operatorID == CHPARAM) changeParam(vlistID2, nch, chnames);
398 else if (operatorID == CHNAME) changeName(vlistID2, nch, chnames);
399 else if (operatorID == CHUNIT) changeUnit(vlistID2, nch, chnames);
400 else if (operatorID == CHLEVEL) changeLevel(vlistID2, nch, chlevels);
401 else if (operatorID == CHLEVELC) changeLevelByCode(chcode, vlistID2, chlevels);
402 else if (operatorID == CHLEVELV) changeLevelByName(chname, vlistID2, chlevels);
403 else if (operatorID == CHLTYPE) changeLtype(vlistID2, nch, chltypes);
404 // clang-format on
405
406 const auto gridsizemax = vlistGridsizeMax(vlistID2);
407 Varray<double> array(gridsizemax);
408
409 CdoStreamID streamID2 = CDO_STREAM_UNDEF;
410
411 int tsID = 0;
412 while (true)
413 {
414 const auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
415 if (nrecs == 0) break;
416
417 cdo_taxis_copy_timestep(taxisID2, taxisID1);
418 if (streamID2 == CDO_STREAM_UNDEF)
419 {
420 streamID2 = cdo_open_write(1);
421 cdo_def_vlist(streamID2, vlistID2);
422 }
423 cdo_def_timestep(streamID2, tsID);
424
425 for (int recID = 0; recID < nrecs; recID++)
426 {
427 int varID, levelID;
428 cdo_inq_record(streamID1, &varID, &levelID);
429 cdo_def_record(streamID2, varID, levelID);
430
431 size_t nmiss;
432 cdo_read_record(streamID1, &array[0], &nmiss);
433 cdo_write_record(streamID2, &array[0], nmiss);
434 }
435
436 tsID++;
437 }
438
439 cdo_stream_close(streamID1);
440 cdo_stream_close(streamID2);
441
442 cdo_finish();
443
444 return nullptr;
445 }
446