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