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, &ltype);
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