1 /*
2 This file is part of CDO. CDO is a collection of Operators to manipulate and analyse Climate model Data.
3
4 Copyright (C) 2006 Brockmann Consult
5
6 Author: Ralf Quast
7
8 */
9
10 #include <algorithm>
11 #include <assert.h>
12
13 #include <cdi.h>
14
15 #include "process_int.h"
16 #include "timebase.h"
17 #include "datetime.h"
18 #include "ecautil.h"
19
20 /**
21 * Convert a Gregorian/Julian date to a Julian day number.
22 *
23 * The Gregorian calendar was adopted midday, October 15, 1582.
24 */
25 static unsigned long
gregdate_to_julday(int year,int month,int day)26 gregdate_to_julday(int year, /* Gregorian year */
27 int month, /* Gregorian month (1-12) */
28 int day /* Gregorian day (1-31) */
29 )
30 {
31 #if INT_MAX <= 0X7FFF
32 long igreg = 15 + 31 * (10 + (12 * 1582));
33 long iy; /* signed, origin 0 year */
34 long ja; /* Julian century */
35 long jm; /* Julian month */
36 long jy; /* Julian year */
37 #else
38 int igreg = 15 + 31 * (10 + (12 * 1582));
39 int iy; /* signed, origin 0 year */
40 int ja; /* Julian century */
41 int jm; /* Julian month */
42 int jy; /* Julian year */
43 #endif
44 unsigned long julday; /* returned Julian day number */
45
46 /*
47 * Because there is no 0 BC or 0 AD, assume the user wants the start of
48 * the common era if they specify year 0.
49 */
50 if (year == 0) year = 1;
51
52 iy = year;
53 if (year < 0) iy++;
54 if (month > 2)
55 {
56 jy = iy;
57 jm = month + 1;
58 }
59 else
60 {
61 jy = iy - 1;
62 jm = month + 13;
63 }
64
65 /*
66 * Note: SLIGHTLY STRANGE CONSTRUCTIONS REQUIRED TO AVOID PROBLEMS WITH
67 * OPTIMISATION OR GENERAL ERRORS UNDER VMS!
68 */
69 julday = day + (int) (30.6001 * jm);
70 if (jy >= 0)
71 {
72 julday += 365 * jy;
73 julday += (unsigned long) (0.25 * jy);
74 }
75 else
76 {
77 double xi = 365.25 * jy;
78
79 {
80 int temp = xi * 100;
81 if (temp % 100 != 0) xi -= 1;
82 }
83 // if (((int) xi) != xi) xi -= 1; // old version, caused warning (25.08.2020 O.Heidmann)
84 julday += (int) xi;
85 }
86 julday += 1720995;
87
88 if (day + (31 * (month + (12 * iy))) >= igreg)
89 {
90 ja = jy / 100;
91 julday -= ja;
92 julday += 2;
93 julday += ja / 4;
94 }
95
96 return julday;
97 }
98
99 /**
100 * Computes the day-of-year correspnding a given Gregorian date.
101 *
102 * @param date a Gregorian date in the form YYYYMMDD
103 *
104 * @return the day-of-year
105 */
106 static unsigned long
day_of_year(int64_t date)107 day_of_year(int64_t date)
108 {
109 const int year = date / 10000;
110 const int month = (date - year * 10000) / 100;
111 const int day = date - year * 10000 - month * 100;
112
113 return gregdate_to_julday(year, month, day) - gregdate_to_julday(year, 1, 1) + 1;
114 }
115
116 /**
117 * Counts the number of nonmissing values. The result of the operation
118 * is computed according to the following rules:
119 *
120 * field1 field2 mode result
121 * a b 0 a + 1
122 * a miss 0 a
123 * miss b 0 1
124 * miss miss 0 0
125 *
126 * a b 1 a + 1
127 * a miss 1 0
128 * miss b 1 1
129 * miss miss 1 0
130 *
131 * a b n b < n ? a : b > n ? a + 1 : a + n
132 * a miss n a
133 * miss b n b < n ? 0 : b
134 * miss miss n 0
135 *
136 * @param field1 the 1st input field, also holds the result
137 * @param field2 the 2nd input field
138 * @param mode the counting mode, must be an exact mathematical
139 * integer
140 */
141 static void
count(Field & field1,const Field & field2,double mode)142 count(Field &field1, const Field &field2, double mode)
143 {
144 const auto missval1 = field1.missval;
145 const auto missval2 = field2.missval;
146 auto &array1 = field1.vec_d;
147 const auto &array2 = field2.vec_d;
148
149 const auto len = field1.size;
150 if (len != field2.size) cdo_abort("Fields have different size (%s)", __func__);
151
152 if (field1.nmiss)
153 {
154 for (size_t i = 0; i < len; i++)
155 {
156 if (DBL_IS_EQUAL(array2[i], missval2))
157 {
158 if (IS_EQUAL(mode, 1.0) || DBL_IS_EQUAL(array1[i], missval1)) array1[i] = 0.0;
159 continue;
160 }
161
162 if (!DBL_IS_EQUAL(array1[i], missval1))
163 {
164 if (IS_EQUAL(mode, 0.0) || IS_EQUAL(mode, 1.0))
165 array1[i] += 1.0;
166 else if (DBL_IS_EQUAL(array2[i], mode) || array2[i] > mode)
167 array1[i] += array2[i];
168 }
169 else
170 {
171 if (IS_EQUAL(mode, 0.0) || IS_EQUAL(mode, 1.0))
172 array1[i] = 1.0;
173 else if (array2[i] < mode)
174 array1[i] = 0.0;
175 else
176 array1[i] = array2[i];
177 }
178 }
179
180 field1.nmiss = field_num_miss(field1);
181 }
182 else
183 {
184 for (size_t i = 0; i < len; i++)
185 {
186 if (DBL_IS_EQUAL(array2[i], missval2))
187 {
188 if (IS_EQUAL(mode, 1.0)) array1[i] = 0.0;
189 continue;
190 }
191
192 if (IS_EQUAL(mode, 0.0) || IS_EQUAL(mode, 1.0))
193 array1[i] += 1.0;
194 else if (DBL_IS_EQUAL(array2[i], mode) || array2[i] > mode)
195 array1[i] += array2[i];
196 }
197 }
198 }
199
200 /**
201 * Selects all field elements that compare to the corresponding
202 * element of a reference field. The result of the operation is
203 * computed according to the following rules:
204 *
205 * field1 field2 result
206 * a b comp(a, b) ? a : miss
207 * a miss miss
208 * miss b miss
209 * miss miss miss
210 *
211 * @param field1 the input field, also holds the result
212 * @param field2 the reference field
213 * @param compare the comparator
214 */
215 static void
selcomp(Field & field1,const Field & field2,int (* compare)(double,double))216 selcomp(Field &field1, const Field &field2, int (*compare)(double, double))
217 {
218 const auto missval1 = field1.missval;
219 const auto missval2 = field2.missval;
220 auto &array1 = field1.vec_d;
221 const auto &array2 = field2.vec_d;
222
223 const auto len = field1.size;
224 if (len != field2.size) cdo_abort("Fields have different size (%s)", __func__);
225
226 if (field1.nmiss || field2.nmiss)
227 {
228 for (size_t i = 0; i < len; i++)
229 if (DBL_IS_EQUAL(array1[i], missval1) || DBL_IS_EQUAL(array2[i], missval2) || !compare(array1[i], array2[i]))
230 array1[i] = missval1;
231 }
232 else
233 {
234 for (size_t i = 0; i < len; i++)
235 if (!compare(array1[i], array2[i])) array1[i] = missval1;
236 }
237
238 field1.nmiss = field_num_miss(field1);
239 }
240
241 /**
242 * Selects all field elements that compare to a certain reference
243 * value. The result of the operation is computed according to the
244 * following rules:
245 *
246 * field c result
247 * a c comp(a, c) ? a : miss
248 * a miss miss
249 * miss c miss
250 * miss miss miss
251 *
252 * @param field the input field, also holds the result
253 * @param c the refence value
254 * @param compare the comparator
255 */
256 static void
selcompc(Field & field,double c,int (* compare)(double,double))257 selcompc(Field &field, double c, int (*compare)(double, double))
258 {
259 const auto missval = field.missval;
260 auto &array = field.vec_d;
261
262 const auto len = field.size;
263
264 if (DBL_IS_EQUAL(c, missval))
265 {
266 for (size_t i = 0; i < len; i++) array[i] = missval;
267 }
268 else if (field.nmiss)
269 {
270 for (size_t i = 0; i < len; i++)
271 if (DBL_IS_EQUAL(array[i], missval) || !compare(array[i], c)) array[i] = missval;
272 }
273 else
274 {
275 for (size_t i = 0; i < len; i++)
276 if (!compare(array[i], c)) array[i] = missval;
277 }
278
279 field.nmiss = field_num_miss(field);
280 }
281
282 static int
le(double a,double b)283 le(double a, double b)
284 {
285 return a <= b;
286 }
287
288 static int
lt(double a,double b)289 lt(double a, double b)
290 {
291 return a < b;
292 }
293
294 static int
ge(double a,double b)295 ge(double a, double b)
296 {
297 return a >= b;
298 }
299
300 static int
gt(double a,double b)301 gt(double a, double b)
302 {
303 return a > b;
304 }
305
306 static int
eq(double a,double b)307 eq(double a, double b)
308 {
309 return DBL_IS_EQUAL(a, b);
310 }
311
312 static int
ne(double a,double b)313 ne(double a, double b)
314 {
315 return !DBL_IS_EQUAL(a, b);
316 }
317
318 void
vfarnum(Field & field1,const Field & field2)319 vfarnum(Field &field1, const Field &field2)
320 {
321 count(field1, field2, 0.0);
322 }
323
324 void
vfarnum2(Field & field1,const Field & field2)325 vfarnum2(Field &field1, const Field &field2)
326 {
327 count(field1, field2, 1.0);
328 }
329
330 void
vfarnum3(Field & field1,const Field & field2,double n)331 vfarnum3(Field &field1, const Field &field2, double n)
332 {
333 count(field1, field2, n);
334 }
335
336 void
vfarsel(Field & field1,const Field & field2)337 vfarsel(Field &field1, const Field &field2)
338 {
339 const auto missval1 = field1.missval;
340 const auto missval2 = field2.missval;
341 auto &array1 = field1.vec_d;
342 const auto &array2 = field2.vec_d;
343
344 const auto len = field1.size;
345 if (len != field2.size) cdo_abort("Fields have different gridsize (%s)", __func__);
346
347 if (field2.nmiss)
348 {
349 for (size_t i = 0; i < len; i++)
350 if (DBL_IS_EQUAL(array2[i], missval2) || DBL_IS_EQUAL(array2[i], 0.0)) array1[i] = missval1;
351 }
352 else
353 {
354 for (size_t i = 0; i < len; i++)
355 if (IS_EQUAL(array2[i], 0.0)) array1[i] = missval1;
356 }
357
358 field1.nmiss = field_num_miss(field1);
359 }
360
361 void
vfarselle(Field & field1,const Field & field2)362 vfarselle(Field &field1, const Field &field2)
363 {
364 selcomp(field1, field2, le);
365 }
366
367 void
vfarsellt(Field & field1,const Field & field2)368 vfarsellt(Field &field1, const Field &field2)
369 {
370 selcomp(field1, field2, lt);
371 }
372
373 void
vfarselge(Field & field1,const Field & field2)374 vfarselge(Field &field1, const Field &field2)
375 {
376 selcomp(field1, field2, ge);
377 }
378
379 void
vfarselgt(Field & field1,const Field & field2)380 vfarselgt(Field &field1, const Field &field2)
381 {
382 selcomp(field1, field2, gt);
383 }
384
385 void
vfarseleq(Field & field1,const Field & field2)386 vfarseleq(Field &field1, const Field &field2)
387 {
388 selcomp(field1, field2, eq);
389 }
390
391 void
vfarselne(Field & field1,const Field & field2)392 vfarselne(Field &field1, const Field &field2)
393 {
394 selcomp(field1, field2, ne);
395 }
396
397 void
vfarsellec(Field & field,double c)398 vfarsellec(Field &field, double c)
399 {
400 selcompc(field, c, le);
401 }
402
403 void
vfarselltc(Field & field,double c)404 vfarselltc(Field &field, double c)
405 {
406 selcompc(field, c, lt);
407 }
408
409 void
vfarselgec(Field & field,double c)410 vfarselgec(Field &field, double c)
411 {
412 selcompc(field, c, ge);
413 }
414
415 void
vfarseleqc(Field & field,double c)416 vfarseleqc(Field &field, double c)
417 {
418 selcompc(field, c, eq);
419 }
420
421 void
vfarselnec(Field & field,double c)422 vfarselnec(Field &field, double c)
423 {
424 selcompc(field, c, ne);
425 }
426
427 void
vfarselgtc(Field & field,double c)428 vfarselgtc(Field &field, double c)
429 {
430 selcompc(field, c, gt);
431 }
432
433 void
update_hist(FieldVector2D & field,int nlevels,size_t gridsize,const std::vector<double> & yvals,bool onlyNorth)434 update_hist(FieldVector2D &field, int nlevels, size_t gridsize, const std::vector<double> &yvals, bool onlyNorth)
435 {
436 for (int levelID = 0; levelID < nlevels; levelID++)
437 for (size_t i = 0; i < gridsize; i++)
438 if (onlyNorth)
439 {
440 if (yvals[i] >= 0.0) field[1][levelID].vec_d[i] = field[0][levelID].vec_d[i];
441 }
442 else
443 field[1][levelID].vec_d[i] = field[0][levelID].vec_d[i];
444 }
445
446 void
define_mid_of_time(int frequency,int taxisID,int year,int month,int MaxMonths)447 define_mid_of_time(int frequency, int taxisID, int year, int month, int MaxMonths)
448 {
449 int64_t vdate, vdateb, vdatebp1;
450 int vtime;
451 const auto vtime0 = cdiEncodeTime(0, 0, 0);
452
453 const auto calendar = taxisInqCalendar(taxisID);
454
455 if (frequency == 8)
456 {
457 vdateb = cdiEncodeDate(year, month, 1);
458
459 int boundmonth = (month + 1 <= MaxMonths) ? month + 1 : 1;
460 int boundyear = (boundmonth != 1) ? year : year + 1;
461 vdatebp1 = cdiEncodeDate(boundyear, boundmonth, 1);
462 }
463 else
464 {
465 vdateb = cdiEncodeDate(year, 1, 1);
466 vdatebp1 = cdiEncodeDate(year + 1, 1, 1);
467 }
468
469 const auto juldate1 = juldate_encode(calendar, vdateb, vtime0);
470 const auto juldate2 = juldate_encode(calendar, vdatebp1, vtime0);
471
472 const auto seconds = juldate_to_seconds(juldate_sub(juldate2, juldate1)) / 2;
473 const auto juldatem = juldate_add_seconds(lround(seconds), juldate1);
474 juldate_decode(calendar, juldatem, vdate, vtime);
475
476 taxisDefVdate(taxisID, vdate);
477 taxisDefVtime(taxisID, vtime);
478 }
479
480 void
adjust_end_date(int nlevels,size_t gridsize,const std::vector<double> & yvals,double missval,int64_t ovdate,const FieldVector2D & startDateWithHist,FieldVector2D & endDateWithHist)481 adjust_end_date(int nlevels, size_t gridsize, const std::vector<double> &yvals, double missval, int64_t ovdate,
482 const FieldVector2D &startDateWithHist, FieldVector2D &endDateWithHist)
483 {
484 const auto ovdateSouth = std::min(cdiEncodeDate(ovdate / 10000, 6, 30), ovdate);
485
486 for (int levelID = 0; levelID < nlevels; levelID++)
487 {
488 for (size_t i = 0; i < gridsize; i++)
489 {
490 // start with southern sphere
491 if (yvals[i] < 0)
492 {
493 if (DBL_IS_EQUAL(startDateWithHist[1][levelID].vec_d[i], missval))
494 {
495 endDateWithHist[0][levelID].vec_d[i] = missval;
496 continue;
497 }
498 if (DBL_IS_EQUAL(endDateWithHist[0][levelID].vec_d[i], missval))
499 {
500 endDateWithHist[0][levelID].vec_d[i] = ovdateSouth;
501 }
502 }
503 else
504 {
505 if (DBL_IS_EQUAL(startDateWithHist[0][levelID].vec_d[i], missval))
506 {
507 endDateWithHist[0][levelID].vec_d[i] = missval;
508 continue;
509 }
510
511 if (DBL_IS_EQUAL(endDateWithHist[0][levelID].vec_d[i], missval))
512 {
513 endDateWithHist[0][levelID].vec_d[i] = ovdate;
514 }
515 }
516 }
517 }
518 }
519
520 void
compute_gsl(int nlevels,size_t gridsize,std::vector<double> & yvals,double missval,FieldVector2D & startDateWithHist,FieldVector2D & endDateWithHist,FieldVector & gslDuration,FieldVector & gslFirstDay,bool useCurrentYear)521 compute_gsl(int nlevels, size_t gridsize, std::vector<double> &yvals, double missval, FieldVector2D &startDateWithHist,
522 FieldVector2D &endDateWithHist, FieldVector &gslDuration, FieldVector &gslFirstDay, bool useCurrentYear)
523 {
524 double firstDay, duration;
525
526 if (!useCurrentYear)
527 {
528 for (int levelID = 0; levelID < nlevels; levelID++)
529 {
530 for (size_t i = 0; i < gridsize; i++)
531 {
532 // start with southern sphere
533 if (yvals[i] < 0.0)
534 {
535 duration = (double) (date_to_julday(CALENDAR_PROLEPTIC, (int64_t) endDateWithHist[0][levelID].vec_d[i])
536 - date_to_julday(CALENDAR_PROLEPTIC, (int64_t) startDateWithHist[1][levelID].vec_d[i]));
537 }
538 else
539 {
540 duration = (double) (date_to_julday(CALENDAR_PROLEPTIC, (int64_t) endDateWithHist[1][levelID].vec_d[i])
541 - date_to_julday(CALENDAR_PROLEPTIC, (int64_t) startDateWithHist[1][levelID].vec_d[i]));
542 }
543
544 if (DBL_IS_EQUAL(startDateWithHist[1][levelID].vec_d[i], missval))
545 firstDay = missval;
546 else
547 firstDay = (double) day_of_year((int64_t) startDateWithHist[1][levelID].vec_d[i]);
548
549 gslDuration[levelID].vec_d[i] = duration;
550 gslFirstDay[levelID].vec_d[i] = firstDay;
551 }
552 }
553 }
554 else
555 {
556 // the current year can only have values for the northern hemisphere
557 for (int levelID = 0; levelID < nlevels; levelID++)
558 {
559 for (size_t i = 0; i < gridsize; i++)
560 {
561 // start with southern sphere
562 if (yvals[i] < 0.0)
563 {
564 gslDuration[levelID].vec_d[i] = missval;
565 gslFirstDay[levelID].vec_d[i] = missval;
566 }
567 else
568 {
569 duration = (double) (date_to_julday(CALENDAR_PROLEPTIC, (int64_t) endDateWithHist[0][levelID].vec_d[i])
570 - date_to_julday(CALENDAR_PROLEPTIC, (int64_t) startDateWithHist[0][levelID].vec_d[i]));
571
572 if (DBL_IS_EQUAL(startDateWithHist[0][levelID].vec_d[i], missval))
573 firstDay = missval;
574 else
575 firstDay = (double) day_of_year((int64_t) startDateWithHist[0][levelID].vec_d[i]);
576
577 gslDuration[levelID].vec_d[i] = duration;
578 gslFirstDay[levelID].vec_d[i] = firstDay;
579 }
580 }
581 }
582 }
583
584 for (int levelID = 0; levelID < nlevels; levelID++)
585 {
586 gslDuration[levelID].nmiss = field_num_miss(gslDuration[levelID]);
587 gslFirstDay[levelID].nmiss = field_num_miss(gslFirstDay[levelID]);
588 }
589 }
590
591 void
write_gsl_stream(CdoStreamID ostreamID,int otaxisID,int otsID,int ovarID1,int ovarID2,int ivlistID1,int first_var_id,FieldVector & gslDuration,FieldVector & gslFirstDay,int64_t vdate,int vtime,int nlevels)592 write_gsl_stream(CdoStreamID ostreamID, int otaxisID, int otsID, int ovarID1, int ovarID2, int ivlistID1, int first_var_id,
593 FieldVector &gslDuration, FieldVector &gslFirstDay, int64_t vdate, int vtime, int nlevels)
594 {
595 (void) ivlistID1;
596 (void) first_var_id;
597
598 taxisDefVdate(otaxisID, vdate);
599 taxisDefVtime(otaxisID, vtime);
600 cdo_def_timestep(ostreamID, otsID);
601
602 for (int levelID = 0; levelID < nlevels; levelID++)
603 {
604 cdo_def_record(ostreamID, ovarID1, levelID);
605 cdo_write_record(ostreamID, gslDuration[levelID].vec_d.data(), gslDuration[levelID].nmiss);
606 }
607
608 for (int levelID = 0; levelID < nlevels; levelID++)
609 {
610 cdo_def_record(ostreamID, ovarID2, levelID);
611 cdo_write_record(ostreamID, gslFirstDay[levelID].vec_d.data(), gslFirstDay[levelID].nmiss);
612 }
613 }
614