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