Merge branch 'develop' of github.com:ecmwf/eccodes into develop

This commit is contained in:
Shahram Najm 2024-01-16 11:45:03 +00:00
commit 0b45c664cb
29 changed files with 2355 additions and 424 deletions

View File

@ -19,6 +19,7 @@ constant million = 1000000 : hidden;
constant grib2divider = 1000000;
alias extraDimensionPresent=zero;
transient angleSubdivisions=grib2divider; # micro degrees
transient forceStepUnits = 255 : hidden;
meta gts_header gts_header() : no_copy,hidden,read_only;
meta gts_TTAAii gts_header(20,6) : no_copy,hidden,read_only;

View File

@ -0,0 +1,7 @@
# alias defaultStepUnits = one; # 1 means Hour. See code table 4.4
# template_nofail default_step_units "grib2/localConcepts/[centre:s]/default_step_units.def";
# codetable[1] stepUnits 'stepUnits.table' = defaultStepUnits : transient,dump,no_copy;
meta stepUnits optimal_step_units(forecastTime,indicatorOfUnitOfTimeRange,lengthOfTimeRange,indicatorOfUnitForTimeRange) : transient,dump;
transient startStepUnit = 255 : hidden; # 255 means MISSING. See code table 4.4
transient endStepUnit = 255 : hidden;

View File

@ -15,9 +15,7 @@ unsigned[1] numberOfRadarSitesUsed : dump;
codetable[1] indicatorOfUnitOfTimeRange ('4.4.table',masterDir,localDir) : dump;
alias indicatorOfUnitForForecastTime = indicatorOfUnitOfTimeRange;
alias defaultStepUnits = one; # 1 means Hour. See code table 4.4
template_nofail default_step_units "grib2/localConcepts/[centre:s]/default_step_units.def";
codetable[1] stepUnits 'stepUnits.table' = defaultStepUnits : transient,dump,no_copy;
include "grib2/stepUnits.def"
# Site latitude (in microdegrees)
unsigned[4] siteLatitude : dump;

View File

@ -12,9 +12,7 @@ alias minutesAfterReferenceTimeOfDataCutoff=minutesAfterDataCutoff;
codetable[1] indicatorOfUnitOfTimeRange ('4.4.table',masterDir,localDir) : dump;
alias indicatorOfUnitForForecastTime = indicatorOfUnitOfTimeRange;
alias defaultStepUnits = one; # 1 means Hour. See code table 4.4
template_nofail default_step_units "grib2/localConcepts/[centre:s]/default_step_units.def";
codetable[1] stepUnits 'stepUnits.table' = defaultStepUnits : transient,dump,no_copy;
include "grib2/stepUnits.def";
# Forecast time in units defined by previous octet (GRIB-29: supports negative forecast time)
signed[4] forecastTime : dump;

View File

@ -14,9 +14,7 @@ alias minutesAfterReferenceTimeOfDataCutoff=minutesAfterDataCutoff;
codetable[1] indicatorOfUnitOfTimeRange ('4.4.table',masterDir,localDir) : dump;
alias indicatorOfUnitForForecastTime = indicatorOfUnitOfTimeRange;
alias defaultStepUnits = one; # 1 means Hour. See code table 4.4
template_nofail default_step_units "grib2/localConcepts/[centre:s]/default_step_units.def";
codetable[1] stepUnits 'stepUnits.table' = defaultStepUnits : transient,dump,no_copy;
include "grib2/stepUnits.def";
# Forecast time in units defined by previous octet
# See GRIB-530: We have to make a special case for the error in WMO spec

View File

