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       Splittime  splithour       Split hours
12       Splittime  splitday        Split days
13       Splittime  splitmon        Split months
14       Splittime  splitseas       Split seasons
15 */
16 
17 #include <time.h>
18 #include <cdi.h>
19 
20 #include "cdo_options.h"
21 #include "process_int.h"
22 #include "cdo_season.h"
23 #include "util_files.h"
24 
25 constexpr int MaxStreams = 32;
26 
27 struct tm
datetime_to_tm(int64_t date,int time)28 datetime_to_tm(int64_t date, int time)
29 {
30   int year, month, day, hour, minute, second;
31   cdiDecodeDate(date, &year, &month, &day);
32   cdiDecodeTime(time, &hour, &minute, &second);
33 
34   struct tm stime;
35   memset(&stime, 0, sizeof(struct tm));
36 
37   stime.tm_sec = second;
38   stime.tm_min = minute;
39   stime.tm_hour = hour;
40   stime.tm_mday = day;
41   stime.tm_mon = month - 1;
42   stime.tm_year = year - 1900;
43 
44   return stime;
45 }
46 
47 void *
Splittime(void * process)48 Splittime(void *process)
49 {
50   CdoStreamID streamID2;
51   CdoStreamID streamIDs[MaxStreams];
52   int tsIDs[MaxStreams];
53   int index = 0;
54 
55   cdo_initialize(process);
56 
57   if (process_self().m_ID != 0) cdo_abort("This operator can't be combined with other operators!");
58 
59   const auto dataIsUnchanged = data_is_unchanged();
60 
61   enum
62   {
63     func_time,
64     func_date
65   };
66 
67   // clang-format off
68                          cdo_operator_add("splithour", func_time, 10000, nullptr);
69                          cdo_operator_add("splitday",  func_date,     1, nullptr);
70   const auto SPLITMON  = cdo_operator_add("splitmon",  func_date,   100, nullptr);
71   const auto SPLITSEAS = cdo_operator_add("splitseas", func_date,   100, nullptr);
72   // clang-format on
73 
74   const auto operatorID = cdo_operator_id();
75   const auto operfunc = cdo_operator_f1(operatorID);
76   const auto operintval = cdo_operator_f2(operatorID);
77 
78   const char *format = nullptr;
79   if (operatorID == SPLITMON && cdo_operator_argc() == 1)
80     format = cdo_operator_argv(0).c_str();
81   else
82     operator_check_argc(0);
83 
84   const char *seas_name[4];
85   get_season_name(seas_name);
86 
87   for (int i = 0; i < MaxStreams; i++) streamIDs[i] = CDO_STREAM_UNDEF;
88   for (int i = 0; i < MaxStreams; i++) tsIDs[i] = 0;
89 
90   const auto streamID1 = cdo_open_read(0);
91 
92   const auto vlistID1 = cdo_stream_inq_vlist(streamID1);
93   const auto vlistID2 = vlistDuplicate(vlistID1);
94 
95   const auto taxisID1 = vlistInqTaxis(vlistID1);
96   const auto taxisID2 = taxisDuplicate(taxisID1);
97   vlistDefTaxis(vlistID2, taxisID2);
98 
99   char filename[8192];
100   strcpy(filename, cdo_get_obase().c_str());
101   const int nchars = strlen(filename);
102 
103   auto refname = cdo_get_stream_name(0);
104   char filesuffix[32] = { 0 };
105   FileUtils::gen_suffix(filesuffix, sizeof(filesuffix), cdo_inq_filetype(streamID1), vlistID1, refname);
106 
107   VarList varList1;
108   varListInit(varList1, vlistID1);
109 
110   Varray<double> array;
111   //  if (! dataIsUnchanged)
112   {
113     auto gridsizemax = vlistGridsizeMax(vlistID1);
114     if (vlistNumber(vlistID1) != CDI_REAL) gridsizemax *= 2;
115     array.resize(gridsizemax);
116   }
117 
118   const auto nvars = vlistNvars(vlistID1);
119   int nconst = 0;
120   for (int varID = 0; varID < nvars; varID++)
121     if (varList1[varID].timetype == TIME_CONSTANT) nconst++;
122 
123   FieldVector2D vars;
124   if (nconst)
125     {
126       vars.resize(nvars);
127 
128       for (int varID = 0; varID < nvars; varID++)
129         {
130           if (varList1[varID].timetype == TIME_CONSTANT)
131             {
132               const auto gridID = varList1[varID].gridID;
133               const auto gridsize = varList1[varID].gridsize;
134               const auto nlevels = varList1[varID].nlevels;
135 
136               vars[varID].resize(nlevels);
137 
138               for (int levelID = 0; levelID < nlevels; levelID++)
139                 {
140                   vars[varID][levelID].grid = gridID;
141                   vars[varID][levelID].resize(gridsize);
142                 }
143             }
144         }
145     }
146 
147   int tsID = 0;
148   while (true)
149     {
150       const auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
151       if (nrecs == 0) break;
152 
153       cdo_taxis_copy_timestep(taxisID2, taxisID1);
154       const auto vdate = taxisInqVdate(taxisID1);
155       const auto vtime = taxisInqVtime(taxisID1);
156 
157       if (operfunc == func_date)
158         {
159           index = (vdate / operintval) % 100;
160           if (index < 0) index = -index;
161 
162           if (operatorID == SPLITSEAS) index = month_to_season(index);
163         }
164       else if (operfunc == func_time)
165         {
166           index = (vtime / operintval) % 100;
167         }
168 
169       if (index < 0 || index >= MaxStreams) cdo_abort("Index out of range!");
170 
171       streamID2 = streamIDs[index];
172       if (streamID2 == CDO_STREAM_UNDEF)
173         {
174           if (operatorID == SPLITSEAS)
175             {
176               sprintf(filename + nchars, "%3s", seas_name[index]);
177               if (filesuffix[0]) sprintf(filename + nchars + 3, "%s", filesuffix);
178             }
179           else
180             {
181               char oformat[32];
182               strcpy(oformat, "%02d");
183 
184               if (operatorID == SPLITMON && format)
185                 {
186                   char sbuf[32];
187                   auto stime = datetime_to_tm(vdate, vtime);
188                   const auto slen = strftime(sbuf, sizeof(sbuf), format, &stime);
189                   if (slen) strcpy(oformat, sbuf);
190                 }
191 
192               const auto slen = sprintf(filename + nchars, oformat, index);
193               if (filesuffix[0]) sprintf(filename + nchars + slen, "%s", filesuffix);
194             }
195 
196           if (Options::cdoVerbose) cdo_print("create file %s", filename);
197 
198           streamID2 = cdo_open_write(filename);
199           cdo_def_vlist(streamID2, vlistID2);
200           streamIDs[index] = streamID2;
201         }
202 
203       cdo_def_timestep(streamID2, tsIDs[index]);
204 
205       if (tsID > 0 && tsIDs[index] == 0 && nconst)
206         {
207           for (int varID = 0; varID < nvars; varID++)
208             {
209               if (varList1[varID].timetype == TIME_CONSTANT)
210                 {
211                   for (int levelID = 0; levelID < varList1[varID].nlevels; levelID++)
212                     {
213                       cdo_def_record(streamID2, varID, levelID);
214                       auto nmiss = vars[varID][levelID].nmiss;
215                       cdo_write_record(streamID2, vars[varID][levelID].vec_d.data(), nmiss);
216                     }
217                 }
218             }
219         }
220 
221       for (int recID = 0; recID < nrecs; recID++)
222         {
223           int varID, levelID;
224           cdo_inq_record(streamID1, &varID, &levelID);
225           cdo_def_record(streamID2, varID, levelID);
226 
227           if (dataIsUnchanged && !(tsID == 0 && nconst))
228             {
229               cdo_copy_record(streamID2, streamID1);
230             }
231           else
232             {
233               size_t nmiss;
234               cdo_read_record(streamID1, array.data(), &nmiss);
235               cdo_write_record(streamID2, array.data(), nmiss);
236 
237               if (tsID == 0 && nconst)
238                 {
239                   if (varList1[varID].timetype == TIME_CONSTANT)
240                     {
241                       varray_copy(varList1[varID].gridsize, array, vars[varID][levelID].vec_d);
242                       vars[varID][levelID].nmiss = nmiss;
243                     }
244                 }
245             }
246         }
247 
248       tsIDs[index]++;
249       tsID++;
250     }
251 
252   cdo_stream_close(streamID1);
253 
254   for (index = 0; index < MaxStreams; index++)
255     {
256       if (streamIDs[index] != CDO_STREAM_UNDEF) cdo_stream_close(streamIDs[index]);
257     }
258 
259   vlistDestroy(vlistID2);
260 
261   cdo_finish();
262 
263   return nullptr;
264 }
265