@ -3,9 +3,8 @@
remove is_localtime;
transient is_localtime=1;
alias defaultStepUnits = one; # 1 means Hour. See code table 4.4
template_nofail default_step_units "grib2/localConcepts/[centre:s]/default_step_units.def";
codetable[1] stepUnits 'stepUnits.table' = defaultStepUnits : transient,dump,no_copy;
include "grib2/stepUnits.def";
alias time.stepUnits = stepUnits;
# Method used to calculate the field value at the local time specified in section 1
@ -82,7 +81,6 @@ if (numberOfForecastsUsedInLocalTime == 1) {
alias mars.date = dateOfForecastUsedInLocalTime : dump;
alias mars.time = timeOfForecastUsedInLocalTime : dump;
alias mars.step = endStep;
alias time.dataDate = dateOfForecastUsedInLocalTime;
alias time.dataTime = timeOfForecastUsedInLocalTime;
alias time.endStep = endStep;

View File

@ -9,7 +9,11 @@
# nor does it submit to any jurisdiction.
#
list( APPEND eccodes_src_files
step_unit.cc
step.cc
step_utilities.cc
grib_api.h
grib_timer.cc
eccodes.h
action.cc
action_class_alias.cc
@ -158,6 +162,7 @@ list( APPEND eccodes_src_files
grib_accessor_class_signed_bits.cc
grib_accessor_class_section.cc
grib_accessor_class_step_in_units.cc
grib_accessor_class_optimal_step_units.cc
grib_accessor_class_section_length.cc
grib_accessor_class_g1_message_length.cc
grib_accessor_class_g1_section4_length.cc
@ -253,7 +258,6 @@ list( APPEND eccodes_src_files
grib_accessor_class_abstract_long_vector.cc
grib_loader_from_handle.cc
grib_bits.cc
grib_timer.cc
grib_ibmfloat.cc
grib_ieeefloat.cc
grib_accessor_class_reference_value_error.cc

View File

@ -142,6 +142,7 @@ extern grib_accessor_class* grib_accessor_class_octahedral_gaussian;
extern grib_accessor_class* grib_accessor_class_octet_number;
extern grib_accessor_class* grib_accessor_class_offset_file;
extern grib_accessor_class* grib_accessor_class_offset_values;
extern grib_accessor_class* grib_accessor_class_optimal_step_units;
extern grib_accessor_class* grib_accessor_class_pack_bufr_values;
extern grib_accessor_class* grib_accessor_class_packing_type;
extern grib_accessor_class* grib_accessor_class_pad;

View File

@ -842,7 +842,7 @@ static int pack_expression(grib_accessor* a, grib_expression* e)
grib_handle* hand = grib_handle_of_accessor(a);
if (strcmp(e->cclass->name, "long") == 0) {
grib_expression_evaluate_long(hand, e, &lval); /* TDOD: check return value */
grib_expression_evaluate_long(hand, e, &lval); /* TODO: check return value */
//if (hand->context->debug) printf("ECCODES DEBUG grib_accessor_class_codetable::pack_expression %s %ld\n", a->name,lval);
ret = grib_pack_long(a, &lval, &len);
}

View File

@ -9,6 +9,10 @@
*/
#include "grib_api_internal.h"
#include "step.h"
#include "step_utilities.h"
#include <stdexcept>
/*
This is used by make_class.pl
@ -16,9 +20,12 @@
CLASS = accessor
SUPER = grib_accessor_class_long
IMPLEMENTS = unpack_long;pack_long
IMPLEMENTS = unpack_double
IMPLEMENTS = unpack_string;pack_string
IMPLEMENTS = init;dump
MEMBERS = const char* start_step
MEMBERS = const char* unit
IMPLEMENTS = get_native_type
MEMBERS = const char* start_step_value
MEMBERS = const char* step_units
MEMBERS = const char* year
MEMBERS = const char* month
@ -34,8 +41,8 @@
MEMBERS = const char* minute_of_end_of_interval
MEMBERS = const char* second_of_end_of_interval
MEMBERS = const char* coded_unit
MEMBERS = const char* coded_time_range
MEMBERS = const char* time_range_unit
MEMBERS = const char* time_range_value
MEMBERS = const char* typeOfTimeIncrement
MEMBERS = const char* numberOfTimeRange
@ -53,8 +60,12 @@ or edit "accessor.class" and rerun ./make_class.pl
*/
static int get_native_type(grib_accessor*);
static int pack_long(grib_accessor*, const long* val, size_t* len);
static int pack_string(grib_accessor*, const char*, size_t* len);
static int unpack_double(grib_accessor*, double* val, size_t* len);
static int unpack_long(grib_accessor*, long* val, size_t* len);
static int unpack_string(grib_accessor*, char*, size_t* len);
static void dump(grib_accessor*, grib_dumper*);
static void init(grib_accessor*, const long, grib_arguments*);
@ -64,8 +75,8 @@ typedef struct grib_accessor_g2end_step
/* Members defined in gen */
/* Members defined in long */
/* Members defined in g2end_step */
const char* start_step;
const char* unit;
const char* start_step_value;
const char* step_units;
const char* year;
const char* month;
const char* day;
@ -78,8 +89,8 @@ typedef struct grib_accessor_g2end_step
const char* hour_of_end_of_interval;
const char* minute_of_end_of_interval;
const char* second_of_end_of_interval;
const char* coded_unit;
const char* coded_time_range;
const char* time_range_unit;
const char* time_range_value;
const char* typeOfTimeIncrement;
const char* numberOfTimeRange;
} grib_accessor_g2end_step;
@ -101,7 +112,7 @@ static grib_accessor_class _grib_accessor_class_g2end_step = {
0, /* get number of values */
0, /* get number of bytes */
0, /* get offset to bytes */
0, /* get native type */
&get_native_type, /* get native type */
0, /* get sub_section */
0, /* pack_missing */
0, /* is_missing */
@ -109,10 +120,10 @@ static grib_accessor_class _grib_accessor_class_g2end_step = {
&unpack_long, /* unpack_long */
0, /* pack_double */
0, /* pack_float */
0, /* unpack_double */
&unpack_double, /* unpack_double */
0, /* unpack_float */
0, /* pack_string */
0, /* unpack_string */
&pack_string, /* pack_string */
&unpack_string, /* unpack_string */
0, /* pack_string_array */
0, /* unpack_string_array */
0, /* pack_bytes */
@ -145,8 +156,8 @@ static void init(grib_accessor* a, const long l, grib_arguments* c)
int n = 0;
grib_handle* h = grib_handle_of_accessor(a);
self->start_step = grib_arguments_get_name(h, c, n++);
self->unit = grib_arguments_get_name(h, c, n++);
self->start_step_value = grib_arguments_get_name(h, c, n++);
self->step_units = grib_arguments_get_name(h, c, n++);
self->year = grib_arguments_get_name(h, c, n++);
self->month = grib_arguments_get_name(h, c, n++);
@ -162,8 +173,8 @@ static void init(grib_accessor* a, const long l, grib_arguments* c)
self->minute_of_end_of_interval = grib_arguments_get_name(h, c, n++);
self->second_of_end_of_interval = grib_arguments_get_name(h, c, n++);
self->coded_unit = grib_arguments_get_name(h, c, n++);
self->coded_time_range = grib_arguments_get_name(h, c, n++);
self->time_range_unit = grib_arguments_get_name(h, c, n++);
self->time_range_value = grib_arguments_get_name(h, c, n++);
self->typeOfTimeIncrement = grib_arguments_get_name(h, c, n++);
self->numberOfTimeRange = grib_arguments_get_name(h, c, n++);
}
@ -173,42 +184,6 @@ static void dump(grib_accessor* a, grib_dumper* dumper)
grib_dump_double(dumper, a, NULL);
}
static const int u2s2[] = {
60, /* (0) minutes */
3600, /* (1) hour */
86400, /* (2) day */
2592000, /* (3) month */
-1, /* (4) */
-1, /* (5) */
-1, /* (6) */
-1, /* (7) */
-1, /* (8) */
-1, /* (9) */
10800, /* (10) 3 hours */
21600, /* (11) 6 hours */
43200, /* (12) 12 hours */
1 /* (13) seconds */
};
static const int u2s[] = {
60, /* (0) minutes */
3600, /* (1) hour */
86400, /* (2) day */
2592000, /* (3) month */
-1, /* (4) */
-1, /* (5) */
-1, /* (6) */
-1, /* (7) */
-1, /* (8) */
-1, /* (9) */
10800, /* (10) 3 hours */
21600, /* (11) 6 hours */
43200, /* (12) 12 hours */
1, /* (13) seconds */
900, /* (14) 15 minutes */
1800 /* (15) 30 minutes */
};
// See GRIB-488
static bool is_special_expver(const grib_handle* h)
{
@ -229,66 +204,51 @@ static bool is_special_expver(const grib_handle* h)
return false;
}
static int convert_time_range(
static int convert_time_range_long_(
grib_handle* h,
long stepUnits, /* unit */
long indicatorOfUnitForTimeRange, /* coded_unit */
long* lengthOfTimeRange /* coded_time_range */
long stepUnits, /* step_units */
long indicatorOfUnitForTimeRange, /* time_range_unit */
long* lengthOfTimeRange /* time_range_value */
)
{
Assert(lengthOfTimeRange != NULL);
if (indicatorOfUnitForTimeRange != stepUnits) {
long u2sf_step_unit;
long coded_time_range_sec = (*lengthOfTimeRange) * u2s2[indicatorOfUnitForTimeRange];
if (coded_time_range_sec < 0) {
long u2sf;
int factor = 60;
if (u2s2[indicatorOfUnitForTimeRange] % factor)
return GRIB_DECODING_ERROR;
if (u2s[stepUnits] % factor)
return GRIB_DECODING_ERROR;
u2sf = u2s2[indicatorOfUnitForTimeRange] / factor;
coded_time_range_sec = (*lengthOfTimeRange) * u2sf;
u2sf_step_unit = u2s[stepUnits] / factor;
eccodes::Step time_range{*lengthOfTimeRange, indicatorOfUnitForTimeRange};
time_range.set_unit(eccodes::Unit{stepUnits});
if (time_range.value<long>() != time_range.value<double>()) {
return GRIB_DECODING_ERROR;
}
else {
u2sf_step_unit = u2s[stepUnits];
}
if (coded_time_range_sec % u2sf_step_unit != 0) {
grib_context_log(h->context, GRIB_LOG_ERROR, "unable to convert endStep in stepUnits");
return GRIB_WRONG_STEP_UNIT;
}
*lengthOfTimeRange = coded_time_range_sec / u2sf_step_unit;
*lengthOfTimeRange = time_range.value<long>();
}
return GRIB_SUCCESS;
}
static int unpack_one_time_range(grib_accessor* a, long* val, size_t* len)
static int unpack_one_time_range_long_(grib_accessor* a, long* val, size_t* len)
{
grib_accessor_g2end_step* self = (grib_accessor_g2end_step*)a;
int err = 0;
long start_step;
long unit;
long coded_unit;
long coded_time_range, typeOfTimeIncrement;
long start_step_value;
long step_units;
long time_range_unit;
long time_range_value, typeOfTimeIncrement;
int add_time_range = 1; /* whether we add lengthOfTimeRange */
grib_handle* h = grib_handle_of_accessor(a);
if ((err = grib_get_long_internal(h, self->start_step, &start_step)))
if ((err = grib_get_long_internal(h, self->start_step_value, &start_step_value)))
return err;
if ((err = grib_get_long_internal(h, self->unit, &unit)))
if ((err = grib_get_long_internal(h, self->step_units, &step_units)))
return err;
if ((err = grib_get_long_internal(h, self->coded_unit, &coded_unit)))
if ((err = grib_get_long_internal(h, self->time_range_unit, &time_range_unit)))
return err;
if ((err = grib_get_long_internal(h, self->coded_time_range, &coded_time_range)))
if ((err = grib_get_long_internal(h, self->time_range_value, &time_range_value)))
return err;
if ((err = grib_get_long_internal(h, self->typeOfTimeIncrement, &typeOfTimeIncrement)))
return err;
err = convert_time_range(h, unit, coded_unit, &coded_time_range);
err = convert_time_range_long_(h, step_units, time_range_unit, &time_range_value);
if (err != GRIB_SUCCESS)
return err;
@ -301,31 +261,79 @@ static int unpack_one_time_range(grib_accessor* a, long* val, size_t* len)
}
}
if (add_time_range) {
*val = start_step + coded_time_range;
*val = start_step_value + time_range_value;
}
else {
*val = start_step;
*val = start_step_value;
}
return GRIB_SUCCESS;
}
static int unpack_one_time_range_double_(grib_accessor* a, double *val , size_t* len)
{
grib_accessor_g2end_step* self = (grib_accessor_g2end_step*)a;
int err = 0;
double start_step_value;
long start_step_unit;
long step_units;
long time_range_unit;
double time_range_value;
long typeOfTimeIncrement;
int add_time_range = 1; /* whether we add lengthOfTimeRange */
grib_handle* h = grib_handle_of_accessor(a);
if ((err = grib_get_double_internal(h, self->start_step_value, &start_step_value)))
return err;
if ((err = grib_get_long_internal(h, "startStepUnit", &start_step_unit)))
return err;
if ((err = grib_get_long_internal(h, self->step_units, &step_units)))
return err;
if ((err = grib_get_long_internal(h, self->time_range_unit, &time_range_unit)))
return err;
if ((err = grib_get_double_internal(h, self->time_range_value, &time_range_value)))
return err;
if ((err = grib_get_long_internal(h, self->typeOfTimeIncrement, &typeOfTimeIncrement)))
return err;
eccodes::Step start_step{start_step_value, start_step_unit};
eccodes::Step time_range{time_range_value, time_range_unit};
if (typeOfTimeIncrement == 1) {
/* See GRIB-488 */
/* Note: For this case, lengthOfTimeRange is not related to step and should not be used to calculate step */
add_time_range = 0;
if (is_special_expver(h)) {
add_time_range = 1;
}
}
if (add_time_range) {
*val = (start_step + time_range).value<double>(eccodes::Unit(step_units));
}
else {
*val = start_step.value<double>(eccodes::Unit(start_step_unit));
}
return GRIB_SUCCESS;
}
#define MAX_NUM_TIME_RANGES 16 /* maximum number of time range specifications */
static int unpack_multiple_time_ranges(grib_accessor* a, long* val, size_t* len)
static int unpack_multiple_time_ranges_long_(grib_accessor* a, long* val, size_t* len)
{
grib_accessor_g2end_step* self = (grib_accessor_g2end_step*)a;
int i = 0, err = 0;
grib_handle* h = grib_handle_of_accessor(a);
long numberOfTimeRange = 0, unit = 0, start_step = 0;
long numberOfTimeRange = 0, step_units = 0, start_step_value = 0;
size_t count = 0;
long arr_typeOfTimeIncrement[MAX_NUM_TIME_RANGES] = {0,};
long arr_coded_unit[MAX_NUM_TIME_RANGES] = {0,};
long arr_coded_time_range[MAX_NUM_TIME_RANGES] = {0,};
if ((err = grib_get_long_internal(h, self->start_step, &start_step)))
if ((err = grib_get_long_internal(h, self->start_step_value, &start_step_value)))
return err;
if ((err = grib_get_long_internal(h, self->unit, &unit)))
if ((err = grib_get_long_internal(h, self->step_units, &step_units)))
return err;
if ((err = grib_get_long_internal(h, self->numberOfTimeRange, &numberOfTimeRange)))
return err;
@ -338,9 +346,9 @@ static int unpack_multiple_time_ranges(grib_accessor* a, long* val, size_t* len)
/* Get the arrays for the N time ranges */
if ((err = grib_get_long_array(h, self->typeOfTimeIncrement, arr_typeOfTimeIncrement, &count)))
return err;
if ((err = grib_get_long_array(h, self->coded_unit, arr_coded_unit, &count)))
if ((err = grib_get_long_array(h, self->time_range_unit, arr_coded_unit, &count)))
return err;
if ((err = grib_get_long_array(h, self->coded_time_range, arr_coded_time_range, &count)))
if ((err = grib_get_long_array(h, self->time_range_value, arr_coded_time_range, &count)))
return err;
/* Look in the array of typeOfTimeIncrements for first entry whose typeOfTimeIncrement == 2 */
@ -350,11 +358,71 @@ static int unpack_multiple_time_ranges(grib_accessor* a, long* val, size_t* len)
long the_coded_unit = arr_coded_unit[i];
long the_coded_time_range = arr_coded_time_range[i];
err = convert_time_range(h, unit, the_coded_unit, &the_coded_time_range);
err = convert_time_range_long_(h, step_units, the_coded_unit, &the_coded_time_range);
if (err != GRIB_SUCCESS)
return err;
*val = start_step + the_coded_time_range;
*val = start_step_value + the_coded_time_range;
return GRIB_SUCCESS;
}
}
grib_context_log(h->context, GRIB_LOG_ERROR,
"Cannot calculate endStep. No time range specification with typeOfTimeIncrement = 2");
return GRIB_DECODING_ERROR;
}
static int unpack_multiple_time_ranges_double_(grib_accessor* a, double* val, size_t* len)
{
grib_accessor_g2end_step* self = (grib_accessor_g2end_step*)a;
int i = 0, err = 0;
grib_handle* h = grib_handle_of_accessor(a);
long numberOfTimeRange = 0;
long step_units = 0;
long start_step_value = 0;
long start_step_unit = 0;
size_t count = 0;
long arr_typeOfTimeIncrement[MAX_NUM_TIME_RANGES] = {0, };
long arr_coded_unit[MAX_NUM_TIME_RANGES] = {0, };
long arr_coded_time_range[MAX_NUM_TIME_RANGES] = {0, };
if ((err = grib_get_long_internal(h, self->start_step_value, &start_step_value)))
return err;
if ((err = grib_get_long_internal(h, "startStepUnit", &start_step_unit)))
return err;
eccodes::Step start_step{start_step_value, start_step_unit};
if ((err = grib_get_long_internal(h, self->step_units, &step_units)))
return err;
if ((err = grib_get_long_internal(h, self->numberOfTimeRange, &numberOfTimeRange)))
return err;
if (numberOfTimeRange > MAX_NUM_TIME_RANGES) {
grib_context_log(h->context, GRIB_LOG_ERROR, "Too many time range specifications!");
return GRIB_DECODING_ERROR;
}
count = numberOfTimeRange;
/* Get the arrays for the N time ranges */
if ((err = grib_get_long_array(h, self->typeOfTimeIncrement, arr_typeOfTimeIncrement, &count)))
return err;
if ((err = grib_get_long_array(h, self->time_range_unit, arr_coded_unit, &count)))
return err;
if ((err = grib_get_long_array(h, self->time_range_value, arr_coded_time_range, &count)))
return err;
/* Look in the array of typeOfTimeIncrements for first entry whose typeOfTimeIncrement == 2 */
for (i = 0; i < count; i++) {
if (arr_typeOfTimeIncrement[i] == 2) {
/* Found the required time range. Get the other two keys from it */
long the_coded_unit = arr_coded_unit[i];
long the_coded_time_range = arr_coded_time_range[i];
eccodes::Step time_range{the_coded_unit, the_coded_time_range};
*val = (start_step + time_range).value<double>(eccodes::Unit(step_units));
return GRIB_SUCCESS;
}
}
@ -370,38 +438,87 @@ static int unpack_multiple_time_ranges(grib_accessor* a, long* val, size_t* len)
static int unpack_long(grib_accessor* a, long* val, size_t* len)
{
grib_accessor_g2end_step* self = (grib_accessor_g2end_step*)a;
int err = 0;
long start_step;
grib_handle* h = grib_handle_of_accessor(a);
int ret = 0;
long start_step_value;
long numberOfTimeRange;
grib_handle* h = grib_handle_of_accessor(a);
if ((err = grib_get_long_internal(h, self->start_step, &start_step)))
return err;
if ((ret = grib_get_long_internal(h, self->start_step_value, &start_step_value)))
return ret;
/* point in time */
if (self->year == NULL) {
*val = start_step;
*val = start_step_value;
return 0;
}
Assert(self->numberOfTimeRange);
if ((err = grib_get_long_internal(h, self->numberOfTimeRange, &numberOfTimeRange)))
return err;
if ((ret = grib_get_long_internal(h, self->numberOfTimeRange, &numberOfTimeRange)))
return ret;
Assert(numberOfTimeRange == 1 || numberOfTimeRange == 2);
if (numberOfTimeRange == 1) {
return unpack_one_time_range(a, val, len);
try {
if (numberOfTimeRange == 1) {
ret = unpack_one_time_range_long_(a, val, len);
return ret;
}
else {
ret = unpack_multiple_time_ranges_long_(a, val, len);
return ret;
}
}
else {
return unpack_multiple_time_ranges(a, val, len);
catch (std::exception& e) {
grib_context_log(h->context, GRIB_LOG_ERROR, "grib_accessor_g2end_step::unpack_long: %s", e.what());
return GRIB_DECODING_ERROR;
}
return GRIB_SUCCESS;
}
static int pack_long(grib_accessor* a, const long* val, size_t* len)
static int unpack_double(grib_accessor* a, double* val, size_t* len)
{
grib_accessor_g2end_step* self = (grib_accessor_g2end_step*)a;
grib_handle* h = grib_handle_of_accessor(a);
int ret = 0;
long start_step_value;
long numberOfTimeRange;
if ((ret = grib_get_long_internal(h, self->start_step_value, &start_step_value)))
return ret;
/* point in time */
if (self->year == NULL) {
*val = start_step_value;
return 0;
}
Assert(self->numberOfTimeRange);
if ((ret = grib_get_long_internal(h, self->numberOfTimeRange, &numberOfTimeRange)))
return ret;
Assert(numberOfTimeRange == 1 || numberOfTimeRange == 2);
try {
if (numberOfTimeRange == 1) {
ret = unpack_one_time_range_double_(a, val, len);
return ret;
}
else {
ret = unpack_multiple_time_ranges_double_(a, val, len);
return ret;
}
}
catch (std::exception& e) {
grib_context_log(h->context, GRIB_LOG_ERROR, "grib_accessor_g2end_step::unpack_double: %s", e.what());
return GRIB_DECODING_ERROR;
}
return GRIB_SUCCESS;
}
static int pack_long_(grib_accessor* a, const long end_step_value, const long end_step_unit)
{
grib_accessor_g2end_step* self = (grib_accessor_g2end_step*)a;
grib_handle* h = grib_handle_of_accessor(a);
int err = 0;
long year;
@ -411,28 +528,30 @@ static int pack_long(grib_accessor* a, const long* val, size_t* len)
long minute;
long second;
long start_step;
long unit, coded_unit;
long start_step_value;
long start_step_unit;
long time_range_unit;
long year_of_end_of_interval;
long month_of_end_of_interval;
long day_of_end_of_interval;
long hour_of_end_of_interval;
long minute_of_end_of_interval = 0;
long second_of_end_of_interval = 0;
long coded_time_range, time_range, typeOfTimeIncrement;
long typeOfTimeIncrement;
double dend, dstep;
int show_hours = a->context->show_hour_stepunit;
eccodes::Step end_step{end_step_value, end_step_unit};
/*point in time */
if (self->year == NULL) {
err = grib_set_long_internal(h, self->start_step, *val);
err = grib_set_long_internal(h, "startStepUnit", end_step.unit().value<long>());
err = grib_set_long_internal(h, self->start_step_value, end_step.value<long>());
return err;
}
if ((err = grib_get_long_internal(h, self->coded_unit, &coded_unit)))
return err;
if ((err = grib_get_long_internal(h, self->unit, &unit)))
if ((err = grib_get_long_internal(h, self->time_range_unit, &time_range_unit)))
return err;
if ((err = grib_get_long_internal(h, self->year, &year)))
return err;
@ -447,16 +566,30 @@ static int pack_long(grib_accessor* a, const long* val, size_t* len)
if ((err = grib_get_long_internal(h, self->second, &second)))
return err;
if ((err = grib_get_long_internal(h, self->start_step, &start_step)))
if ((err = grib_get_long_internal(h, self->start_step_value, &start_step_value)))
return err;
if ((err = grib_get_long_internal(h, "startStepUnit", &start_step_unit)))
return err;
long force_step_units;
if ((err= grib_get_long_internal(h, "forceStepUnits", &force_step_units)) != GRIB_SUCCESS)
return err;
if (eccodes::Unit{start_step_unit} == eccodes::Unit{eccodes::Unit::Value::MISSING}) {
grib_context_log(h->context, GRIB_LOG_ERROR,
"missing start step unit");
return GRIB_WRONG_STEP_UNIT;
}
if ((err = grib_get_long_internal(h, self->typeOfTimeIncrement, &typeOfTimeIncrement)))
return err;
time_range = *val - start_step;
eccodes::Step start_step{start_step_value, start_step_unit};
eccodes::Step time_range = end_step - start_step;
if (time_range < 0) {
if (time_range.value<double>() < 0) {
grib_context_log(h->context, GRIB_LOG_ERROR,
"endStep < startStep (%ld < %ld)", *val, start_step);
"endStep < startStep (%s < %s)", end_step.value<std::string>("%g", show_hours).c_str(), start_step.value<std::string>("%g", show_hours).c_str());
return GRIB_WRONG_STEP;
}
@ -464,7 +597,7 @@ static int pack_long(grib_accessor* a, const long* val, size_t* len)
if (err != GRIB_SUCCESS)
return err;
dstep = (((double)(*val)) * u2s[unit]) / u2s[2]; /* in days */
dstep = end_step.value<double>(eccodes::Unit{eccodes::Unit::Value::DAY});
dend += dstep;
err = grib_julian_to_datetime(dend, &year_of_end_of_interval, &month_of_end_of_interval,
@ -486,21 +619,130 @@ static int pack_long(grib_accessor* a, const long* val, size_t* len)
if ((err = grib_set_long_internal(h, self->second_of_end_of_interval, second_of_end_of_interval)))
return err;
if (time_range * u2s[unit] % u2s2[coded_unit]) {
coded_unit = unit;
if ((err = grib_set_long_internal(h, self->coded_unit, coded_unit)))
return err;
coded_time_range = time_range;
const char* forecast_time_value_key = "forecastTime";
const char* forecast_time_unit_key = "indicatorOfUnitOfTimeRange";
eccodes::Step forecast_time_opt;
eccodes::Step time_range_opt;
if (eccodes::Unit{force_step_units} == eccodes::Unit{eccodes::Unit::Value::MISSING}) {
std::tie(forecast_time_opt, time_range_opt) = find_common_units(start_step.optimize_unit(), time_range.optimize_unit());
}
else {
forecast_time_opt = eccodes::Step{start_step.value<long>(eccodes::Unit{force_step_units}), eccodes::Unit{force_step_units}};
time_range_opt = eccodes::Step{time_range.value<long>(eccodes::Unit{force_step_units}), eccodes::Unit{force_step_units}};
}
else
coded_time_range = (time_range * u2s[unit]) / u2s2[coded_unit];
if (typeOfTimeIncrement != 1) {
/* 1 means "Successive times processed have same forecast time, start time of forecast is incremented" */
/* Note: For this case, length of timeRange is not related to step and so should NOT be used to calculate step */
if ((err = grib_set_long_internal(h, self->coded_time_range, coded_time_range)))
return err;
if ((err = grib_set_long_internal(grib_handle_of_accessor(a), self->time_range_value, time_range_opt.value<long>())) != GRIB_SUCCESS)
return err;
if ((err = grib_set_long_internal(grib_handle_of_accessor(a), self->time_range_unit, time_range_opt.unit().value<long>())) != GRIB_SUCCESS)
return err;
if ((err = grib_set_long_internal(grib_handle_of_accessor(a), forecast_time_value_key, forecast_time_opt.value<long>())) != GRIB_SUCCESS)
return err;
if ((err = grib_set_long_internal(grib_handle_of_accessor(a), forecast_time_unit_key, forecast_time_opt.unit().value<long>())) != GRIB_SUCCESS)
return err;
return GRIB_SUCCESS;
}
static int unpack_string(grib_accessor* a, char* val, size_t* len)
{
grib_accessor_g2end_step* self = (grib_accessor_g2end_step*)a;
grib_handle* h = grib_handle_of_accessor(a);
int ret = 0;
char fp_format[128] = "%g";
size_t fp_format_len = sizeof(fp_format);
size_t step_len = 0;
long step_value;
long step_units;
int show_hours = a->context->show_hour_stepunit;
if ((ret = unpack_long(a, &step_value, &step_len)) != GRIB_SUCCESS)
return ret;
if ((ret = grib_get_long_internal(h, self->step_units, &step_units)) != GRIB_SUCCESS)
return ret;
if ((ret = grib_get_string(h, "formatForDoubles", fp_format, &fp_format_len)) != GRIB_SUCCESS)
return ret;
try {
eccodes::Step step(step_value, step_units);
step.set_unit(step_units);
std::stringstream ss;
ss << step.value<std::string>(fp_format, show_hours);
size_t size = ss.str().size() + 1;
if (*len < size)
return GRIB_ARRAY_TOO_SMALL;
*len = size;
memcpy(val, ss.str().c_str(), size);
}
catch (std::exception& e) {
grib_context_log(h->context, GRIB_LOG_ERROR, "grib_accessor_g2end_step::unpack_string: %s", e.what());
return GRIB_DECODING_ERROR;
}
return GRIB_SUCCESS;
}
static int pack_long(grib_accessor* a, const long* val, size_t* len)
{
grib_handle* h = grib_handle_of_accessor(a);
int ret;
long force_step_units;
if ((ret = grib_get_long_internal(h, "forceStepUnits", &force_step_units)) != GRIB_SUCCESS)
return ret;
try {
long end_step_unit;
if (eccodes::Unit{force_step_units} == eccodes::Unit{eccodes::Unit::Value::MISSING}) {
if ((ret = grib_get_long_internal(h, "endStepUnit", &end_step_unit)) != GRIB_SUCCESS)
return ret;
if (eccodes::Unit{end_step_unit} == eccodes::Unit{eccodes::Unit::Value::MISSING})
end_step_unit = eccodes::Unit{eccodes::Unit::Value::HOUR}.value<long>();
}
else {
end_step_unit = force_step_units;
}
ret = pack_long_(a, *val, end_step_unit);
}
catch (std::exception& e) {
grib_context_log(h->context, GRIB_LOG_ERROR, "grib_accessor_g2end_step::pack_long: %s", e.what());
return GRIB_DECODING_ERROR;
}
return ret;
}
static int pack_string(grib_accessor* a, const char* val, size_t* len)
{
grib_handle* h = grib_handle_of_accessor(a);
int ret = 0;
long force_step_units;
if ((ret = grib_get_long_internal(h, "forceStepUnits", &force_step_units)) != GRIB_SUCCESS)
return ret;
try {
eccodes::Step end_step = step_from_string(val, eccodes::Unit{force_step_units});
end_step.optimize_unit();
if ((ret = grib_set_long_internal(h, "endStepUnit", end_step.unit().value<long>())) != GRIB_SUCCESS)
return ret;
if ((ret = pack_long_(a, end_step.value<long>(), end_step.unit().value<long>())) != GRIB_SUCCESS)
return ret;
}
catch (std::exception& e) {
grib_context_log(h->context, GRIB_LOG_ERROR, "grib_accessor_g2end_step::pack_string: %s", e.what());
return GRIB_DECODING_ERROR;
}
return GRIB_SUCCESS;
}
static int get_native_type(grib_accessor* a)
{
return GRIB_TYPE_STRING;
}

View File

@ -8,11 +8,12 @@
* virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
*/
/*********************************************
* Enrico Fucile
*******************************************/
#include "grib_api_internal.h"
#include "step.h"
#include "step_utilities.h"
#include <vector>
#include <sstream>
#include <iostream>
/*
This is used by make_class.pl
@ -20,11 +21,13 @@
CLASS = accessor
SUPER = grib_accessor_class_gen
IMPLEMENTS = pack_string;unpack_string;value_count
IMPLEMENTS = pack_long;unpack_long;dump
IMPLEMENTS = unpack_long;pack_long
IMPLEMENTS = unpack_double
IMPLEMENTS = unpack_string;pack_string
IMPLEMENTS = get_native_type;string_length
IMPLEMENTS = init
MEMBERS = const char* startStep
MEMBERS = const char* endStep
MEMBERS = const char* start_step
MEMBERS = const char* end_step
END_CLASS_DEF
*/
@ -42,11 +45,11 @@ or edit "accessor.class" and rerun ./make_class.pl
static int get_native_type(grib_accessor*);
static int pack_long(grib_accessor*, const long* val, size_t* len);
static int pack_string(grib_accessor*, const char*, size_t* len);
static int unpack_double(grib_accessor*, double* val, size_t* len);
static int unpack_long(grib_accessor*, long* val, size_t* len);
static int unpack_string(grib_accessor*, char*, size_t* len);
static size_t string_length(grib_accessor*);
static int value_count(grib_accessor*, long*);
static void dump(grib_accessor*, grib_dumper*);
static void init(grib_accessor*, const long, grib_arguments*);
typedef struct grib_accessor_g2step_range
@ -54,8 +57,8 @@ typedef struct grib_accessor_g2step_range
grib_accessor att;
/* Members defined in gen */
/* Members defined in g2step_range */
const char* startStep;
const char* endStep;
const char* start_step;
const char* end_step;
} grib_accessor_g2step_range;
extern grib_accessor_class* grib_accessor_class_gen;
@ -69,7 +72,7 @@ static grib_accessor_class _grib_accessor_class_g2step_range = {
&init, /* init */
0, /* post_init */
0, /* destroy */
&dump, /* dump */
0, /* dump */
0, /* next_offset */
&string_length, /* get length of string */
&value_count, /* get number of values */
@ -83,7 +86,7 @@ static grib_accessor_class _grib_accessor_class_g2step_range = {
&unpack_long, /* unpack_long */
0, /* pack_double */
0, /* pack_float */
0, /* unpack_double */
&unpack_double, /* unpack_double */
0, /* unpack_float */
&pack_string, /* pack_string */
&unpack_string, /* unpack_string */
@ -119,83 +122,141 @@ static void init(grib_accessor* a, const long l, grib_arguments* c)
int n = 0;
self->startStep = grib_arguments_get_name(grib_handle_of_accessor(a), c, n++);
self->endStep = grib_arguments_get_name(grib_handle_of_accessor(a), c, n++);
self->start_step = grib_arguments_get_name(grib_handle_of_accessor(a), c, n++);
self->end_step = grib_arguments_get_name(grib_handle_of_accessor(a), c, n++);
a->length = 0;
}
static void dump(grib_accessor* a, grib_dumper* dumper)
{
grib_dump_string(dumper, a, NULL);
}
//static void dump(grib_accessor* a, grib_dumper* dumper)
//{
//grib_dump_string(dumper, a, NULL);
//}
static int unpack_string(grib_accessor* a, char* val, size_t* len)
{
grib_accessor_g2step_range* self = (grib_accessor_g2step_range*)a;
grib_handle* h = grib_handle_of_accessor(a);
char buf[100];
int ret = 0;
size_t size = 0;
long start = 0, theEnd = 0;
double start_step_value = 0;
double end_step_value = 0;
long step_units;
ret = grib_get_long_internal(h, self->startStep, &start);
if (ret)
int show_hours = a->context->show_hour_stepunit;
if ((ret = grib_get_double_internal(h, self->start_step, &start_step_value)) != GRIB_SUCCESS)
return ret;
if ((ret= grib_get_long_internal(h, "stepUnits", &step_units)) != GRIB_SUCCESS)
return ret;
try {
if (eccodes::Unit{step_units} == eccodes::Unit{eccodes::Unit::Value::MISSING}) {
if ((ret = grib_get_long_internal(h, "stepUnits", &step_units)) != GRIB_SUCCESS)
return ret;
}
if (self->endStep == NULL) {
snprintf(buf, sizeof(buf), "%ld", start);
}
else {
ret = grib_get_long_internal(h, self->endStep, &theEnd);
if (ret)
char fp_format[128] = "%g";
size_t fp_format_len = sizeof(fp_format);
if ((ret = grib_get_string_internal(h, "formatForDoubles", fp_format, &fp_format_len)) != GRIB_SUCCESS)
return ret;
std::stringstream ss;
if (start == theEnd) {
snprintf(buf, sizeof(buf), "%ld", theEnd);
eccodes::Step start_step{start_step_value, step_units};
if (self->end_step == NULL) {
ss << start_step.value<std::string>(fp_format, show_hours);
}
else {
snprintf(buf, sizeof(buf), "%ld-%ld", start, theEnd);
if ((ret = grib_get_double_internal(h, self->end_step, &end_step_value)) != GRIB_SUCCESS)
return ret;
eccodes::Step end_step{end_step_value, step_units};
if (start_step_value == end_step_value) {
ss << end_step.value<std::string>(fp_format, show_hours);
}
else {
ss << start_step.value<std::string>(fp_format, show_hours) << "-" << end_step.value<std::string>(fp_format, show_hours);
}
}
size = ss.str().size() + 1;
if (*len < size)
return GRIB_ARRAY_TOO_SMALL;
*len = size;
memcpy(val, ss.str().c_str(), size);
}
catch (std::exception& e) {
grib_context_log(a->context, GRIB_LOG_ERROR, "grib_accessor_g2step_range::unpack_string: %s", e.what());
return GRIB_DECODING_ERROR;
}
size = strlen(buf) + 1;
if (*len < size)
return GRIB_ARRAY_TOO_SMALL;
*len = size;
memcpy(val, buf, size);
return GRIB_SUCCESS;
}
// Step range format: <start_step>[-<end_step>]
// <start_step> and <end_step> can be in different units
// stepRange="X" in instantaneous field is equivalent to set step=X
// stepRange="X" in accumulated field is equivalent to startStep=X, endStep=startStep
static int pack_string(grib_accessor* a, const char* val, size_t* len)
{
grib_accessor_g2step_range* self = (grib_accessor_g2step_range*)a;
grib_handle* h = grib_handle_of_accessor(a);
long start = 0, theEnd = -1;
int ret = 0;
char *p = NULL, *q = NULL;
start = strtol(val, &p, 10);
theEnd = start;
if (*p != 0)
theEnd = strtol(++p, &q, 10);
ret = grib_set_long_internal(h, self->startStep, start);
if (ret)
long force_step_units;
if ((ret = grib_get_long_internal(h, "forceStepUnits", &force_step_units)) != GRIB_SUCCESS)
return ret;
if (self->endStep != NULL) {
ret = grib_set_long_internal(h, self->endStep, theEnd);
if (ret)
return ret;
}
try {
std::vector<eccodes::Step> steps = parse_range(val, eccodes::Unit{force_step_units});
if (steps.size() == 0) {
grib_context_log(a->context, GRIB_LOG_ERROR, "Could not parse step range: %s", val);
return GRIB_INVALID_ARGUMENT;
}
return 0;
eccodes::Step step_0;
eccodes::Step step_1;
if (eccodes::Unit{force_step_units} == eccodes::Unit{eccodes::Unit::Value::MISSING}) {
if (steps.size() > 1)
std::tie(step_0, step_1) = find_common_units(steps[0].optimize_unit(), steps[1].optimize_unit());
else
step_0 = steps[0].optimize_unit();
}
else {
step_0 = eccodes::Step{steps[0].value<long>(eccodes::Unit{force_step_units}), eccodes::Unit{force_step_units}};
if (steps.size() > 1) {
step_1 = eccodes::Step{steps[1].value<long>(eccodes::Unit{force_step_units}), eccodes::Unit{force_step_units}};
}
}
if ((ret = grib_set_long_internal(h, "startStepUnit", step_0.unit().value<long>())))
return ret;
if ((ret = set_step(h, "forecastTime" , "indicatorOfUnitOfTimeRange", step_0)) != GRIB_SUCCESS)
return ret;
if (self->end_step != NULL) {
if (steps.size() > 1) {
if ((ret = grib_set_long_internal(h, "endStepUnit", step_1.unit().value<long>())))
return ret;
if ((ret = grib_set_long_internal(h, self->end_step, step_1.value<long>())))
return ret;
} else {
if ((ret = grib_set_long_internal(h, "endStepUnit", step_0.unit().value<long>())))
return ret;
if ((ret = grib_set_long_internal(h, self->end_step, step_0.value<long>())))
return ret;
}
}
}
catch (std::exception& e) {
grib_context_log(a->context, GRIB_LOG_ERROR, "grib_accessor_class_g2step_range::pack_string: %s", e.what());
return GRIB_INVALID_ARGUMENT;
}
return GRIB_SUCCESS;
}
static int value_count(grib_accessor* a, long* count)
@ -218,29 +279,86 @@ static int pack_long(grib_accessor* a, const long* val, size_t* len)
return pack_string(a, buff, &bufflen);
}
static int unpack_long(grib_accessor* a, long* val, size_t* len)
{
char buff[100];
size_t bufflen = 100;
long start, theEnd;
char* p = buff;
char* q = NULL;
int err = 0;
grib_accessor_g2step_range* self = (grib_accessor_g2step_range*)a;
grib_handle* h = grib_handle_of_accessor(a);
int ret = 0;
long end_start_value = 0;
long end_step_value = 0;
long step_units = 0;
if ((ret = grib_get_long_internal(h, self->start_step, &end_start_value)) != GRIB_SUCCESS)
return ret;
try {
if ((ret = grib_get_long_internal(h, "stepUnits", &step_units)) != GRIB_SUCCESS)
throw std::runtime_error("Failed to get stepUnits");
if (eccodes::Unit{step_units} == eccodes::Unit{eccodes::Unit::Value::MISSING}) {
if ((ret = grib_get_long_internal(h, "stepUnits", &step_units)) != GRIB_SUCCESS)
return ret;
}
if ((err = unpack_string(a, buff, &bufflen)) != GRIB_SUCCESS)
return err;
eccodes::Step start_step{end_start_value, step_units};
if (self->end_step == NULL) {
*val = start_step.value<long>();
}
else {
if ((ret = grib_get_long_internal(h, self->end_step, &end_step_value)) != GRIB_SUCCESS)
return ret;
eccodes::Step end_step{end_step_value, step_units};
*val = end_step.value<long>();
}
}
catch (std::exception& e) {
grib_context_log(a->context, GRIB_LOG_ERROR, "Failed to unpack step range: %s", e.what());
return GRIB_DECODING_ERROR;
}
start = strtol(buff, &p, 10);
theEnd = start;
if (*p != 0)
theEnd = strtol(++p, &q, 10);
*val = theEnd;
return 0;
return GRIB_SUCCESS;
}
static int unpack_double(grib_accessor* a, double* val, size_t* len)
{
grib_accessor_g2step_range* self = (grib_accessor_g2step_range*)a;
grib_handle* h = grib_handle_of_accessor(a);
int ret = 0;
double end_start_value = 0;
double end_step_value = 0;
long step_units = 0;
if ((ret = grib_get_double_internal(h, self->start_step, &end_start_value)) != GRIB_SUCCESS)
return ret;
if ((ret = grib_get_long_internal(h, "stepUnits", &step_units)) != GRIB_SUCCESS)
throw std::runtime_error("Failed to get stepUnits");
try {
if (eccodes::Unit{step_units} == eccodes::Unit{eccodes::Unit::Value::MISSING}) {
if ((ret = grib_get_long_internal(h, "stepUnits", &step_units)) != GRIB_SUCCESS)
return ret;
}
eccodes::Step start_step{end_start_value, step_units};
if (self->end_step == NULL) {
*val = start_step.value<long>();
}
else {
if ((ret = grib_get_double_internal(h, self->end_step, &end_step_value)) != GRIB_SUCCESS)
return ret;
eccodes::Step end_step{end_step_value, step_units};
*val = end_step.value<double>();
}
}
catch (std::exception& e) {
grib_context_log(a->context, GRIB_LOG_ERROR, "grid_accessor_g2step_range::unpack_double: %s", e.what());
return GRIB_DECODING_ERROR;
}
return GRIB_SUCCESS;
}
static int get_native_type(grib_accessor* a)
{
return GRIB_TYPE_STRING;

View File

@ -0,0 +1,285 @@
/*
* (C) Copyright 2005- ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
*
* In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
* virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
*/
#include "grib_api_internal.h"
#include "step.h"
#include "step_utilities.h"
/*
This is used by make_class.pl
START_CLASS_DEF
CLASS = accessor
SUPER = grib_accessor_class_gen
IMPLEMENTS = pack_long;unpack_long;dump
IMPLEMENTS = pack_string;unpack_string;dump
IMPLEMENTS = string_length;pack_expression;get_native_type;is_missing
IMPLEMENTS = init
MEMBERS = const char* forecast_time_value
MEMBERS = const char* forecast_time_unit
MEMBERS = const char* time_range_value
MEMBERS = const char* time_range_unit
END_CLASS_DEF
*/
/* START_CLASS_IMP */
/*
Don't edit anything between START_CLASS_IMP and END_CLASS_IMP
Instead edit values between START_CLASS_DEF and END_CLASS_DEF
or edit "accessor.class" and rerun ./make_class.pl
*/
static int get_native_type(grib_accessor*);
static int is_missing(grib_accessor*);
static int pack_long(grib_accessor*, const long* val, size_t* len);
static int pack_string(grib_accessor*, const char*, size_t* len);
static int pack_expression(grib_accessor*, grib_expression*);
static int unpack_long(grib_accessor*, long* val, size_t* len);
static int unpack_string(grib_accessor*, char*, size_t* len);
static size_t string_length(grib_accessor*);
static void dump(grib_accessor*, grib_dumper*);
static void init(grib_accessor*, const long, grib_arguments*);
typedef struct grib_accessor_optimal_step_units
{
grib_accessor att;
/* Members defined in gen */
/* Members defined in optimal_step_units */
const char* forecast_time_value;
const char* forecast_time_unit;
const char* time_range_value;
const char* time_range_unit;
} grib_accessor_optimal_step_units;
extern grib_accessor_class* grib_accessor_class_gen;
static grib_accessor_class _grib_accessor_class_optimal_step_units = {
&grib_accessor_class_gen, /* super */
"optimal_step_units", /* name */
sizeof(grib_accessor_optimal_step_units), /* size */
0, /* inited */
0, /* init_class */
&init, /* init */
0, /* post_init */
0, /* destroy */
&dump, /* dump */
0, /* next_offset */
&string_length, /* get length of string */
0, /* get number of values */
0, /* get number of bytes */
0, /* get offset to bytes */
&get_native_type, /* get native type */
0, /* get sub_section */
0, /* pack_missing */
&is_missing, /* is_missing */
&pack_long, /* pack_long */
&unpack_long, /* unpack_long */
0, /* pack_double */
0, /* pack_float */
0, /* unpack_double */
0, /* unpack_float */
&pack_string, /* pack_string */
&unpack_string, /* unpack_string */
0, /* pack_string_array */
0, /* unpack_string_array */
0, /* pack_bytes */
0, /* unpack_bytes */
&pack_expression, /* pack_expression */
0, /* notify_change */
0, /* update_size */
0, /* preferred_size */
0, /* resize */
0, /* nearest_smaller_value */
0, /* next accessor */
0, /* compare vs. another accessor */
0, /* unpack only ith value (double) */
0, /* unpack only ith value (float) */
0, /* unpack a given set of elements (double) */
0, /* unpack a given set of elements (float) */
0, /* unpack a subarray */
0, /* clear */
0, /* clone accessor */
};
grib_accessor_class* grib_accessor_class_optimal_step_units = &_grib_accessor_class_optimal_step_units;
/* END_CLASS_IMP */
static void init(grib_accessor* a, const long l, grib_arguments* c)
{
grib_accessor_optimal_step_units* self = (grib_accessor_optimal_step_units*)a;
int n = 0;
self->forecast_time_value = grib_arguments_get_name(grib_handle_of_accessor(a), c, n++);
self->forecast_time_unit = grib_arguments_get_name(grib_handle_of_accessor(a), c, n++);
self->time_range_value = grib_arguments_get_name(grib_handle_of_accessor(a), c, n++);
self->time_range_unit= grib_arguments_get_name(grib_handle_of_accessor(a), c, n++);
a->length = 0;
}
static void dump(grib_accessor* a, grib_dumper* dumper)
{
grib_dump_string(dumper, a, NULL);
}
static size_t string_length(grib_accessor* a)
{
return 255;
}
static int pack_expression(grib_accessor* a, grib_expression* e)
{
const char* cval = NULL;
int ret = 0;
long lval = 0;
size_t len = 1;
grib_handle* hand = grib_handle_of_accessor(a);
const char* cclass_name = a->cclass->name;
if (strcmp(e->cclass->name, "long") == 0) {
grib_expression_evaluate_long(hand, e, &lval); /* TODO: check return value */
ret = grib_pack_long(a, &lval, &len);
}
else {
char tmp[1024];
len = sizeof(tmp);
cval = grib_expression_evaluate_string(hand, e, tmp, &len, &ret);
if (ret != GRIB_SUCCESS) {
grib_context_log(a->context, GRIB_LOG_ERROR,
"%s.%s: Unable to evaluate string %s to be set in %s",
cclass_name, __func__, grib_expression_get_name(e), a->name);
return ret;
}
len = strlen(cval) + 1;
//if (hand->context->debug)
// printf("ECCODES DEBUG grib_accessor_class_codetable::pack_expression %s %s\n", a->name, cval);
ret = grib_pack_string(a, cval, &len);
}
return ret;
}
static long staticStepUnits = eccodes::Unit{eccodes::Unit::Value::MISSING}.value<long>();
static long staticForceStepUnits = eccodes::Unit{eccodes::Unit::Value::MISSING}.value<long>();
static int pack_long(grib_accessor* a, const long* val, size_t* len)
{
grib_handle* h = grib_handle_of_accessor(a);
auto supported_units = eccodes::Unit::list_supported_units();
try {
eccodes::Unit unit{*val}; // throws if not supported
auto iter = std::find(supported_units.begin(), supported_units.end(), unit);
if (iter == supported_units.end()) {
throw std::runtime_error{"eccodes::Unit not supported"};
}
}
catch (std::exception& e) {
std::string supported_units_str;
for (auto& u : supported_units)
supported_units_str += eccodes::Unit{u}.value<std::string>() + ",";
supported_units_str.pop_back();
std::string msg = std::string{"Invalid unit: "} + std::to_string(*val) + " (" + e.what() + ")" + ". Available units are: " + supported_units_str;
grib_context_log(a->context, GRIB_LOG_ERROR, msg.c_str());
return GRIB_INVALID_ARGUMENT;
}
int ret;
staticStepUnits = *val;
if ((ret = grib_set_long_internal(h, "forceStepUnits", *val)) != GRIB_SUCCESS) {
return ret;
}
return GRIB_SUCCESS;
}
static int unpack_long(grib_accessor* a, long* val, size_t* len)
{
try {
if (eccodes::Unit{staticStepUnits} != eccodes::Unit{eccodes::Unit::Value::MISSING}) {
*val = staticStepUnits;
return GRIB_SUCCESS;
}
grib_accessor_optimal_step_units* self = (grib_accessor_optimal_step_units*)a;
grib_handle* h = grib_handle_of_accessor(a);
auto forecast_time_opt = get_step(h, self->forecast_time_value, self->forecast_time_unit);
auto time_range_opt = get_step(h, self->time_range_value, self->time_range_unit);
if (forecast_time_opt && time_range_opt) {
auto [step_a, step_b] = find_common_units(forecast_time_opt.value().optimize_unit(), (forecast_time_opt.value() + time_range_opt.value()).optimize_unit());
*val = step_a.unit().value<long>();
}
else if (forecast_time_opt && !time_range_opt) {
*val = forecast_time_opt.value().optimize_unit().unit().value<long>();
}
else if (!forecast_time_opt && time_range_opt) {
*val = time_range_opt.value().optimize_unit().unit().value<long>();
}
else if (!forecast_time_opt && !time_range_opt) {
*val = eccodes::Unit{eccodes::Unit::Value::HOUR}.value<long>();
}
}
catch (std::exception& e) {
grib_context_log(a->context, GRIB_LOG_ERROR, e.what());
return GRIB_INTERNAL_ERROR;
}
return GRIB_SUCCESS;
}
static int pack_string(grib_accessor* a, const char* val, size_t* len)
{
try {
long unit = eccodes::Unit{val}.value<long>();
pack_long(a, &unit, len);
}
catch (std::exception& e) {
auto supported_units = eccodes::Unit::list_supported_units();
std::string supported_units_str;
for (auto& u : supported_units)
supported_units_str += eccodes::Unit{u}.value<std::string>() + ",";
supported_units_str.pop_back();
std::string msg = "Invalid unit: " + std::string(val) + " (" + e.what() + ")" + ". Available units are: " + supported_units_str;
grib_context_log(a->context, GRIB_LOG_ERROR, msg.c_str());
return GRIB_INVALID_ARGUMENT;
}
return GRIB_SUCCESS;
}
static int unpack_string(grib_accessor* a, char* val, size_t* len)
{
int ret = 0;
long unit = 0;
size_t unit_len = 0;
if ((ret = unpack_long(a, &unit, &unit_len)) != GRIB_SUCCESS)
return ret;
*len = snprintf(val, *len, "%s", eccodes::Unit{unit}.value<std::string>().c_str());
return GRIB_SUCCESS;
}
// Step units are never missing
// If the user does not specify a step unit, we default to hours
static int is_missing(grib_accessor* a)
{
return 0;
}
static int get_native_type(grib_accessor* a)
{
return GRIB_TYPE_LONG;
}

View File

@ -9,6 +9,7 @@
*/
#include "grib_api_internal.h"
#include "step.h"
/*
This is used by make_class.pl
@ -127,10 +128,10 @@ static int get_step_human_readable(grib_handle* h, char* result, size_t* length)
/* Change units to seconds (highest resolution)
* before computing the step value
*/
err = grib_set_string(h, "stepUnits", "s", &slen);
if (err) return err;
err = grib_get_long(h, "step", &step);
if (err) return err;
if ((err = grib_set_string(h, "stepUnits", "s", &slen)) != GRIB_SUCCESS)
return err;
if ((err = grib_get_long(h, "step", &step)) != GRIB_SUCCESS)
return err;
hour = step/3600;
minute = step/60 % 60;

View File

@ -9,6 +9,9 @@
*/
#include "grib_api_internal.h"
#include "step.h"
#include "step_utilities.h"
#include <stdexcept>
/*
This is used by make_class.pl
@ -16,12 +19,15 @@
CLASS = accessor
SUPER = grib_accessor_class_long
IMPLEMENTS = unpack_long;pack_long
IMPLEMENTS = unpack_double
IMPLEMENTS = unpack_string;pack_string
IMPLEMENTS = init;dump
MEMBERS = const char* codedStep
MEMBERS = const char* codedUnits
MEMBERS = const char* stepUnits
MEMBERS = const char* indicatorOfUnitForTimeRange
MEMBERS = const char* lengthOfTimeRange
IMPLEMENTS = get_native_type
MEMBERS = const char* forecast_time_value
MEMBERS = const char* forecast_time_unit
MEMBERS = const char* step_units
MEMBERS = const char* time_range_unit
MEMBERS = const char* time_range_value
END_CLASS_DEF
@ -37,8 +43,12 @@ or edit "accessor.class" and rerun ./make_class.pl
*/
static int get_native_type(grib_accessor*);
static int pack_long(grib_accessor*, const long* val, size_t* len);
static int pack_string(grib_accessor*, const char*, size_t* len);
static int unpack_double(grib_accessor*, double* val, size_t* len);
static int unpack_long(grib_accessor*, long* val, size_t* len);
static int unpack_string(grib_accessor*, char*, size_t* len);
static void dump(grib_accessor*, grib_dumper*);
static void init(grib_accessor*, const long, grib_arguments*);
@ -48,11 +58,11 @@ typedef struct grib_accessor_step_in_units
/* Members defined in gen */
/* Members defined in long */
/* Members defined in step_in_units */
const char* codedStep;
const char* codedUnits;
const char* stepUnits;
const char* indicatorOfUnitForTimeRange;
const char* lengthOfTimeRange;
const char* forecast_time_value;
const char* forecast_time_unit;
const char* step_units;
const char* time_range_unit;
const char* time_range_value;
} grib_accessor_step_in_units;
extern grib_accessor_class* grib_accessor_class_long;
@ -72,7 +82,7 @@ static grib_accessor_class _grib_accessor_class_step_in_units = {
0, /* get number of values */
0, /* get number of bytes */
0, /* get offset to bytes */
0, /* get native type */
&get_native_type, /* get native type */
0, /* get sub_section */
0, /* pack_missing */
0, /* is_missing */
@ -80,10 +90,10 @@ static grib_accessor_class _grib_accessor_class_step_in_units = {
&unpack_long, /* unpack_long */
0, /* pack_double */
0, /* pack_float */
0, /* unpack_double */
&unpack_double, /* unpack_double */
0, /* unpack_float */
0, /* pack_string */
0, /* unpack_string */
&pack_string, /* pack_string */
&unpack_string, /* unpack_string */
0, /* pack_string_array */
0, /* unpack_string_array */
0, /* pack_bytes */
@ -113,13 +123,13 @@ grib_accessor_class* grib_accessor_class_step_in_units = &_grib_accessor_class_s
static void init(grib_accessor* a, const long l, grib_arguments* c)
{
grib_accessor_step_in_units* self = (grib_accessor_step_in_units*)a;
int n = 0;
int n = 0;
self->codedStep = grib_arguments_get_name(grib_handle_of_accessor(a), c, n++);
self->codedUnits = grib_arguments_get_name(grib_handle_of_accessor(a), c, n++);
self->stepUnits = grib_arguments_get_name(grib_handle_of_accessor(a), c, n++);
self->indicatorOfUnitForTimeRange = grib_arguments_get_name(grib_handle_of_accessor(a), c, n++);
self->lengthOfTimeRange = grib_arguments_get_name(grib_handle_of_accessor(a), c, n++);
self->forecast_time_value = grib_arguments_get_name(grib_handle_of_accessor(a), c, n++);
self->forecast_time_unit = grib_arguments_get_name(grib_handle_of_accessor(a), c, n++);
self->step_units = grib_arguments_get_name(grib_handle_of_accessor(a), c, n++);
self->time_range_unit = grib_arguments_get_name(grib_handle_of_accessor(a), c, n++);
self->time_range_value = grib_arguments_get_name(grib_handle_of_accessor(a), c, n++);
}
static void dump(grib_accessor* a, grib_dumper* dumper)
@ -127,139 +137,217 @@ static void dump(grib_accessor* a, grib_dumper* dumper)
grib_dump_double(dumper, a, NULL);
}
/* Conversion of to seconds - Grib edition 2 table 4.4 */
static const int u2s2[] = {
60, /* (0) minutes */
3600, /* (1) hour */
86400, /* (2) day */
2592000, /* (3) month */
-1, /* (4) year */
-1, /* (5) decade */
-1, /* (6) 30 years */
-1, /* (7) century */
-1, /* (8) RESERVED */
-1, /* (9) RESERVED */
10800, /* (10) 3 hours */
21600, /* (11) 6 hours */
43200, /* (12) 12 hours */
1 /* (13) seconds */
};
/* Note: 'stepUnits' has a different table with extra entries e.g. 15 and 30 mins */
static const int u2s[] = {
60, /* (0) minutes */
3600, /* (1) hour */
86400, /* (2) day */
2592000, /* (3) month */
-1, /* (4) year */
-1, /* (5) decade */
-1, /* (6) 30 years */
-1, /* (7) century */
-1, /* (8) */
-1, /* (9) */
10800, /* (10) 3 hours */
21600, /* (11) 6 hours */
43200, /* (12) 12 hours */
1, /* (13) seconds */
900, /* (14) 15 minutes */
1800 /* (15) 30 minutes */
};
static int unpack_long(grib_accessor* a, long* val, size_t* len)
{
grib_accessor_step_in_units* self = (grib_accessor_step_in_units*)a;
int err = 0;
long codedStep, codedUnits, stepUnits;
int err = 0;
long forecast_time_value, forecast_time_unit, step_units;
grib_handle* h = grib_handle_of_accessor(a);
int factor = 0;
long u2sf, u2sf_step_unit;
if ((err = grib_get_long_internal(h, self->codedUnits, &codedUnits)))
if ((err= grib_get_long_internal(h, "stepUnits", &step_units)) != GRIB_SUCCESS)
return err;
if ((err = grib_get_long_internal(h, self->stepUnits, &stepUnits)))
if ((err = grib_get_long_internal(h, self->forecast_time_unit, &forecast_time_unit)))
return err;
if ((err = grib_get_long_internal(h, self->codedStep, &codedStep)))
if ((err = grib_get_long_internal(h, self->forecast_time_value, &forecast_time_value)))
return err;
if (stepUnits != codedUnits) {
*val = codedStep * u2s2[codedUnits];
if (*val < 0) {
factor = 60;
if (u2s2[codedUnits] % factor)
return GRIB_DECODING_ERROR;
if (u2s[stepUnits] % factor)
return GRIB_DECODING_ERROR;
u2sf = u2s2[codedUnits] / factor;
*val = codedStep * u2sf;
u2sf_step_unit = u2s[stepUnits] / factor;
}
else {
u2sf_step_unit = u2s[stepUnits];
}
try {
eccodes::Step step{forecast_time_value, forecast_time_unit};
step.optimize_unit();
if (*val % u2sf_step_unit != 0) {
err = grib_set_long_internal(h, self->stepUnits, codedUnits);
*val = codedStep;
if ((err = grib_set_long_internal(h, "startStepUnit", eccodes::Unit{step_units}.value<long>())) != GRIB_SUCCESS)
return err;
}
*val = *val / u2sf_step_unit;
*val = step.value<long>(eccodes::Unit{step_units});
}
else
*val = codedStep;
catch (std::exception& e) {
grib_context_log(h->context, GRIB_LOG_ERROR, "step_in_units: %s", e.what());
return GRIB_DECODING_ERROR;
}
return GRIB_SUCCESS;
}
static int unpack_double(grib_accessor* a, double * val, size_t* len)
{
grib_accessor_step_in_units* self = (grib_accessor_step_in_units*)a;
int err = 0;
long forecast_time_value, forecast_time_unit, step_units;
grib_handle* h = grib_handle_of_accessor(a);
if ((err= grib_get_long_internal(h, "stepUnits", &step_units)) != GRIB_SUCCESS)
return err;
if ((err = grib_get_long_internal(h, self->forecast_time_unit, &forecast_time_unit)))
return err;
if ((err = grib_get_long_internal(h, self->forecast_time_value, &forecast_time_value)))
return err;
try {
eccodes::Step step{forecast_time_value, forecast_time_unit};
if ((err = grib_set_long_internal(h, "startStepUnit", eccodes::Unit{step_units}.value<long>())) != GRIB_SUCCESS)
return err;
*val = step.value<double>(eccodes::Unit{step_units});
}
catch (std::exception& e) {
grib_context_log(h->context, GRIB_LOG_ERROR, "step_in_units: %s", e.what());
return GRIB_DECODING_ERROR;
}
return GRIB_SUCCESS;
}
static int pack_long_new_(grib_accessor* a, const long start_step_value, const long start_step_unit, const long force_step_units)
{
grib_accessor_step_in_units* self = (grib_accessor_step_in_units*)a;
grib_handle* h = grib_handle_of_accessor(a);
int err = 0;
long forecast_time_unit;
long start_step_value_old;
long start_step_unit_old;
size_t len = 0;
if ((err = grib_get_long_internal(h, self->forecast_time_unit, &forecast_time_unit)) != GRIB_SUCCESS)
return err;
if ((err = unpack_long(a, &start_step_value_old, &len)) != GRIB_SUCCESS)
return err;
if ((err = grib_get_long_internal(h, "startStepUnit", &start_step_unit_old)) != GRIB_SUCCESS)
return err;
eccodes::Step start_step_old(start_step_value_old, start_step_unit_old);
eccodes::Step forecast_time(start_step_value, start_step_unit);
eccodes::Step time_range_new{};
auto time_range_opt = get_step(h, self->time_range_value, self->time_range_unit);
if (time_range_opt) {
auto time_range = time_range_opt.value();
time_range = time_range - (forecast_time - start_step_old);
if (time_range.value<long>() < 0)
time_range = eccodes::Step{0l, time_range.unit()};
auto [sa, sb] = find_common_units(forecast_time.optimize_unit(), time_range.optimize_unit());
if ((err = set_step(h, self->forecast_time_value, self->forecast_time_unit, sa)) != GRIB_SUCCESS)
return err;
if ((err = grib_set_long_internal(h, "startStepUnit", forecast_time.unit().value<long>())) != GRIB_SUCCESS)
return err;
if ((err = set_step(h, self->time_range_value, self->time_range_unit, sb)) != GRIB_SUCCESS)
return err;
return GRIB_SUCCESS;
}
if (eccodes::Unit{force_step_units} == eccodes::Unit{eccodes::Unit::Value::MISSING}) {
forecast_time.optimize_unit();
}
if ((err = grib_set_long_internal(h, "startStepUnit", forecast_time.unit().value<long>())) != GRIB_SUCCESS)
return err;
if ((err = set_step(h, self->forecast_time_value, self->forecast_time_unit, forecast_time)) != GRIB_SUCCESS)
return err;
return GRIB_SUCCESS;
}
static int pack_long(grib_accessor* a, const long* val, size_t* len)
{
grib_accessor_step_in_units* self = (grib_accessor_step_in_units*)a;
grib_handle* h = grib_handle_of_accessor(a);
int err = 0;
long codedStep, codedUnits, stepUnits;
long oldStep = 0;
long indicatorOfUnitForTimeRange, lengthOfTimeRange;
grib_handle* h = grib_handle_of_accessor(a);
int ret = GRIB_SUCCESS;
if ((err = grib_get_long_internal(h, self->codedUnits, &codedUnits)))
return err;
if ((err = grib_get_long_internal(h, self->stepUnits, &stepUnits)))
return err;
long force_step_units;
if ((ret = grib_get_long_internal(h, "forceStepUnits", &force_step_units)) != GRIB_SUCCESS)
return ret;
unpack_long(a, &oldStep, len);
long start_step_unit;
try {
if (eccodes::Unit{force_step_units} == eccodes::Unit{eccodes::Unit::Value::MISSING}) {
if ((ret = grib_get_long_internal(h, "startStepUnit", &start_step_unit)) != GRIB_SUCCESS)
return ret;
if (stepUnits != codedUnits) {
codedStep = *val * u2s[stepUnits];
if (codedStep % u2s2[codedUnits] != 0) {
codedUnits = stepUnits;
err = grib_set_long_internal(h, self->codedUnits, codedUnits);
if (err != GRIB_SUCCESS)
return err;
codedStep = *val;
if (eccodes::Unit{start_step_unit} == eccodes::Unit{eccodes::Unit::Value::MISSING})
start_step_unit = eccodes::Unit{eccodes::Unit::Value::HOUR}.value<long>();
}
else {
codedStep = codedStep / u2s2[codedUnits];
start_step_unit = force_step_units;
}
}
else {
codedStep = *val;
catch (std::exception& e) {
grib_context_log(h->context, GRIB_LOG_ERROR, "step_in_units: %s", e.what());
return GRIB_DECODING_ERROR;
}
if (self->indicatorOfUnitForTimeRange) {
if ((err = grib_get_long_internal(h,
self->indicatorOfUnitForTimeRange, &indicatorOfUnitForTimeRange)))
return err;
if ((err = grib_get_long_internal(h,
self->lengthOfTimeRange, &lengthOfTimeRange)))
return err;
if (codedUnits == indicatorOfUnitForTimeRange)
lengthOfTimeRange -= codedStep - oldStep;
else
lengthOfTimeRange -= codedStep * u2s2[codedUnits] / u2s2[indicatorOfUnitForTimeRange];
lengthOfTimeRange = lengthOfTimeRange > 0 ? lengthOfTimeRange : 0;
err = grib_set_long_internal(grib_handle_of_accessor(a), self->lengthOfTimeRange, lengthOfTimeRange);
if (err != GRIB_SUCCESS)
return err;
}
ret = pack_long_new_(a, *val, start_step_unit, force_step_units);
return grib_set_long_internal(grib_handle_of_accessor(a), self->codedStep, codedStep);
return ret;
}
static int pack_string(grib_accessor* a, const char* val, size_t* len)
{
grib_handle* h = grib_handle_of_accessor(a);
//long force_step_units = eccodes::Unit(eccodes::Unit::Value::MISSING).value<long>();
int ret = GRIB_SUCCESS;
long force_step_units;
if ((ret = grib_get_long_internal(h, "forceStepUnits", &force_step_units)) != GRIB_SUCCESS)
return ret;
try {
eccodes::Step step = step_from_string(val, eccodes::Unit{force_step_units});
if ((ret = pack_long_new_(a, step.value<long>(), step.unit().value<long>(), force_step_units)) != GRIB_SUCCESS)
return ret;
}
catch (std::exception& e) {
grib_context_log(a->context, GRIB_LOG_ERROR, "step_in_units: %s", e.what());
return GRIB_DECODING_ERROR;
}
return GRIB_SUCCESS;
}
static int unpack_string(grib_accessor* a, char* val, size_t* len)
{
grib_accessor_step_in_units* self = (grib_accessor_step_in_units*)a;
grib_handle* h = grib_handle_of_accessor(a);
int ret = GRIB_SUCCESS;
long start_step_value;
long start_step_unit;
long step_units;
char fp_format[128] = "%g";
size_t fp_format_len = sizeof(fp_format);
int show_hours = a->context->show_hour_stepunit;
if ((ret = grib_get_long_internal(h, "startStep", &start_step_value)) != GRIB_SUCCESS)
return ret;
if ((ret = grib_get_long_internal(h, "startStepUnit", &start_step_unit)) != GRIB_SUCCESS)
return ret;
if ((ret = grib_get_long_internal(h, self->step_units, &step_units)))
return ret;
if ((ret = grib_get_string_internal(h, "formatForDoubles", fp_format, &fp_format_len)) != GRIB_SUCCESS)
return ret;
try {
eccodes::Step step{start_step_value, start_step_unit};
std::stringstream ss;
ss << step.value<std::string>(fp_format, show_hours);
size_t size = ss.str().size() + 1;
if (*len < size)
return GRIB_ARRAY_TOO_SMALL;
*len = size;
memcpy(val, ss.str().c_str(), size);
}
catch (std::exception& e) {
grib_context_log(h->context, GRIB_LOG_ERROR, "step_in_units: %s", e.what());
return GRIB_DECODING_ERROR;
}
return GRIB_SUCCESS;
}
static int get_native_type(grib_accessor* a)
{
return GRIB_TYPE_STRING;
}

View File

@ -37,7 +37,7 @@
#line 6 "accessor_class_list.gperf"
struct accessor_class_hash { char *name; grib_accessor_class **cclass;};
#define TOTAL_KEYWORDS 203
#define TOTAL_KEYWORDS 204
#define MIN_WORD_LENGTH 1
#define MAX_WORD_LENGTH 44
#define MIN_HASH_VALUE 1
@ -117,21 +117,21 @@ static const struct accessor_class_hash classes[] =
#line 9 "accessor_class_list.gperf"
{" "},
{""}, {""}, {""}, {""},
#line 177 "accessor_class_list.gperf"
#line 178 "accessor_class_list.gperf"
{"size", &grib_accessor_class_size},
#line 12 "accessor_class_list.gperf"
{"ascii", &grib_accessor_class_ascii},
#line 174 "accessor_class_list.gperf"
#line 175 "accessor_class_list.gperf"
{"signed", &grib_accessor_class_signed},
#line 154 "accessor_class_list.gperf"
#line 155 "accessor_class_list.gperf"
{"pad", &grib_accessor_class_pad},
#line 180 "accessor_class_list.gperf"
#line 181 "accessor_class_list.gperf"
{"spd", &grib_accessor_class_spd},
{""},
#line 75 "accessor_class_list.gperf"
{"dirty", &grib_accessor_class_dirty},
{""},
#line 175 "accessor_class_list.gperf"
#line 176 "accessor_class_list.gperf"
{"signed_bits", &grib_accessor_class_signed_bits},
{""},
#line 66 "accessor_class_list.gperf"
@ -141,7 +141,7 @@ static const struct accessor_class_hash classes[] =
{"data_simple_packing", &grib_accessor_class_data_simple_packing},
#line 68 "accessor_class_list.gperf"
{"data_secondary_bitmap", &grib_accessor_class_data_secondary_bitmap},
#line 168 "accessor_class_list.gperf"
#line 169 "accessor_class_list.gperf"
{"section", &grib_accessor_class_section},
#line 43 "accessor_class_list.gperf"
{"data_apply_bitmap", &grib_accessor_class_data_apply_bitmap},
@ -155,36 +155,36 @@ static const struct accessor_class_hash classes[] =
{"data_jpeg2000_packing", &grib_accessor_class_data_jpeg2000_packing},
#line 65 "accessor_class_list.gperf"
{"data_png_packing", &grib_accessor_class_data_png_packing},
#line 170 "accessor_class_list.gperf"
{"section_padding", &grib_accessor_class_section_padding},
#line 171 "accessor_class_list.gperf"
{"section_padding", &grib_accessor_class_section_padding},
#line 172 "accessor_class_list.gperf"
{"section_pointer", &grib_accessor_class_section_pointer},
#line 44 "accessor_class_list.gperf"
{"data_apply_boustrophedonic", &grib_accessor_class_data_apply_boustrophedonic},
{""}, {""}, {""}, {""},
#line 80 "accessor_class_list.gperf"
{"expanded_descriptors", &grib_accessor_class_expanded_descriptors},
#line 155 "accessor_class_list.gperf"
#line 156 "accessor_class_list.gperf"
{"padding", &grib_accessor_class_padding},
#line 45 "accessor_class_list.gperf"
{"data_apply_boustrophedonic_bitmap", &grib_accessor_class_data_apply_boustrophedonic_bitmap},
#line 110 "accessor_class_list.gperf"
{"gds_is_present", &grib_accessor_class_gds_is_present},
#line 167 "accessor_class_list.gperf"
#line 168 "accessor_class_list.gperf"
{"second_order_bits_per_value", &grib_accessor_class_second_order_bits_per_value},
#line 169 "accessor_class_list.gperf"
#line 170 "accessor_class_list.gperf"
{"section_length", &grib_accessor_class_section_length},
#line 113 "accessor_class_list.gperf"
{"getenv", &grib_accessor_class_getenv},
#line 57 "accessor_class_list.gperf"
{"data_g22order_packing", &grib_accessor_class_data_g22order_packing},
#line 189 "accessor_class_list.gperf"
#line 190 "accessor_class_list.gperf"
{"time", &grib_accessor_class_time},
{""},
#line 61 "accessor_class_list.gperf"
{"data_g2shsimple_packing", &grib_accessor_class_data_g2shsimple_packing},
{""},
#line 153 "accessor_class_list.gperf"
#line 154 "accessor_class_list.gperf"
{"packing_type", &grib_accessor_class_packing_type},
#line 62 "accessor_class_list.gperf"
{"data_g2simple_packing", &grib_accessor_class_data_g2simple_packing},
@ -201,25 +201,25 @@ static const struct accessor_class_hash classes[] =
#line 141 "accessor_class_list.gperf"
{"nearest", &grib_accessor_class_nearest},
{""},
#line 156 "accessor_class_list.gperf"
#line 157 "accessor_class_list.gperf"
{"padto", &grib_accessor_class_padto},
#line 187 "accessor_class_list.gperf"
#line 188 "accessor_class_list.gperf"
{"sum", &grib_accessor_class_sum},
{""},
#line 107 "accessor_class_list.gperf"
{"g2lon", &grib_accessor_class_g2lon},
#line 201 "accessor_class_list.gperf"
#line 202 "accessor_class_list.gperf"
{"uint8", &grib_accessor_class_uint8},
{""},
#line 186 "accessor_class_list.gperf"
#line 187 "accessor_class_list.gperf"
{"step_in_units", &grib_accessor_class_step_in_units},
#line 63 "accessor_class_list.gperf"
{"data_g2simple_packing_with_preprocessing", &grib_accessor_class_data_g2simple_packing_with_preprocessing},
#line 199 "accessor_class_list.gperf"
#line 200 "accessor_class_list.gperf"
{"uint64", &grib_accessor_class_uint64},
#line 47 "accessor_class_list.gperf"
{"data_complex_packing", &grib_accessor_class_data_complex_packing},
#line 197 "accessor_class_list.gperf"
#line 198 "accessor_class_list.gperf"
{"uint32", &grib_accessor_class_uint32},
#line 13 "accessor_class_list.gperf"
{"bit", &grib_accessor_class_bit},
@ -245,10 +245,10 @@ static const struct accessor_class_hash classes[] =
#line 16 "accessor_class_list.gperf"
{"bits_per_value", &grib_accessor_class_bits_per_value},
{""}, {""}, {""}, {""}, {""},
#line 165 "accessor_class_list.gperf"
#line 166 "accessor_class_list.gperf"
{"scale", &grib_accessor_class_scale},
{""},
#line 183 "accessor_class_list.gperf"
#line 184 "accessor_class_list.gperf"
{"statistics", &grib_accessor_class_statistics},
#line 102 "accessor_class_list.gperf"
{"g2date", &grib_accessor_class_g2date},
@ -265,13 +265,13 @@ static const struct accessor_class_hash classes[] =
{"gds_not_present_bitmap", &grib_accessor_class_gds_not_present_bitmap},
#line 122 "accessor_class_list.gperf"
{"iterator", &grib_accessor_class_iterator},
#line 184 "accessor_class_list.gperf"
#line 185 "accessor_class_list.gperf"
{"statistics_spectral", &grib_accessor_class_statistics_spectral},
#line 46 "accessor_class_list.gperf"
{"data_ccsds_packing", &grib_accessor_class_data_ccsds_packing},
#line 145 "accessor_class_list.gperf"
{"number_of_points_gaussian", &grib_accessor_class_number_of_points_gaussian},
#line 204 "accessor_class_list.gperf"
#line 205 "accessor_class_list.gperf"
{"unsigned", &grib_accessor_class_unsigned},
#line 138 "accessor_class_list.gperf"
{"md5", &grib_accessor_class_md5},
@ -280,30 +280,30 @@ static const struct accessor_class_hash classes[] =
{"g2_aerosol", &grib_accessor_class_g2_aerosol},
#line 139 "accessor_class_list.gperf"
{"message", &grib_accessor_class_message},
#line 205 "accessor_class_list.gperf"
#line 206 "accessor_class_list.gperf"
{"unsigned_bits", &grib_accessor_class_unsigned_bits},
#line 172 "accessor_class_list.gperf"
#line 173 "accessor_class_list.gperf"
{"select_step_template", &grib_accessor_class_select_step_template},
#line 136 "accessor_class_list.gperf"
{"mars_param", &grib_accessor_class_mars_param},
#line 202 "accessor_class_list.gperf"
#line 203 "accessor_class_list.gperf"
{"unexpanded_descriptors", &grib_accessor_class_unexpanded_descriptors},
#line 191 "accessor_class_list.gperf"
#line 192 "accessor_class_list.gperf"
{"to_integer", &grib_accessor_class_to_integer},
{""}, {""}, {""},
#line 176 "accessor_class_list.gperf"
#line 177 "accessor_class_list.gperf"
{"simple_packing_error", &grib_accessor_class_simple_packing_error},
#line 185 "accessor_class_list.gperf"
#line 186 "accessor_class_list.gperf"
{"step_human_readable", &grib_accessor_class_step_human_readable},
#line 140 "accessor_class_list.gperf"
{"message_copy", &grib_accessor_class_message_copy},
#line 161 "accessor_class_list.gperf"
#line 162 "accessor_class_list.gperf"
{"raw", &grib_accessor_class_raw},
{""}, {""},
#line 200 "accessor_class_list.gperf"
#line 201 "accessor_class_list.gperf"
{"uint64_little_endian", &grib_accessor_class_uint64_little_endian},
{""},
#line 198 "accessor_class_list.gperf"
#line 199 "accessor_class_list.gperf"
{"uint32_little_endian", &grib_accessor_class_uint32_little_endian},
#line 114 "accessor_class_list.gperf"
{"global_gaussian", &grib_accessor_class_global_gaussian},
@ -311,20 +311,20 @@ static const struct accessor_class_hash classes[] =
#line 76 "accessor_class_list.gperf"
{"divdouble", &grib_accessor_class_divdouble},
{""},
#line 173 "accessor_class_list.gperf"
#line 174 "accessor_class_list.gperf"
{"sexagesimal2decimal", &grib_accessor_class_sexagesimal2decimal},
#line 99 "accessor_class_list.gperf"
{"g2_mars_labeling", &grib_accessor_class_g2_mars_labeling},
#line 78 "accessor_class_list.gperf"
{"element", &grib_accessor_class_element},
#line 158 "accessor_class_list.gperf"
#line 159 "accessor_class_list.gperf"
{"padtomultiple", &grib_accessor_class_padtomultiple},
#line 127 "accessor_class_list.gperf"
{"latitudes", &grib_accessor_class_latitudes},
#line 162 "accessor_class_list.gperf"
#line 163 "accessor_class_list.gperf"
{"rdbtime_guess_date", &grib_accessor_class_rdbtime_guess_date},
{""}, {""}, {""},
#line 192 "accessor_class_list.gperf"
#line 193 "accessor_class_list.gperf"
{"to_string", &grib_accessor_class_to_string},
#line 132 "accessor_class_list.gperf"
{"long", &grib_accessor_class_long},
@ -348,20 +348,20 @@ static const struct accessor_class_hash classes[] =
{""},
#line 28 "accessor_class_list.gperf"
{"bufrdc_expanded_descriptors", &grib_accessor_class_bufrdc_expanded_descriptors},
#line 159 "accessor_class_list.gperf"
#line 160 "accessor_class_list.gperf"
{"position", &grib_accessor_class_position},
{""}, {""}, {""},
#line 17 "accessor_class_list.gperf"
{"blob", &grib_accessor_class_blob},
#line 109 "accessor_class_list.gperf"
{"gaussian_grid_name", &grib_accessor_class_gaussian_grid_name},
#line 195 "accessor_class_list.gperf"
#line 196 "accessor_class_list.gperf"
{"trim", &grib_accessor_class_trim},
{""}, {""},
#line 119 "accessor_class_list.gperf"
{"ibmfloat", &grib_accessor_class_ibmfloat},
{""}, {""},
#line 193 "accessor_class_list.gperf"
#line 194 "accessor_class_list.gperf"
{"transient", &grib_accessor_class_transient},
#line 55 "accessor_class_list.gperf"
{"data_g1shsimple_packing", &grib_accessor_class_data_g1shsimple_packing},
@ -370,13 +370,13 @@ static const struct accessor_class_hash classes[] =
{"data_g1simple_packing", &grib_accessor_class_data_g1simple_packing},
#line 49 "accessor_class_list.gperf"
{"data_g1complex_packing", &grib_accessor_class_data_g1complex_packing},
#line 164 "accessor_class_list.gperf"
#line 165 "accessor_class_list.gperf"
{"round", &grib_accessor_class_round},
#line 194 "accessor_class_list.gperf"
#line 195 "accessor_class_list.gperf"
{"transient_darray", &grib_accessor_class_transient_darray},
#line 18 "accessor_class_list.gperf"
{"budgdate", &grib_accessor_class_budgdate},
#line 208 "accessor_class_list.gperf"
#line 209 "accessor_class_list.gperf"
{"values", &grib_accessor_class_values},
#line 143 "accessor_class_list.gperf"
{"number_of_coded_values", &grib_accessor_class_number_of_coded_values},
@ -386,30 +386,30 @@ static const struct accessor_class_hash classes[] =
#line 38 "accessor_class_list.gperf"
{"concept", &grib_accessor_class_concept},
{""},
#line 181 "accessor_class_list.gperf"
#line 182 "accessor_class_list.gperf"
{"spectral_truncation", &grib_accessor_class_spectral_truncation},
{""}, {""},
#line 83 "accessor_class_list.gperf"
{"g1_message_length", &grib_accessor_class_g1_message_length},
#line 182 "accessor_class_list.gperf"
#line 183 "accessor_class_list.gperf"
{"sprintf", &grib_accessor_class_sprintf},
#line 188 "accessor_class_list.gperf"
#line 189 "accessor_class_list.gperf"
{"suppressed", &grib_accessor_class_suppressed},
{""},
#line 211 "accessor_class_list.gperf"
#line 212 "accessor_class_list.gperf"
{"when", &grib_accessor_class_when},
{""}, {""},
#line 178 "accessor_class_list.gperf"
#line 179 "accessor_class_list.gperf"
{"smart_table", &grib_accessor_class_smart_table},
{""},
#line 39 "accessor_class_list.gperf"
{"constant", &grib_accessor_class_constant},
{""}, {""},
#line 160 "accessor_class_list.gperf"
#line 161 "accessor_class_list.gperf"
{"proj_string", &grib_accessor_class_proj_string},
#line 149 "accessor_class_list.gperf"
{"octet_number", &grib_accessor_class_octet_number},
#line 179 "accessor_class_list.gperf"
#line 180 "accessor_class_list.gperf"
{"smart_table_column", &grib_accessor_class_smart_table_column},
{""},
#line 135 "accessor_class_list.gperf"
@ -426,7 +426,7 @@ static const struct accessor_class_hash classes[] =
{"data_sh_unpacked", &grib_accessor_class_data_sh_unpacked},
#line 27 "accessor_class_list.gperf"
{"bufr_string_values", &grib_accessor_class_bufr_string_values},
#line 196 "accessor_class_list.gperf"
#line 197 "accessor_class_list.gperf"
{"uint16", &grib_accessor_class_uint16},
{""}, {""},
#line 71 "accessor_class_list.gperf"
@ -439,7 +439,7 @@ static const struct accessor_class_hash classes[] =
{""},
#line 54 "accessor_class_list.gperf"
{"data_g1secondary_bitmap", &grib_accessor_class_data_g1secondary_bitmap},
#line 190 "accessor_class_list.gperf"
#line 191 "accessor_class_list.gperf"
{"to_double", &grib_accessor_class_to_double},
#line 101 "accessor_class_list.gperf"
{"g2bitmap_present", &grib_accessor_class_g2bitmap_present},
@ -447,14 +447,14 @@ static const struct accessor_class_hash classes[] =
{"ieeefloat", &grib_accessor_class_ieeefloat},
#line 150 "accessor_class_list.gperf"
{"offset_file", &grib_accessor_class_offset_file},
#line 206 "accessor_class_list.gperf"
{"validity_date", &grib_accessor_class_validity_date},
#line 207 "accessor_class_list.gperf"
{"validity_date", &grib_accessor_class_validity_date},
#line 208 "accessor_class_list.gperf"
{"validity_time", &grib_accessor_class_validity_time},
{""},
#line 130 "accessor_class_list.gperf"
{"library_version", &grib_accessor_class_library_version},
#line 210 "accessor_class_list.gperf"
#line 211 "accessor_class_list.gperf"
{"vector", &grib_accessor_class_vector},
#line 84 "accessor_class_list.gperf"
{"g1_section4_length", &grib_accessor_class_g1_section4_length},
@ -467,7 +467,7 @@ static const struct accessor_class_hash classes[] =
{""},
#line 24 "accessor_class_list.gperf"
{"bufr_extract_subsets", &grib_accessor_class_bufr_extract_subsets},
#line 157 "accessor_class_list.gperf"
#line 158 "accessor_class_list.gperf"
{"padtoeven", &grib_accessor_class_padtoeven},
#line 50 "accessor_class_list.gperf"
{"data_g1second_order_constant_width_packing", &grib_accessor_class_data_g1second_order_constant_width_packing},
@ -483,7 +483,7 @@ static const struct accessor_class_hash classes[] =
#line 21 "accessor_class_list.gperf"
{"bufr_elements_table", &grib_accessor_class_bufr_elements_table},
{""}, {""}, {""}, {""},
#line 152 "accessor_class_list.gperf"
#line 153 "accessor_class_list.gperf"
{"pack_bufr_values", &grib_accessor_class_pack_bufr_values},
#line 35 "accessor_class_list.gperf"
{"codetable", &grib_accessor_class_codetable},
@ -503,12 +503,14 @@ static const struct accessor_class_hash classes[] =
{""},
#line 33 "accessor_class_list.gperf"
{"closest_date", &grib_accessor_class_closest_date},
{""}, {""},
#line 209 "accessor_class_list.gperf"
#line 152 "accessor_class_list.gperf"
{"optimal_step_units", &grib_accessor_class_optimal_step_units},
{""},
#line 210 "accessor_class_list.gperf"
{"variable", &grib_accessor_class_variable},
#line 133 "accessor_class_list.gperf"
{"long_vector", &grib_accessor_class_long_vector},
#line 166 "accessor_class_list.gperf"
#line 167 "accessor_class_list.gperf"
{"scale_values", &grib_accessor_class_scale_values},
{""}, {""}, {""},
#line 128 "accessor_class_list.gperf"
@ -533,7 +535,7 @@ static const struct accessor_class_hash classes[] =
{""}, {""},
#line 41 "accessor_class_list.gperf"
{"count_missing", &grib_accessor_class_count_missing},
#line 203 "accessor_class_list.gperf"
#line 204 "accessor_class_list.gperf"
{"unpack_bufr_values", &grib_accessor_class_unpack_bufr_values},
{""}, {""},
#line 121 "accessor_class_list.gperf"
@ -598,7 +600,7 @@ static const struct accessor_class_hash classes[] =
#line 87 "accessor_class_list.gperf"
{"g1day_of_the_year_date", &grib_accessor_class_g1day_of_the_year_date},
{""}, {""}, {""}, {""}, {""},
#line 163 "accessor_class_list.gperf"
#line 164 "accessor_class_list.gperf"
{"reference_value_error", &grib_accessor_class_reference_value_error},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},

View File

@ -141,6 +141,7 @@
{ "octet_number", &grib_accessor_class_octet_number, },
{ "offset_file", &grib_accessor_class_offset_file, },
{ "offset_values", &grib_accessor_class_offset_values, },
{ "optimal_step_units", &grib_accessor_class_optimal_step_units, },
{ "pack_bufr_values", &grib_accessor_class_pack_bufr_values, },
{ "packing_type", &grib_accessor_class_packing_type, },
{ "pad", &grib_accessor_class_pad, },

View File

@ -142,6 +142,7 @@ octahedral_gaussian, &grib_accessor_class_octahedral_gaussian
octet_number, &grib_accessor_class_octet_number
offset_file, &grib_accessor_class_offset_file
offset_values, &grib_accessor_class_offset_values
optimal_step_units, &grib_accessor_class_optimal_step_units
pack_bufr_values, &grib_accessor_class_pack_bufr_values
packing_type, &grib_accessor_class_packing_type
pad, &grib_accessor_class_pad

View File

@ -1004,6 +1004,7 @@ struct grib_context
int no_big_group_split;
int no_spd;
int keep_matrix;
int show_hour_stepunit;
char* grib_definition_files_path;
char* grib_samples_path;
char* grib_concept_path;

View File

@ -287,6 +287,7 @@ static grib_context default_grib_context = {
0, /* no_big_group_split */
0, /* no_spd */
0, /* keep_matrix */
0, /* show_hour_stepunit */
0, /* grib_definition_files_path */
0, /* grib_samples_path */
0, /* grib_concept_path */
@ -388,6 +389,7 @@ grib_context* grib_context_get_default()
const char* no_big_group_split = NULL;
const char* no_spd = NULL;
const char* keep_matrix = NULL;
const char* show_hour_stepunit = NULL;
const char* bufrdc_mode = NULL;
const char* bufr_set_to_missing_if_out_of_range = NULL;
const char* bufr_multi_element_constant_arrays = NULL;
@ -415,6 +417,7 @@ grib_context* grib_context_get_default()
no_big_group_split = codes_getenv("ECCODES_GRIB_NO_BIG_GROUP_SPLIT");
no_spd = codes_getenv("ECCODES_GRIB_NO_SPD");
keep_matrix = codes_getenv("ECCODES_GRIB_KEEP_MATRIX");
show_hour_stepunit = codes_getenv("ECCODES_GRIB_SHOW_HOUR_STEPUNIT");
file_pool_max_opened_files = getenv("ECCODES_FILE_POOL_MAX_OPENED_FILES");
/* On UNIX, when we read from a file we get exactly what is in the file on disk.
@ -429,6 +432,7 @@ grib_context* grib_context_get_default()
default_grib_context.no_big_group_split = no_big_group_split ? atoi(no_big_group_split) : 0;
default_grib_context.no_spd = no_spd ? atoi(no_spd) : 0;
default_grib_context.keep_matrix = keep_matrix ? atoi(keep_matrix) : 1;
default_grib_context.show_hour_stepunit = show_hour_stepunit ? atoi(show_hour_stepunit) : 0;
default_grib_context.write_on_fail = write_on_fail ? atoi(write_on_fail) : 0;
default_grib_context.no_abort = no_abort ? atoi(no_abort) : 0;
default_grib_context.debug = debug ? atoi(debug) : 0;

220
src/step.cc Normal file
View File

@ -0,0 +1,220 @@
/*
* (C) Copyright 2005- ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
*
* In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
* virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
*/
#include <map>
#include <stdexcept>
#include <utility>
#include <limits>
#include <iostream>
#include <algorithm>
#include <cassert>
#include <regex>
#include "step_unit.h"
#include "step.h"
namespace eccodes {
Step step_from_string(const std::string& step, const Unit& force_unit)
{
std::regex re("([0-9.]+)([smhDMYC]?)");
std::smatch match;
if (std::regex_match(step, match, re)) {
if (match.size() == 3) {
std::string value = match[1];
std::string unit_str = match[2];
Unit unit;
if (unit_str.size() != 0) {
if (force_unit == Unit{Unit::Value::MISSING})
unit = Unit{unit_str};
else
throw std::runtime_error("Cannot force unit when unit is specified in step string");
}
else {
if (force_unit == Unit{Unit::Value::MISSING})
unit = Unit{Unit::Value::HOUR};
else
unit = force_unit;
}
Step ret{std::stod(value), unit};
return ret;
}
}
throw std::runtime_error("Could not parse step: " + step);
}
std::vector<Step> parse_range(const std::string& range_str, const Unit& force_unit)
{
std::vector<Step> steps;
std::string::size_type pos = 0;
std::string::size_type prev = 0;
while ((pos = range_str.find("-", prev)) != std::string::npos) {
steps.push_back(step_from_string(range_str.substr(prev, pos - prev), force_unit));
prev = pos + 1;
}
steps.push_back(step_from_string(range_str.substr(prev), force_unit));
return steps;
}
bool Step::operator==(const Step& other) const
{
if ((internal_value_ == other.internal_value_) && (internal_unit_ == other.internal_unit_)) {
return true;
}
return false;
}
bool Step::operator>(const Step& step) const
{
auto [a, b] = find_common_units(this->copy().optimize_unit(), step.copy().optimize_unit());
assert(a.internal_unit_ == b.internal_unit_);
return a.internal_value_ > b.internal_value_;
}
bool Step::operator<(const Step& step) const
{
auto [a, b] = find_common_units(this->copy().optimize_unit(), step.copy().optimize_unit());
assert(a.internal_unit_ == b.internal_unit_);
return a.internal_value_ < b.internal_value_;
}
Step Step::operator+(const Step& step) const
{
auto [a, b] = find_common_units(this->copy().optimize_unit(), step.copy().optimize_unit());
assert(a.internal_unit_ == b.internal_unit_);
return Step(a.internal_value_ + b.internal_value_, a.internal_unit_);
}
Step Step::operator-(const Step& step) const
{
auto [a, b] = find_common_units(this->copy().optimize_unit(), step.copy().optimize_unit());
assert(a.internal_unit_ == b.internal_unit_);
return Step(a.internal_value_ - b.internal_value_, a.internal_unit_);
}
std::pair<Step, Step> find_common_units(const Step& startStep, const Step& endStep)
{
Step a = startStep;
Step b = endStep;
if (a.internal_value_ == 0 && b.internal_value_ == 0) {
Unit unit = a.internal_unit_ > b.internal_unit_ ? a.internal_unit_ : b.internal_unit_;
b.internal_unit_ = unit;
b.unit_ = unit;
a.internal_unit_ = unit;
a.unit_ = unit;
}
else if (b.internal_value_ == 0) {
b.internal_unit_ = a.internal_unit_;
b.unit_ = a.internal_unit_;
a.unit_ = a.internal_unit_;
a.recalculateValue();
}
else if (a.internal_value_ == 0) {
a.internal_unit_ = b.internal_unit_;
a.unit_ = b.internal_unit_;
b.unit_ = b.internal_unit_;
b.recalculateValue();
}
else {
auto it = std::find_if(Unit::grib_selected_units.begin(), Unit::grib_selected_units.end(), [&](const auto& e) {
return e == a.unit().value<Unit::Value>() || e == b.unit().value<Unit::Value>();
});
assert(it != Unit::grib_selected_units.end());
a.set_unit(*it);
b.set_unit(*it);
a.recalculateValue();
b.recalculateValue();
assert(a.internal_unit_ == b.internal_unit_);
}
return {a, b};
}
void Step::init_long(long value, const Unit& unit)
{
internal_value_ = value;
internal_unit_ = unit;
unit_ = unit;
}
void Step::init_double(double value, const Unit& unit)
{
auto seconds = Unit::get_converter().unit_to_duration(unit.value<Unit::Value>());
internal_value_ = value * seconds;
internal_unit_ = Unit{Unit::Value::SECOND};
unit_ = unit;
}
Step& Step::optimize_unit()
{
if (internal_value_ == 0) {
if (unit() > Unit{Unit::Value::HOUR}) {
set_unit(Unit{Unit::Value::HOUR});
}
return *this;
}
unit_ = internal_unit_;
Seconds<long> seconds = to_seconds<long>(internal_value_, internal_unit_);
long abs_seconds = seconds.count() < 0 ? -seconds.count() : seconds.count();
for (auto it = Unit::grib_selected_units.rbegin(); it != Unit::grib_selected_units.rend(); ++it) {
long multiplier = Unit::get_converter().unit_to_duration(*it);
if (abs_seconds % multiplier == 0) {
internal_value_ = seconds.count() / multiplier;
internal_unit_ = *it;
unit_ = *it;
return *this;
}
}
return *this;
}
template <>
std::string Step::value<std::string>(const std::string& format, bool show_hours) const {
constexpr int MAX_SIZE = 128;
char output[MAX_SIZE];
std::string u;
int err;
// Do not print unit if it is HOUR to keep backward compatibility
// with previous versions of ecCodes (see ECC-1620). This is a temporary solution.
if (show_hours) {
u = unit_.value<std::string>();
}
else {
if (unit_ != Unit::Value::HOUR)
u = unit_.value<std::string>();
}
if (unit_ == Unit::Value::MINUTES15 ||
unit_ == Unit::Value::MINUTES30 ||
unit_ == Unit::Value::HOURS3 ||
unit_ == Unit::Value::HOURS6 ||
unit_ == Unit::Value::HOURS12 ||
unit_ == Unit::Value::YEARS10 ||
unit_ == Unit::Value::YEARS30
)
err = snprintf(output, MAX_SIZE, (format + "x%s").c_str(), value<double>(), u.c_str());
else
err = snprintf(output, MAX_SIZE, (format + "%s").c_str(), value<double>(), u.c_str());
if (err < 0 || err >= MAX_SIZE) {
throw std::runtime_error("Error while formatting Step to string");
}
return output;
}
} // namespace eccodes

127
src/step.h Normal file
View File

@ -0,0 +1,127 @@
/*
* (C) Copyright 2005- ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
*
* In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
* virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
*/
#pragma once
#include <tuple>
#include <map>
#include <exception>
#include <stdexcept>
#include <string>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <cassert>
#include <sstream>
#include <optional>
#include "step_unit.h"
namespace eccodes {
class Step {
public:
// Constructors
Step() : internal_value_(0), internal_unit_(Unit::Value::SECOND) {}
Step(double value, const Unit& unit) : internal_unit_{unit}, unit_{internal_unit_} {init_double(value, unit);}
Step(double value, Unit::Value unit) {init_double(value, Unit{unit});}
Step(double value, long unit) {init_double(value, Unit{unit});}
Step(double value, const std::string& unit) {init_double(value, Unit{unit});}
Step(long value, const Unit& unit) { init_long(value, unit);}
Step(long value, Unit::Value unit) {init_long(value, Unit{unit});}
Step(long value, long unit) {init_long(value, Unit{unit});}
Step(long value, const std::string& unit) {init_long(value, Unit{unit});}
// Getters
template <typename T> T value() const;
template <typename T> T value(const Unit& unit) const;
template <typename T> T value(const std::string& format, bool show_hours) const;
Unit unit() const { return unit_; }
// Setters
Step& set_unit(const std::string& unit_name) {unit_ = Unit{unit_name}; return *this;}
Step& set_unit(long unit_code) {unit_ = Unit{unit_code}; return *this;}
Step& set_unit(const Unit& new_unit) {unit_ = new_unit; return *this;}
Step& set_unit(const Unit::Value new_unit) {unit_ = new_unit; return *this;}
// Operators
bool operator==(const Step& other) const;
bool operator!=(const Step& other) const;
Step operator+(const Step& step) const;
Step operator-(const Step& step) const;
bool operator>(const Step& step) const;
bool operator<(const Step& step) const;
Step copy() const {
Step ret{};
ret.internal_value_ = internal_value_;
ret.internal_unit_ = internal_unit_;
ret.unit_ = unit_;
return ret;
}
// Methods
Step& optimize_unit();
friend std::pair<Step, Step> find_common_units(const Step& startStep, const Step& endStep);
private:
void init_long(long value, const Unit& unit);
void init_double(double value, const Unit& unit);
Step& recalculateValue() {
if (internal_value_ == 0) {
internal_unit_ = unit_;
return *this;
}
Seconds<long> seconds = to_seconds<long>(internal_value_, internal_unit_);
long multiplier = Unit::get_converter().unit_to_duration(unit_.value<Unit::Value>());
internal_value_ = seconds.count() / multiplier;
internal_unit_ = unit_;
return *this;
}
long internal_value_;
Unit internal_unit_;
Unit unit_;
};
Step step_from_string(const std::string& step, const Unit& force_unit);
std::vector<Step> parse_range(const std::string& range_str, const Unit& force_unit);
std::pair<Step, Step> find_common_units(const Step& startStep, const Step& endStep);
template <typename T> T Step::value() const {
if (internal_value_ == 0) {
return 0;
}
if (internal_unit_ == unit_) {
return internal_value_;
}
Seconds<T> seconds = to_seconds<T>(internal_value_, internal_unit_);
T value = from_seconds<T>(seconds, unit_);
return value;
}
template <typename T> T Step::value(const Unit& unit) const {
if (internal_value_ == 0) {
return 0;
}
if (internal_unit_ == unit) {
return internal_value_;
}
Seconds<T> seconds = to_seconds<T>(internal_value_, internal_unit_);
T value = from_seconds<T>(seconds, unit);
return value;
}
} // namespace eccodes

53
src/step_unit.cc Normal file
View File

@ -0,0 +1,53 @@
/*
* (C) Copyright 2005- ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
*
* In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
* virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
*/
#include "step_unit.h"
namespace eccodes {
Unit::Map Unit::map_{};
std::vector<Unit::Value> Unit::grib_selected_units = {
Unit::Value::SECOND,
Unit::Value::MINUTE,
Unit::Value::HOUR,
};
std::vector<Unit::Value> Unit::complete_unit_order_ = {
Unit::Value::MISSING ,
Unit::Value::SECOND ,
Unit::Value::MINUTE ,
Unit::Value::MINUTES15 ,
Unit::Value::MINUTES30 ,
Unit::Value::HOUR ,
Unit::Value::HOURS3 ,
Unit::Value::HOURS6 ,
Unit::Value::HOURS12 ,
Unit::Value::DAY ,
Unit::Value::MONTH ,
Unit::Value::YEAR ,
Unit::Value::YEARS10 ,
Unit::Value::YEARS30 ,
Unit::Value::CENTURY
};
template <> long Unit::value<long>() const {
return map_.unit_to_long(internal_value_);
}
template <> Unit::Value Unit::value<Unit::Value>() const {
return internal_value_;
}
template <> std::string Unit::value<std::string>() const {
return map_.unit_to_name(internal_value_);
}
} // namespace eccodes

238
src/step_unit.h Normal file
View File

@ -0,0 +1,238 @@
/*
* (C) Copyright 2005- ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
*
* In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
* virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
*/
#pragma once
#include <chrono>
#include <string>
#include <vector>
#include <stdexcept>
#include <array>
#include <unordered_map>
#include <algorithm>
namespace eccodes {
template <typename T> using Minutes = std::chrono::duration<T, std::ratio<60>>;
template <typename T> using Hours = std::chrono::duration<T, std::ratio<3600>>;
template <typename T> using Days = std::chrono::duration<T, std::ratio<86400>>;
template <typename T> using Months = std::chrono::duration<T, std::ratio<2592000>>;
template <typename T> using Years = std::chrono::duration<T, std::ratio<31536000>>;
template <typename T> using Years10 = std::chrono::duration<T, std::ratio<315360000>>;
template <typename T> using Years30 = std::chrono::duration<T, std::ratio<946080000>>;
template <typename T> using Centuries = std::chrono::duration<T, std::ratio<3153600000>>;
template <typename T> using Hours3 = std::chrono::duration<T, std::ratio<10800>>;
template <typename T> using Hours6 = std::chrono::duration<T, std::ratio<21600>>;
template <typename T> using Hours12 = std::chrono::duration<T, std::ratio<43200>>;
template <typename T> using Seconds = std::chrono::duration<T, std::ratio<1>>;
template <typename T> using Minutes15 = std::chrono::duration<T, std::ratio<900>>;
template <typename T> using Minutes30 = std::chrono::duration<T, std::ratio<1800>>;
template <typename T> using Missing = std::chrono::duration<T, std::ratio<0>>;
class Unit;
template <typename T> Seconds<T> to_seconds(long value, const Unit& unit);
template <typename T> T from_seconds(Seconds<T> seconds, const Unit& unit);
class Unit {
public:
enum class Value {
MINUTE = 0,
HOUR = 1,
DAY = 2,
MONTH = 3,
YEAR = 4,
YEARS10 = 5,
YEARS30 = 6,
CENTURY = 7,
HOURS3 = 10,
HOURS6 = 11,
HOURS12 = 12,
SECOND = 13,
MINUTES15 = 14,
MINUTES30 = 15,
MISSING = 255,
};
Unit() : internal_value_(Value::HOUR) {}
explicit Unit(Value unit_value) : internal_value_(unit_value) {}
explicit Unit(const std::string& unit_value) {
try {
internal_value_ = map_.name_to_unit(unit_value);
} catch (std::exception& e) {
throw std::runtime_error(std::string{"Unit not found "} + e.what());
}
}
explicit Unit(long unit_value) {
try {
internal_value_ = map_.long_to_unit(unit_value);
} catch (std::exception& e) {
throw std::runtime_error(std::string{"Unit not found "} + e.what());
}
}
bool operator>(const Unit& other) const {return map_.unit_to_duration(internal_value_) > map_.unit_to_duration(other.internal_value_);}
bool operator==(const Value value) const {return map_.unit_to_duration(internal_value_) == map_.unit_to_duration(value);}
bool operator==(const Unit& unit) const {return map_.unit_to_duration(internal_value_) == map_.unit_to_duration(unit.internal_value_);}
bool operator!=(const Unit& unit) const {return !(*this == unit);}
bool operator!=(const Value value) const {return !(*this == value);}
Unit& operator=(const Value value) {
internal_value_ = value;
return *this;
}
template <typename T> T value() const;
static std::vector<Value> grib_selected_units;
static std::vector<Value> complete_unit_order_;
static std::vector<Unit> list_supported_units() {
std::vector<Unit> result;
result.reserve(32);
for (const auto& val : complete_unit_order_) {
if (val == Value::MISSING)
continue;
result.push_back(Unit(val));
}
return result;
}
private:
class Map {
public:
Map() {
for (const auto& entry : tab_) {
// unit_value <-> unit_name
name_to_value_[entry.unit_name] = entry.unit_value;
value_to_name_[entry.unit_value] = entry.unit_name;
// unit_value <-> duration in seconds
value_to_duration_[entry.unit_value] = entry.duration;
duration_to_value_[entry.duration] = entry.unit_value;
// unit_value <-> wmo_code
value_to_long_[entry.unit_value] = static_cast<long>(entry.unit_value);
long_to_value_[static_cast<long>(entry.unit_value)] = entry.unit_value;
}
}
// wmo_code <-> unit_name
std::string unit_to_name(const Value& unit_value) const {return value_to_name_.at(unit_value);}
Value name_to_unit(const std::string& name) const {return name_to_value_.at(name);}
// unit_value <-> duration
uint64_t unit_to_duration(const Value& unit_value) const {return value_to_duration_.at(unit_value);}
Value duration_to_unit(long duration) const {return duration_to_value_.at(duration);}
// wmo_code <-> unit_name
long unit_to_long(const Value& unit_value) const {return value_to_long_.at(unit_value);}
Value long_to_unit(long wmo_code) const {return long_to_value_.at(wmo_code);}
private:
struct Entry {
Value unit_value;
std::string unit_name;
uint64_t duration;
};
const std::array<Entry, 15> tab_ = {{
Entry{Value::MISSING , "MISSING" , 0},
Entry{Value::SECOND , "s" , 1},
Entry{Value::MINUTE , "m" , 60},
Entry{Value::MINUTES15 , "15m" , 900},
Entry{Value::MINUTES30 , "30m" , 1800},
Entry{Value::HOUR , "h" , 3600},
Entry{Value::HOURS3 , "3h" , 10800},
Entry{Value::HOURS6 , "6h" , 21600},
Entry{Value::HOURS12 , "12h" , 43200},
Entry{Value::DAY , "D" , 86400},
Entry{Value::MONTH , "M" , 2592000},
Entry{Value::YEAR , "Y" , 31536000},
Entry{Value::YEARS10 , "10Y" , 315360000},
Entry{Value::YEARS30 , "30Y" , 946080000},
Entry{Value::CENTURY , "C" , 3153600000},
}};
std::unordered_map<std::string, Value> name_to_value_;
std::unordered_map<Value, std::string> value_to_name_;
std::unordered_map<Value, long> value_to_long_;
std::unordered_map<long, Value> long_to_value_;
std::unordered_map<Value, uint64_t> value_to_duration_;
std::unordered_map<uint64_t, Value> duration_to_value_;
};
Value internal_value_;
static Map map_;
public:
static Map& get_converter() {return map_;}
};
template <typename T>
Seconds<T> to_seconds(long value, const Unit& unit) {
Seconds<T> seconds;
switch (unit.value<Unit::Value>()) {
case Unit::Value::SECOND: seconds = Seconds<T>(value); break;
case Unit::Value::MINUTE: seconds = Minutes<T>(value); break;
case Unit::Value::MINUTES15: seconds = Minutes15<T>(value); break;
case Unit::Value::MINUTES30: seconds = Minutes30<T>(value); break;
case Unit::Value::HOUR: seconds = Hours<T>(value); break;
case Unit::Value::HOURS3: seconds = Hours3<T>(value); break;
case Unit::Value::HOURS6: seconds = Hours6<T>(value); break;
case Unit::Value::HOURS12: seconds = Hours12<T>(value); break;
case Unit::Value::DAY: seconds = Days<T>(value); break;
case Unit::Value::MONTH: seconds = Months<T>(value); break;
case Unit::Value::YEAR: seconds = Years<T>(value); break;
case Unit::Value::YEARS10: seconds = Years10<T>(value); break;
case Unit::Value::YEARS30: seconds = Years30<T>(value); break;
case Unit::Value::CENTURY: seconds = Centuries<T>(value); break;
default:
std::string msg = "Unknown unit: " + unit.value<std::string>();
throw std::runtime_error(msg);
}
return seconds;
}
template <typename T>
T from_seconds(Seconds<T> seconds, const Unit& unit) {
T value;
switch (unit.value<Unit::Value>()) {
case Unit::Value::SECOND: value = std::chrono::duration_cast<Seconds<T>>(seconds).count(); break;
case Unit::Value::MINUTE: value = std::chrono::duration_cast<Minutes<T>>(seconds).count(); break;
case Unit::Value::MINUTES15: value = std::chrono::duration_cast<Minutes15<T>>(seconds).count(); break;
case Unit::Value::MINUTES30: value = std::chrono::duration_cast<Minutes30<T>>(seconds).count(); break;
case Unit::Value::HOUR: value = std::chrono::duration_cast<Hours<T>>(seconds).count(); break;
case Unit::Value::HOURS3: value = std::chrono::duration_cast<Hours3<T>>(seconds).count(); break;
case Unit::Value::HOURS6: value = std::chrono::duration_cast<Hours6<T>>(seconds).count(); break;
case Unit::Value::HOURS12: value = std::chrono::duration_cast<Hours12<T>>(seconds).count(); break;
case Unit::Value::DAY: value = std::chrono::duration_cast<Days<T>>(seconds).count(); break;
case Unit::Value::MONTH: value = std::chrono::duration_cast<Months<T>>(seconds).count(); break;
case Unit::Value::YEAR: value = std::chrono::duration_cast<Years<T>>(seconds).count(); break;
case Unit::Value::YEARS10: value = std::chrono::duration_cast<Years10<T>>(seconds).count(); break;
case Unit::Value::YEARS30: value = std::chrono::duration_cast<Years30<T>>(seconds).count(); break;
case Unit::Value::CENTURY: value = std::chrono::duration_cast<Centuries<T>>(seconds).count(); break;
default:
std::string msg = "Unknown unit: " + unit.value<std::string>();
throw std::runtime_error(msg);
}
return value;
}
} // namespace eccodes

40
src/step_utilities.cc Normal file
View File

@ -0,0 +1,40 @@
/*
* (C) Copyright 2005- ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
*
* In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
* virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
*/
#include "step_utilities.h"
#include <type_traits>
std::optional<eccodes::Step> get_step(grib_handle* h, const char* value_key, const char* unit_key)
{
if (value_key && unit_key && grib_is_defined(h, unit_key) && grib_is_defined(h, value_key)) {
long unit = 0;
if (grib_get_long_internal(h, unit_key, &unit) != GRIB_SUCCESS)
return {};
long value = 0;
if (grib_get_long_internal(h, value_key, &value) != GRIB_SUCCESS)
return {};
return eccodes::Step(value, unit);
}
else {
return {};
}
}
int set_step(grib_handle* h, const std::string& value_key, const std::string& unit_key, const eccodes::Step& step)
{
int err;
if ((err = grib_set_long_internal(h, value_key.c_str(), step.value<long>())) != GRIB_SUCCESS)
return err;
if ((err = grib_set_long_internal(h, unit_key.c_str(), step.unit().value<long>())) != GRIB_SUCCESS)
return err;
return GRIB_SUCCESS;
}

18
src/step_utilities.h Normal file
View File

@ -0,0 +1,18 @@
/*
* (C) Copyright 2005- ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
*
* In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
* virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
*/
#pragma once
#include "grib_api_internal.h"
#include "step.h"
#include <optional>
std::optional<eccodes::Step> get_step(grib_handle* h, const char* value_key, const char* unit_key);
int set_step(grib_handle* h, const std::string& value_key, const std::string& unit_key, const eccodes::Step& step);

View File

@ -248,6 +248,7 @@ if( HAVE_BUILD_TOOLS )
grib_ecc-1397
grib_ecc-1425
grib_ecc-1467
grib_sub_hourly
grib_set_bytes
grib_set_force
bufr_ecc-556

View File

@ -147,7 +147,7 @@ ${tools_dir}/grib_set -s stepUnits=d $input $outfile > $temp 2>&1
status=$?
set -e
[ $status -ne 0 ]
grep -q "stepUnits: No such code table entry.*Did you mean" $temp
grep -q "Unit not found" $temp
set +e
${tools_dir}/grib_set -s centre=ECMF $input $outfile > $temp 2>&1

View File

@ -182,28 +182,28 @@ result=$( ${tools_dir}/grib_get -p dataTime -s hour=2,minute=255 $input )
# Various step units
# --------------------
input=${data_dir}/tigge_cf_ecmwf.grib2
result=$(${tools_dir}/grib_get -w count=1 -p step -s stepUnits=h $input)
result=$(${tools_dir}/grib_get -w count=1 -p step:i -s stepUnits=h $input)
[ $result = 96 ]
result=$(${tools_dir}/grib_get -w count=1 -p step -s stepUnits=30m $input)
result=$(${tools_dir}/grib_get -w count=1 -p step:i -s stepUnits=30m $input)
[ $result = 192 ]
result=$(${tools_dir}/grib_get -w count=1 -p step -s stepUnits=15m $input)
result=$(${tools_dir}/grib_get -w count=1 -p step:i -s stepUnits=15m $input)
[ $result = 384 ]
result=$(${tools_dir}/grib_get -w count=1 -p step -s stepUnits=s $input)
result=$(${tools_dir}/grib_get -w count=1 -p step:i -s stepUnits=s $input)
[ $result = 345600 ]
result=$(${tools_dir}/grib_get -w count=1 -p step -s stepUnits=12h $input)
result=$(${tools_dir}/grib_get -w count=1 -p step:i -s stepUnits=12h $input)
[ $result = 8 ]
result=$(${tools_dir}/grib_get -w count=1 -p step -s stepUnits=6h $input)
result=$(${tools_dir}/grib_get -w count=1 -p step:i -s stepUnits=6h $input)
[ $result = 16 ]
result=$(${tools_dir}/grib_get -w count=1 -p step -s stepUnits=D $input)
result=$(${tools_dir}/grib_get -w count=1 -p step:i -s stepUnits=D $input)
[ $result = 4 ]
result=$(${tools_dir}/grib_get -w count=1 -p step -s stepUnits=m $input)
result=$(${tools_dir}/grib_get -w count=1 -p step:i -s stepUnits=m $input)
[ $result = 5760 ]
# GRIB1 stepRange and timeRangeIndicator=10

486
tests/grib_sub_hourly.sh Executable file
View File

@ -0,0 +1,486 @@
#!/bin/sh
# (C) Copyright 2005- ECMWF.
#
# This software is licensed under the terms of the Apache Licence Version 2.0
# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
#
# In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
# virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
#
. ./include.ctest.sh
# See JIRA issues ECC-1620, ECC-1238
# -----------------------------------
grib_expect_failure()
{
a_file=$1
a_params=$2
${tools_dir}/grib_get $a_params $a_file > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "File: '$a_file'"
echo "Key(s): '$a_params'"
echo "Expected: 'failure'"
echo "Result: 'success'"
exit 1
fi
}
grib_check_key_equals()
{
a_file=$1
a_params=$2
a_expected=$3
a_result=`${tools_dir}/grib_get $a_params $a_file`
if [ "$a_result" != "$a_expected" ]; then
echo "File: '$a_file'"
echo "Key(s): '$a_params'"
echo "Expected: '$a_expected'"
echo "Result: '$a_result'"
${tools_dir}/grib_dump -O $a_file | grep -E "indicatorOfUnitOfTimeRange|lengthOfTimeRange|indicatorOfUnitForTimeRange|forecastTime"
exit 1
fi
}
HOUR=""
if (set -u; : ${ECCODES_GRIB_SHOW_HOUR_STEPUNIT?}) 2> /dev/null; then
if [ $ECCODES_GRIB_SHOW_HOUR_STEPUNIT -gt 0 ]; then
export HOUR="h"
fi
fi
label="grib_sub_hourly"
temp=temp.1.$label
temp2=temp.2.$label
tempFilt=temp.$label.filt
samples_dir=$ECCODES_SAMPLES_PATH
instantaneous_field=$data_dir/reduced_gaussian_surface.grib2
accumulated_field=$data_dir/reduced_gaussian_sub_area.grib2
#### Make sure that step, stepRange, startStep, endStep produce the same result for instantaneous fields
fn="$instantaneous_field"
low_level_keys="forecastTime,indicatorOfUnitOfTimeRange:s"
keys_step="step,step:s,step:i,step:d,stepUnits:s"
keys_step_range="stepRange,stepRange:s,stepRange:i,stepRange:d,stepUnits:s"
keys_start_step="startStep,startStep:s,startStep:i,startStep:d,stepUnits:s"
keys_end_step="endStep,endStep:s,endStep:i,endStep:d,stepUnits:s"
${tools_dir}/grib_set -s forecastTime=0,indicatorOfUnitOfTimeRange=h $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "0 h"
${tools_dir}/grib_set -s stepUnits=m,step=59 $fn $temp
grib_check_key_equals $temp "-p $keys_step" "59m 59m 59 59 m"
grib_check_key_equals $temp "-p $keys_step_range" "59m 59m 59 59 m"
grib_check_key_equals $temp "-p $keys_start_step" "59m 59m 59 59 m"
grib_check_key_equals $temp "-p $keys_end_step" "59m 59m 59 59 m"
${tools_dir}/grib_set -s forecastTime=0,indicatorOfUnitOfTimeRange=h $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "0 h"
${tools_dir}/grib_set -s step=59m $fn $temp
grib_check_key_equals $temp "-p $keys_step" "59m 59m 59 59 m"
grib_check_key_equals $temp "-p $keys_step_range" "59m 59m 59 59 m"
grib_check_key_equals $temp "-p $keys_start_step" "59m 59m 59 59 m"
grib_check_key_equals $temp "-p $keys_end_step" "59m 59m 59 59 m"
#### stepUnits overrides the units in the low level keys
# if stepUnits=UNIT is set, then set the low level keys to UNIT
# else optimise low level keys
# instant fields:
low_level_keys="forecastTime,indicatorOfUnitOfTimeRange:s,lengthOfTimeRange,indicatorOfUnitForTimeRange:s"
fn="$instantaneous_field"
low_level_keys="forecastTime,indicatorOfUnitOfTimeRange:s"
keys__="step,stepUnits:s"
keys_s="step:s"
keys_i="step:i,stepUnits:s"
keys_d="step:d,stepUnits:s"
${tools_dir}/grib_set -s stepUnits=m,step=60 $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "60 m"
grib_check_key_equals $temp "-p $keys_s" "1$HOUR"
grib_check_key_equals $temp "-p $keys_s -s stepUnits=m" "60m"
${tools_dir}/grib_set -s stepUnits=m,step=60 $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "60 m"
grib_check_key_equals $temp "-p $keys_s" "1$HOUR"
grib_check_key_equals $temp "-p $keys_s -s stepUnits=m" "60m"
${tools_dir}/grib_set -s step=60m $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "1 h"
grib_check_key_equals $temp "-p $keys_s" "1$HOUR"
grib_check_key_equals $temp "-p $keys_s -s stepUnits=m" "60m"
# accumulated fields:
fn="$accumulated_field"
low_level_keys="forecastTime,indicatorOfUnitOfTimeRange:s,lengthOfTimeRange,indicatorOfUnitForTimeRange:s"
keys__="step,startStep,endStep,stepRange,stepUnits:s"
keys_s="step:s,startStep:s,endStep:s,stepRange:s,stepUnits:s"
keys_i="step:i,startStep:i,endStep:i,stepRange:i,stepUnits:s"
keys_d="step:d,startStep:d,endStep:d,stepRange:d,stepUnits:s"
${tools_dir}/grib_set -s stepUnits=m,stepRange=60-120 $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "60 m 60 m"
grib_check_key_equals $temp "-p $keys_s" "2$HOUR 1$HOUR 2$HOUR 1$HOUR-2$HOUR h"
grib_check_key_equals $temp "-p $keys_s -s stepUnits=m" "120m 60m 120m 60m-120m m"
${tools_dir}/grib_set -s stepUnits=m,stepRange=60-120 $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "60 m 60 m"
grib_check_key_equals $temp "-p $keys_s" "2$HOUR 1$HOUR 2$HOUR 1$HOUR-2$HOUR h"
grib_check_key_equals $temp "-p $keys_s -s stepUnits=m" "120m 60m 120m 60m-120m m"
${tools_dir}/grib_set -s stepRange=60m-120m $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "1 h 1 h"
grib_check_key_equals $temp "-p $keys_s" "2$HOUR 1$HOUR 2$HOUR 1$HOUR-2$HOUR h"
grib_check_key_equals $temp "-p $keys_s -s stepUnits=m" "120m 60m 120m 60m-120m m"
#### CHECK units
fn="$accumulated_field"
low_level_keys="forecastTime,indicatorOfUnitOfTimeRange:s,lengthOfTimeRange,indicatorOfUnitForTimeRange:s"
${tools_dir}/grib_set -s forecastTime=0,indicatorOfUnitOfTimeRange=h,lengthOfTimeRange=96,indicatorOfUnitForTimeRange=h $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "0 h 96 h"
grib_check_key_equals $temp " -w count=1 -s stepUnits=s -p step:i,stepUnits:s" "345600 s"
grib_check_key_equals $temp " -w count=1 -s stepUnits=m -p step:i,stepUnits:s" "5760 m"
grib_check_key_equals $temp " -w count=1 -s stepUnits=15m -p step:i,stepUnits:s" "384 15m"
grib_check_key_equals $temp " -w count=1 -s stepUnits=30m -p step:i,stepUnits:s" "192 30m"
grib_check_key_equals $temp " -w count=1 -s stepUnits=h -p step:i,stepUnits:s" "96 h"
grib_check_key_equals $temp " -w count=1 -s stepUnits=6h -p step:i,stepUnits:s" "16 6h"
grib_check_key_equals $temp " -w count=1 -s stepUnits=12h -p step:i,stepUnits:s" "8 12h"
grib_check_key_equals $temp " -w count=1 -s stepUnits=D -p step:i,stepUnits:s" "4 D"
grib_check_key_equals $temp " -w count=1 -s stepUnits=s -p step,stepUnits:s" "345600s s"
grib_check_key_equals $temp " -w count=1 -s stepUnits=m -p step,stepUnits:s" "5760m m"
grib_check_key_equals $temp " -w count=1 -s stepUnits=15m -p step,stepUnits:s" "384x15m 15m"
grib_check_key_equals $temp " -w count=1 -s stepUnits=30m -p step,stepUnits:s" "192x30m 30m"
grib_check_key_equals $temp " -w count=1 -s stepUnits=h -p step,stepUnits:s" "96$HOUR h"
grib_check_key_equals $temp " -w count=1 -s stepUnits=6h -p step,stepUnits:s" "16x6h 6h"
grib_check_key_equals $temp " -w count=1 -s stepUnits=12h -p step,stepUnits:s" "8x12h 12h"
grib_check_key_equals $temp " -w count=1 -s stepUnits=D -p step,stepUnits:s" "4D D"
${tools_dir}/grib_set -s stepUnits=s,startStep=0,endStep=345600 $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "0 s 345600 s"
${tools_dir}/grib_set -s stepUnits=m,startStep=0,endStep=5760 $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "0 m 5760 m"
${tools_dir}/grib_set -s stepUnits=h,startStep=0,endStep=96 $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "0 h 96 h"
${tools_dir}/grib_set -s stepUnits=6h,startStep=0,endStep=16 $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "0 6h 16 6h"
${tools_dir}/grib_set -s stepUnits=12h,startStep=0,endStep=8 $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "0 12h 8 12h"
${tools_dir}/grib_set -s stepUnits=D,startStep=0,endStep=4 $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "0 D 4 D"
#### CHECK negative forecastTime
fn="$accumulated_field"
low_level_keys="forecastTime,indicatorOfUnitOfTimeRange:s,lengthOfTimeRange,indicatorOfUnitForTimeRange:s"
${tools_dir}/grib_set -s forecastTime=-6,indicatorOfUnitOfTimeRange=h,lengthOfTimeRange=6,indicatorOfUnitForTimeRange=h $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "-6 h 6 h"
grib_check_key_equals $temp "-s stepUnits:s=h -p startStep:s,endStep:s" "-6$HOUR 0$HOUR"
grib_check_key_equals $temp "-s stepUnits:s=m -p startStep:s,endStep:s" "-360m 0m"
grib_check_key_equals $temp "-s stepUnits:s=s -p startStep:s,endStep:s" "-21600s 0s"
${tools_dir}/grib_set -s forecastTime=-48,indicatorOfUnitOfTimeRange=h,lengthOfTimeRange=0,indicatorOfUnitForTimeRange=h $fn $temp
grib_check_key_equals $temp "-p stepRange" "-48$HOUR"
#### CHECK: check optimal units are set correctly in GRIB files
fn="$accumulated_field"
low_level_keys="forecastTime,indicatorOfUnitOfTimeRange:s,lengthOfTimeRange,indicatorOfUnitForTimeRange:s"
${tools_dir}/grib_set -s forecastTime=24,indicatorOfUnitOfTimeRange=h,lengthOfTimeRange=1,indicatorOfUnitForTimeRange=D $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "24 h 1 D"
### TODO(maee): @Shahram: how to make parameters position independent
${tools_dir}/grib_set -s stepUnits:s=s,startStep:i=60,endStep:i=180 $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "60 s 120 s"
#${tools_dir}/grib_set -s startStep:i=60,endStep:i=180,stepUnits:s=s $temp $temp2
#grib_check_key_equals $temp2 "-p $low_level_keys" "1 m 2 m"
# Seconds
${tools_dir}/grib_set -s stepUnits:i=13,startStep:i=60,endStep:i=180 $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "60 s 120 s"
${tools_dir}/grib_set -s stepUnits:s=s,startStep:i=60,endStep:i=180 $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "60 s 120 s"
# Minutes
${tools_dir}/grib_set -s stepUnits:i=0,startStep:i=60,endStep:i=180 $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "60 m 120 m"
${tools_dir}/grib_set -s stepUnits:s=m,startStep:i=60,endStep:i=180 $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "60 m 120 m"
# Hours
${tools_dir}/grib_set -s stepUnits:i=1,startStep:i=60,endStep:i=180 $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "60 h 120 h"
${tools_dir}/grib_set -s stepUnits:s=h,startStep:i=60,endStep:i=180 $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "60 h 120 h"
#fn="$accumulated_field"
#low_level_keys="forecastTime,indicatorOfUnitOfTimeRange:s,lengthOfTimeRange,indicatorOfUnitForTimeRange:s"
##high_level_keys="startStep:s,endStep:s"
#high_level_keys="startStep:i,endStep:i"
#${tools_dir}/grib_set -s forecastTime=24,indicatorOfUnitOfTimeRange=h,lengthOfTimeRange=1,indicatorOfUnitForTimeRange=D $fn $temp
#grib_check_key_equals $temp "-p $low_level_keys" "24 h 1 D"
#grib_check_key_equals $temp "-p $high_level_keys" "24 48"
#${tools_dir}/grib_set -s startStep:i=24 $temp $temp2
#grib_check_key_equals $temp2 "-p $low_level_keys" "24 h 0 h"
#grib_check_key_equals $temp2 "-p $high_level_keys" "24 24"
#${tools_dir}/grib_set -s forecastTime=24,indicatorOfUnitOfTimeRange=h,lengthOfTimeRange=24,indicatorOfUnitForTimeRange=h $fn $temp
#grib_check_key_equals $temp "-p $low_level_keys" "24 h 24 h"
#grib_check_key_equals $temp "-p $high_level_keys" "24 48"
#${tools_dir}/grib_set -s startStep:i=24 $temp $temp2
#grib_check_key_equals $temp2 "-p $low_level_keys" "24 h 24 h"
#grib_check_key_equals $temp2 "-p $high_level_keys" "24 48"
#exit
#### CHECK: grib_set - endStep + stepUnits
fn="$accumulated_field"
low_level_keys="forecastTime,indicatorOfUnitOfTimeRange:s,lengthOfTimeRange,indicatorOfUnitForTimeRange:s"
${tools_dir}/grib_set -s forecastTime=24,indicatorOfUnitOfTimeRange=h,lengthOfTimeRange=1,indicatorOfUnitForTimeRange=D $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "24 h 1 D"
# Use range unit: hour
${tools_dir}/grib_set -s endStep:d=30 $temp $temp2 # TODO(maee) remove in the future behavior
#${tools_dir}/grib_set -s endStep:i=30 $temp $temp2 # TODO(maee) keep for backwards compatibility
#${tools_dir}/grib_set -s endStep:s=30 $temp $temp2
#${tools_dir}/grib_set -s endStep:s=30h $temp $temp2
#${tools_dir}/grib_set -s endStep=30h $temp $temp2 # TODO(maee) add to tests
grib_check_key_equals $temp2 "-p $low_level_keys" "24 h 6 h"
# Use stepUnits
${tools_dir}/grib_set -s endStep:s=30 $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "24 h 6 h"
${tools_dir}/grib_set -s endStep:s=30h $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "24 h 6 h"
${tools_dir}/grib_set -s endStep:s=88200s $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "1440 m 30 m"
${tools_dir}/grib_set -s endStep:s=24024 $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "24 h 24000 h"
${tools_dir}/grib_set -s startStep:d=5 $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "5 h 43 h"
${tools_dir}/grib_set -s startStep:s=5h $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "5 h 43 h"
${tools_dir}/grib_set -s startStep:s=240s $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "4 m 2876 m"
${tools_dir}/grib_set -s startStep:s=2 $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "2 h 46 h"
${tools_dir}/grib_set -s stepRange:s=5h-30h $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "5 h 25 h"
grib_check_key_equals $temp2 "-p stepRange:s" "5$HOUR-30$HOUR"
${tools_dir}/grib_set -s stepRange:s=5-30 $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "5 h 25 h"
grib_check_key_equals $temp2 "-p stepRange:s" "5$HOUR-30$HOUR"
${tools_dir}/grib_set -s stepRange:s=60m-120m $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "1 h 1 h"
grib_check_key_equals $temp2 "-p stepRange:s" "1$HOUR-2$HOUR"
${tools_dir}/grib_set -s stepRange:s=60s-120s $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "1 m 1 m"
grib_check_key_equals $temp2 "-p stepRange:s" "1m-2m"
${tools_dir}/grib_set -s stepRange:s=60m-121m $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "60 m 61 m"
grib_check_key_equals $temp2 "-p stepRange:s" "60m-121m"
${tools_dir}/grib_set -s stepRange:s=62D-122D $temp $temp2
grib_check_key_equals $temp2 "-p $low_level_keys" "1488 h 1440 h"
grib_check_key_equals $temp2 "-p stepRange:s" "1488$HOUR-2928$HOUR"
fn="$instantaneous_field"
low_level_keys="forecastTime,indicatorOfUnitOfTimeRange:s"
keys__="step,stepUnits:s"
keys_s="step:s"
keys_i="step:i,stepUnits:s"
keys_d="step:d,stepUnits:s"
${tools_dir}/grib_set -s forecastTime=59,indicatorOfUnitOfTimeRange=m $fn $temp
grib_check_key_equals $temp "-p $keys__ -s stepUnits=s" "3540s s"
grib_check_key_equals $temp "-p $keys__ -s stepUnits=m" "59m m"
#grib_check_key_equals $temp "-p $keys__ -s stepUnits=h" "0" # not supported
grib_check_key_equals $temp "-p $keys_s -s stepUnits=s" "3540s"
grib_check_key_equals $temp "-p $keys_s -s stepUnits=m" "59m"
#grib_check_key_equals $temp "-p $keys_s -F"%.2f" -s stepUnits=h" "0.98" # not supported
grib_check_key_equals $temp "-p $keys_i -s stepUnits=s" "3540 s"
grib_check_key_equals $temp "-p $keys_i -s stepUnits=m" "59 m"
#grib_check_key_equals $temp "-p $keys_i -s stepUnits=h" "0" # not supported
grib_check_key_equals $temp "-p $keys_d -s stepUnits=s" "3540 s"
grib_check_key_equals $temp "-p $keys_d -s stepUnits=m" "59 m"
#grib_check_key_equals $temp "-p $keys_d -s stepUnits=h" "0.983333" # not supported
${tools_dir}/grib_set -s forecastTime=0,indicatorOfUnitOfTimeRange=m $fn $temp
grib_check_key_equals $temp "-p $keys_i -s stepUnits=s" "0 s"
grib_check_key_equals $temp "-p $keys_i -s stepUnits=m" "0 m"
grib_check_key_equals $temp "-p $keys_i -s stepUnits=h" "0 h"
grib_check_key_equals $temp "-p $keys_d -s stepUnits=s" "0 s"
grib_check_key_equals $temp "-p $keys_d -s stepUnits=m" "0 m"
grib_check_key_equals $temp "-p $keys_d -s stepUnits=h" "0 h"
fn="$instantaneous_field"
low_level_keys="forecastTime,indicatorOfUnitOfTimeRange:s"
keys__="step,stepUnits:s"
keys_s="step:s,stepUnits:s"
keys_i="step:i,stepUnits:s"
keys_d="step:d,stepUnits:s"
${tools_dir}/grib_set -s forecastTime=0,indicatorOfUnitOfTimeRange=m $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "0 m"
grib_check_key_equals $temp "-p $keys__" "0m m"
grib_check_key_equals $temp "-p $keys_s" "0m m"
grib_check_key_equals $temp "-p $keys_i" "0 m"
grib_check_key_equals $temp "-p $keys_d" "0 m"
grib_check_key_equals $temp "-p $keys__ -s stepUnits=s" "0s s"
grib_check_key_equals $temp "-p $keys__ -s stepUnits=m" "0m m"
grib_check_key_equals $temp "-p $keys__ -s stepUnits=h" "0$HOUR h"
grib_check_key_equals $temp "-p $keys_s -s stepUnits=s" "0s s"
grib_check_key_equals $temp "-p $keys_s -s stepUnits=m" "0m m"
grib_check_key_equals $temp "-p $keys_s -s stepUnits=h" "0$HOUR h"
grib_check_key_equals $temp "-p $keys_i -s stepUnits=s" "0 s"
grib_check_key_equals $temp "-p $keys_i -s stepUnits=m" "0 m"
grib_check_key_equals $temp "-p $keys_i -s stepUnits=h" "0 h"
grib_check_key_equals $temp "-p $keys_d -s stepUnits=s" "0 s"
grib_check_key_equals $temp "-p $keys_d -s stepUnits=m" "0 m"
grib_check_key_equals $temp "-p $keys_d -s stepUnits=h" "0 h"
${tools_dir}/grib_set -s forecastTime=59,indicatorOfUnitOfTimeRange=m $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "59 m"
grib_check_key_equals $temp "-p $keys__" "59m m"
#grib_check_key_equals $temp "-p $keys_s" "59"
grib_check_key_equals $temp "-p $keys_s" "59m m"
grib_check_key_equals $temp "-p $keys_i" "59 m"
grib_check_key_equals $temp "-p $keys_d" "59 m"
${tools_dir}/grib_set -s forecastTime=60,indicatorOfUnitOfTimeRange=m $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "60 m"
grib_check_key_equals $temp "-p $keys__" "1$HOUR h"
grib_check_key_equals $temp "-p $keys_s" "1$HOUR h"
grib_check_key_equals $temp "-p $keys_i" "1 h"
grib_check_key_equals $temp "-p $keys_d" "1 h"
${tools_dir}/grib_set -s forecastTime=61,indicatorOfUnitOfTimeRange=m $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "61 m"
grib_check_key_equals $temp "-p $keys__" "61m m"
#grib_check_key_equals $temp "-p $keys_s" "61"
grib_check_key_equals $temp "-p $keys_s" "61m m"
grib_check_key_equals $temp "-p $keys_i" "61 m"
grib_check_key_equals $temp "-p $keys_d" "61 m"
${tools_dir}/grib_set -s forecastTime=24,indicatorOfUnitOfTimeRange=h $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "24 h"
grib_check_key_equals $temp "-p $keys__" "24$HOUR h"
grib_check_key_equals $temp "-p $keys_s" "24$HOUR h"
grib_check_key_equals $temp "-p $keys_i" "24 h"
grib_check_key_equals $temp "-p $keys_d" "24 h"
${tools_dir}/grib_set -s forecastTime=1440,indicatorOfUnitOfTimeRange=m $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "1440 m"
grib_check_key_equals $temp "-p $keys__" "24$HOUR h"
grib_check_key_equals $temp "-p $keys_s" "24$HOUR h"
grib_check_key_equals $temp "-p $keys_i" "24 h"
grib_check_key_equals $temp "-p $keys_d" "24 h"
fn="$accumulated_field"
low_level_keys="forecastTime,indicatorOfUnitOfTimeRange:s,lengthOfTimeRange,indicatorOfUnitForTimeRange:s"
${tools_dir}/grib_set -s stepRange=60m-2h $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "1 h 1 h"
fn="$accumulated_field"
low_level_keys="forecastTime,indicatorOfUnitOfTimeRange:s,lengthOfTimeRange,indicatorOfUnitForTimeRange:s"
keys__="stepRange,startStep,endStep"
keys_s="stepRange:s,startStep:s,endStep:s"
keys_i="stepRange:i,startStep:i,endStep:i"
keys_d="stepRange:d,startStep:d,endStep:d"
${tools_dir}/grib_set -s forecastTime=0,indicatorOfUnitOfTimeRange=m,lengthOfTimeRange=2,indicatorOfUnitForTimeRange=h $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "0 m 2 h"
grib_check_key_equals $temp "-p $keys__" "0$HOUR-2$HOUR 0$HOUR 2$HOUR"
grib_check_key_equals $temp "-p $keys_s" "0$HOUR-2$HOUR 0$HOUR 2$HOUR"
grib_check_key_equals $temp "-p $keys_i" "2 0 2"
grib_check_key_equals $temp "-p $keys_d" "2 0 2"
${tools_dir}/grib_set -s forecastTime=24,indicatorOfUnitOfTimeRange=h,lengthOfTimeRange=1,indicatorOfUnitForTimeRange=D $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "24 h 1 D"
grib_check_key_equals $temp "-p $keys__" "24$HOUR-48$HOUR 24$HOUR 48$HOUR"
grib_check_key_equals $temp "-p $keys_s" "24$HOUR-48$HOUR 24$HOUR 48$HOUR"
grib_check_key_equals $temp "-p $keys_i" "48 24 48"
grib_check_key_equals $temp "-p $keys_d" "48 24 48"
${tools_dir}/grib_set -s forecastTime=25,indicatorOfUnitOfTimeRange=h,lengthOfTimeRange=1,indicatorOfUnitForTimeRange=D $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "25 h 1 D"
grib_check_key_equals $temp "-p $keys__" "25$HOUR-49$HOUR 25$HOUR 49$HOUR"
grib_check_key_equals $temp "-p $keys_s" "25$HOUR-49$HOUR 25$HOUR 49$HOUR"
grib_check_key_equals $temp "-p $keys_i" "49 25 49"
grib_check_key_equals $temp "-p $keys_d" "49 25 49"
${tools_dir}/grib_set -s forecastTime=45,indicatorOfUnitOfTimeRange=m,lengthOfTimeRange=15,indicatorOfUnitForTimeRange=m $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "45 m 15 m"
grib_check_key_equals $temp "-p $keys__" "45m-60m 45m 60m"
#grib_check_key_equals $temp "-p $keys_s" "45-60 45 60"
grib_check_key_equals $temp "-p $keys_s" "45m-60m 45m 60m"
grib_check_key_equals $temp "-p $keys_i" "60 45 60"
grib_check_key_equals $temp "-p $keys_d" "60 45 60"
${tools_dir}/grib_set -s forecastTime=60,indicatorOfUnitOfTimeRange=m,lengthOfTimeRange=2,indicatorOfUnitForTimeRange=h $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "60 m 2 h"
grib_check_key_equals $temp "-p $keys__" "1$HOUR-3$HOUR 1$HOUR 3$HOUR"
grib_check_key_equals $temp "-p $keys_s" "1$HOUR-3$HOUR 1$HOUR 3$HOUR"
grib_check_key_equals $temp "-p $keys_i" "3 1 3"
grib_check_key_equals $temp "-p $keys_d" "3 1 3"
${tools_dir}/grib_set -s forecastTime=18,indicatorOfUnitOfTimeRange=h,lengthOfTimeRange=6,indicatorOfUnitForTimeRange=h $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "18 h 6 h"
grib_check_key_equals $temp "-p $keys__" "18$HOUR-24$HOUR 18$HOUR 24$HOUR"
grib_check_key_equals $temp "-p $keys_s" "18$HOUR-24$HOUR 18$HOUR 24$HOUR"
grib_check_key_equals $temp "-p $keys_i" "24 18 24"
grib_check_key_equals $temp "-p $keys_d" "24 18 24"
${tools_dir}/grib_set -s forecastTime=1080,indicatorOfUnitOfTimeRange=m,lengthOfTimeRange=360,indicatorOfUnitForTimeRange=m $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "1080 m 360 m"
grib_check_key_equals $temp "-p $keys__" "18$HOUR-24$HOUR 18$HOUR 24$HOUR"
grib_check_key_equals $temp "-p $keys_s" "18$HOUR-24$HOUR 18$HOUR 24$HOUR"
grib_check_key_equals $temp "-p $keys_i" "24 18 24"
grib_check_key_equals $temp "-p $keys_d" "24 18 24"
${tools_dir}/grib_set -s forecastTime=1080,indicatorOfUnitOfTimeRange=m,lengthOfTimeRange=6,indicatorOfUnitForTimeRange=h $fn $temp
grib_check_key_equals $temp "-p $low_level_keys" "1080 m 6 h"
grib_check_key_equals $temp "-p $keys__" "18$HOUR-24$HOUR 18$HOUR 24$HOUR"
grib_check_key_equals $temp "-p $keys_s" "18$HOUR-24$HOUR 18$HOUR 24$HOUR"
grib_check_key_equals $temp "-p $keys_i" "24 18 24"
grib_check_key_equals $temp "-p $keys_d" "24 18 24"
cat >$tempFilt<<EOF
set stepUnits="m"; print "[step]";
EOF
${tools_dir}/grib_filter $tempFilt $data_dir/constant_field.grib2
cat >$tempFilt<<EOF
set stepUnits="s"; print "[step]";
EOF
${tools_dir}/grib_filter $tempFilt $data_dir/constant_field.grib2
rm -f $temp $temp2 $tempFilt
#~/build/eccodes/bin/grib_ls -m /perm/maro/referenceGRIBfiles4MTG2testing/grib1+2_operational_and_rd/151145_s2_enfo_cf_o2d_zos_2002_prod_ecmf_glob.grib2
#~/build/eccodes/bin/grib_ls -m /perm/maro/referenceGRIBfiles4MTG2testing/grib1+2_operational_and_rd/240023_ce_efas_fc_sfc_dis06_2022_0001_ecmf_lisflood.grib2