Ejemplo n.º 1
0
def set_data(source, grib):
    """
    Sets the actual data of a grib message.
    """
    var_name = get_varible_name(source)
    # treat masked arrays differently
    if isinstance(source[var_name].values, np.ma.core.MaskedArray):
        gribapi.grib_set(grib, "bitmapPresent", 1)
        # use the missing value from the masked array as default
        missing_value = source[var_name].values.get_fill_value()
        # but give the netCDF specified missing value preference
        missing_value = source[var_name].attrs.get('missing_value',
                                                        missing_value)
        gribapi.grib_set_double(grib, "missingValue",
                                float(missing_value))
        data = source[var_name].values.filled()
    else:
        gribapi.grib_set_double(grib, "missingValue", 9999)
        data = source[var_name].values[:]
    gribapi.grib_set_long(grib, "bitsPerValue", 12)
    #gribapi.grib_set_long(grib, "bitsPerValueAndRepack", 12)
    gribapi.grib_set_long(grib, "decimalPrecision", 2)
    gribapi.grib_set_long(grib, "decimalScaleFactor", 2)
    #gribapi.grib_set_long(grib, "binaryScaleFactor", 0)
    gribapi.grib_set_long(grib, "dataRepresentationType", 0)
    # get the grib code for the variable
    code = reverse_codes[conv.to_grib1[var_name]]
    _, grib_unit = codes[code]
    # default to the grib default unit
    unit = source[var_name].attrs.get('units', grib_unit)
    mult = 1.
    if not unit == grib_unit:
        mult = units._speed[unit] / units._speed[grib_unit]
    # add the data
    gribapi.grib_set_double_array(grib, "values", mult * data.flatten())
Ejemplo n.º 2
0
def set_time_range(time_coord, grib):
    """
    Set the time range keys in the specified message
    based on the bounds of the provided time coordinate.

    """
    if len(time_coord.points) != 1:
        msg = 'Expected length one time coordinate, got {} points'
        raise ValueError(msg.format(len(time_coord.points)))

    if time_coord.nbounds != 2:
        msg = 'Expected time coordinate with two bounds, got {} bounds'
        raise ValueError(msg.format(time_coord.nbounds))

    # Set type to hours and convert period to this unit.
    gribapi.grib_set(grib, "indicatorOfUnitForTimeRange",
                     _TIME_RANGE_UNITS['hours'])
    hours_since_units = cf_units.Unit('hours since epoch',
                                      calendar=time_coord.units.calendar)
    start_hours, end_hours = time_coord.units.convert(time_coord.bounds[0],
                                                      hours_since_units)
    # Cast from np.float to Python int. The lengthOfTimeRange key is a
    # 4 byte integer so we cast to highlight truncation of any floating
    # point value. The grib_api will do the cast from float to int, but it
    # cannot handle numpy floats.
    time_range_in_hours = round(end_hours) - round(
        start_hours)  # required for NCMRWF
    integer_hours = round(time_range_in_hours)  # required for NCMRWF
    if integer_hours != time_range_in_hours:
        msg = 'Truncating floating point lengthOfTimeRange {} to ' \
              'integer value {}'
        warnings.warn(msg.format(time_range_in_hours, integer_hours))
    gribapi.grib_set(grib, "lengthOfTimeRange", integer_hours)
Ejemplo n.º 3
0
def tweak_grib_msg(variable, cube):
    """ Iterate through cubes and pair with grib messages (using reference table) before saving"""

    for cube, grib_message in iris_grib.save_pairs_from_cube(cube):

        gribapi.grib_set_long(grib_message, "centre",
                              ec_grib_centre[variable][0][1])
        gribapi.grib_set_long(grib_message, "discipline",
                              ec_grib_centre[variable][1][1])
        gribapi.grib_set_long(grib_message, "parameterCategory",
                              ec_grib_centre[variable][1][2])
        gribapi.grib_set_long(grib_message, "parameterNumber",
                              ec_grib_centre[variable][1][3])

        gribapi.grib_set_long(grib_message, "edition",
                              ec_grib_centre[variable][2][1])
        gribapi.grib_set(grib_message, "shortNameECMF",
                         ec_grib_centre[variable][3][1])
        gribapi.grib_set_long(grib_message, "indicatorOfParameter",
                              ec_grib_centre[variable][4][1])

        if (variable == 'V1' or variable == 'V2' or variable == 'V3'
                or variable == 'V4' or variable == 'T1' or variable == 'T2'
                or variable == 'T3' or variable == 'T4'):

            gribapi.grib_set_long(grib_message, "table2Version",
                                  ec_grib_centre[variable][5][1])
            gribapi.grib_set_long(grib_message, "topLevel",
                                  ec_grib_centre[variable][6][1])
            gribapi.grib_set_long(grib_message, "bottomLevel",
                                  ec_grib_centre[variable][7][1])

    yield grib_message
Ejemplo n.º 4
0
def set_time_range(time_coord, grib):
    """
    Set the time range keys in the specified message
    based on the bounds of the provided time coordinate.

    """
    if len(time_coord.points) != 1:
        msg = 'Expected length one time coordinate, got {} points'
        raise ValueError(msg.format(len(time_coord.points)))

    if time_coord.nbounds != 2:
        msg = 'Expected time coordinate with two bounds, got {} bounds'
        raise ValueError(msg.format(time_coord.nbounds))

    # Set type to hours and convert period to this unit.
    gribapi.grib_set(grib, "indicatorOfUnitForTimeRange",
                     _TIME_RANGE_UNITS['hours'])
    hours_since_units = cf_units.Unit('hours since epoch',
                                      calendar=time_coord.units.calendar)
    start_hours, end_hours = time_coord.units.convert(time_coord.bounds[0],
                                                      hours_since_units)
    # Cast from np.float to Python int. The lengthOfTimeRange key is a
    # 4 byte integer so we cast to highlight truncation of any floating
    # point value. The grib_api will do the cast from float to int, but it
    # cannot handle numpy floats.
    time_range_in_hours = end_hours - start_hours
    integer_hours = int(time_range_in_hours)
    if integer_hours != time_range_in_hours:
        msg = 'Truncating floating point lengthOfTimeRange {} to ' \
              'integer value {}'
        warnings.warn(msg.format(time_range_in_hours, integer_hours))
    gribapi.grib_set(grib, "lengthOfTimeRange", integer_hours)
Ejemplo n.º 5
0
def product_definition_template_8(cube, grib):
    """
    Set keys within the provided grib message based on Product
    Definition Template 4.8.

    Template 4.8 is used to represent an aggregation over a time
    interval.

    """
    gribapi.grib_set(grib, "productDefinitionTemplateNumber", 8)
    _product_definition_template_8_and_11(cube, grib)
Ejemplo n.º 6
0
def ensure_set_int32_value(grib, key, value):
    """
    Ensure the workaround function :func:`fixup_int32_as_uint32` is applied as
    necessary to problem keys.

    """
    try:
        gribapi.grib_set(grib, key, value)
    except gribapi.GribInternalError:
        value = fixup_int32_as_uint32(value)
        gribapi.grib_set(grib, key, value)
Ejemplo n.º 7
0
def product_definition_template_8(cube, grib):
    """
    Set keys within the provided grib message based on Product
    Definition Template 4.8.

    Template 4.8 is used to represent an aggregation over a time
    interval.

    """
    gribapi.grib_set(grib, "productDefinitionTemplateNumber", 8)
    _product_definition_template_8_10_and_11(cube, grib)
Ejemplo n.º 8
0
def ensure_set_int32_value(grib, key, value):
    """
    Ensure the workaround function :func:`fixup_int32_as_uint32` is applied as
    necessary to problem keys.

    """
    try:
        gribapi.grib_set(grib, key, value)
    except gribapi.GribInternalError:
        value = fixup_int32_as_uint32(value)
        gribapi.grib_set(grib, key, value)
Ejemplo n.º 9
0
    def __setitem__(self, key, value):
        """
        Set value associated with key.

        If the object is iterable,
        """
        # Passed value is iterable and not string
        if (isinstance(value, collections.Iterable)
                and not isinstance(value, basestring)):
            gribapi.grib_set_array(self.gid, key, value)
        else:
            gribapi.grib_set(self.gid, key, value)
Ejemplo n.º 10
0
def product_definition_template_11(cube, grib):
    """
    Set keys within the provided grib message based on Product
    Definition Template 4.11.

    Template 4.11 is used to represent an aggregation over a time
    interval for an ensemble member.

    """
    gribapi.grib_set(grib, "productDefinitionTemplateNumber", 11)
    set_ensemble(cube, grib)
    _product_definition_template_8_10_and_11(cube, grib)
Ejemplo n.º 11
0
    def __setitem__(self, key, value):
        """
        Set value associated with key.

        If the object is iterable,
        """
        # Passed value is iterable and not string
        if (isinstance(value, collections.Iterable) and not
                isinstance(value, basestring)):
            gribapi.grib_set_array(self.gid, key, value)
        else:
            gribapi.grib_set(self.gid, key, value)
Ejemplo n.º 12
0
def product_definition_template_11(cube, grib):
    """
    Set keys within the provided grib message based on Product
    Definition Template 4.11.

    Template 4.11 is used to represent an aggregation over a time
    interval for an ensemble member.

    """
    gribapi.grib_set(grib, "productDefinitionTemplateNumber", 11)
    set_ensemble(cube, grib)
    _product_definition_template_8_10_and_11(cube, grib)
Ejemplo n.º 13
0
def product_definition_template_1(cube, grib):
    """
    Set keys within the provided grib message based on Product
    Definition Template 4.1.

    Template 4.1 is used to represent an individual ensemble forecast, control
    and perturbed, at a horizontal level or in a horizontal layer at a point
    in time.

    """
    gribapi.grib_set(grib, "productDefinitionTemplateNumber", 1)
    product_definition_template_common(cube, grib)
    set_ensemble(cube, grib)
Ejemplo n.º 14
0
def product_definition_template_1(cube, grib):
    """
    Set keys within the provided grib message based on Product
    Definition Template 4.1.

    Template 4.1 is used to represent an individual ensemble forecast, control
    and perturbed, at a horizontal level or in a horizontal layer at a point
    in time.

    """
    gribapi.grib_set(grib, "productDefinitionTemplateNumber", 1)
    product_definition_template_common(cube, grib)
    set_ensemble(cube, grib)
Ejemplo n.º 15
0
def product_definition_template_40(cube, grib):
    """
    Set keys within the provided grib message based on Product
    Definition Template 4.40.

    Template 4.40 is used to represent an analysis or forecast at a horizontal
    level or in a horizontal layer at a point in time for atmospheric chemical
    constituents.

    """
    gribapi.grib_set(grib, "productDefinitionTemplateNumber", 40)
    product_definition_template_common(cube, grib)
    constituent_type = cube.attributes['WMO_constituent_type']
    gribapi.grib_set(grib, "constituentType", constituent_type)
Ejemplo n.º 16
0
def product_definition_template_40(cube, grib):
    """
    Set keys within the provided grib message based on Product
    Definition Template 4.40.

    Template 4.40 is used to represent an analysis or forecast at a horizontal
    level or in a horizontal layer at a point in time for atmospheric chemical
    constituents.

    """
    gribapi.grib_set(grib, "productDefinitionTemplateNumber", 40)
    product_definition_template_common(cube, grib)
    constituent_type = cube.attributes['WMO_constituent_type']
    gribapi.grib_set(grib, "constituentType", constituent_type)
Ejemplo n.º 17
0
def product_definition_template_10(cube, grib):
    """
    Set keys within the provided grib message based on Product Definition
    Template 4.10.

    Template 4.10 is used to represent a percentile forecast over a time
    interval.

    """
    gribapi.grib_set(grib, "productDefinitionTemplateNumber", 10)
    if not (cube.coords('percentile_over_time') and
            len(cube.coord('percentile_over_time').points) == 1):
        raise ValueError("A cube 'percentile_over_time' coordinate with one "
                         "point is required, but not present.")
    gribapi.grib_set(grib, "percentileValue",
                     int(cube.coord('percentile_over_time').points[0]))
    _product_definition_template_8_10_and_11(cube, grib)
Ejemplo n.º 18
0
def product_definition_template_10(cube, grib):
    """
    Set keys within the provided grib message based on Product Definition
    Template 4.10.

    Template 4.10 is used to represent a percentile forecast over a time
    interval.

    """
    gribapi.grib_set(grib, "productDefinitionTemplateNumber", 10)
    if not (cube.coords('percentile_over_time') and
            len(cube.coord('percentile_over_time').points) == 1):
        raise ValueError("A cube 'percentile_over_time' coordinate with one "
                         "point is required, but not present.")
    gribapi.grib_set(grib, "percentileValue",
                     int(cube.coord('percentile_over_time').points[0]))
    _product_definition_template_8_10_and_11(cube, grib)
Ejemplo n.º 19
0
    def __setitem__(self, key, value):
        """
        Set value associated with key.

        If the object is iterable,
        """
        # Alternative implemented (TODO: evaluate)
        # if gribapi.grib_get_size(self.gid, key) > 1:
        #     gribapi.grib_set_array(self.gid, key, value)
        # else:
        #     gribapi.grib_set(self.gid, key, value)

        # Passed value is iterable and not string
        if (isinstance(value, collections.Iterable) and not
                isinstance(value, basestring)):
            gribapi.grib_set_array(self.gid, key, value)
        else:
            gribapi.grib_set(self.gid, key, value)
Ejemplo n.º 20
0
def dx_dy(x_coord, y_coord, grib):
    x_step = regular_step(x_coord)
    y_step = regular_step(y_coord)
    # Set x and y step.  For degrees, this is encoded as an integer:
    # 1 * 10^6 * floating point value.
    # WMO Manual on Codes regulation 92.1.6
    if x_coord.units == 'degrees':
        gribapi.grib_set(grib, "iDirectionIncrement",
                         round(1e6 * float(abs(x_step))))
    else:
        raise ValueError('X coordinate must be in degrees, not {}'
                         '.'.format(x_coord.units))
    if y_coord.units == 'degrees':
        gribapi.grib_set(grib, "jDirectionIncrement",
                         round(1e6 * float(abs(y_step))))
    else:
        raise ValueError('Y coordinate must be in degrees, not {}'
                         '.'.format(y_coord.units))
Ejemplo n.º 21
0
def set_forecast_time(cube, grib):
    """
    Set the forecast time keys based on the forecast_period coordinate. In
    the absence of a forecast_period and forecast_reference_time,
    the forecast time is set to zero.

    """
    try:
        fp_coord = cube.coord("forecast_period")
    except iris.exceptions.CoordinateNotFoundError:
        fp_coord = None

    if fp_coord is not None:
        _, _, fp, grib_time_code = _non_missing_forecast_period(cube)
    else:
        _, _, fp, grib_time_code = _missing_forecast_period(cube)

    gribapi.grib_set(grib, "indicatorOfUnitOfTimeRange", grib_time_code)
    gribapi.grib_set(grib, "forecastTime", fp)
Ejemplo n.º 22
0
def set_forecast_time(cube, grib):
    """
    Set the forecast time keys based on the forecast_period coordinate. In
    the absence of a forecast_period and forecast_reference_time,
    the forecast time is set to zero.

    """
    try:
        fp_coord = cube.coord("forecast_period")
    except iris.exceptions.CoordinateNotFoundError:
        fp_coord = None

    if fp_coord is not None:
        _, _, fp, grib_time_code = _non_missing_forecast_period(cube)
    else:
        _, _, fp, grib_time_code = _missing_forecast_period(cube)

    gribapi.grib_set(grib, "indicatorOfUnitOfTimeRange", grib_time_code)
    gribapi.grib_set(grib, "forecastTime", fp)
Ejemplo n.º 23
0
def data_section(cube, grib):
    # Masked data?
    if isinstance(cube.data, ma.core.MaskedArray):
        # What missing value shall we use?
        if not np.isnan(cube.data.fill_value):
            # Use the data's fill value.
            fill_value = float(cube.data.fill_value)
        else:
            # We can't use the data's fill value if it's NaN,
            # the GRIB API doesn't like it.
            # Calculate an MDI outside the data range.
            min, max = cube.data.min(), cube.data.max()
            fill_value = min - (max - min) * 0.1
        # Prepare the unmaksed data array, using fill_value as the MDI.
        data = cube.data.filled(fill_value)
    else:
        fill_value = None
        data = cube.data

    # units scaling
    grib2_info = gptx.cf_phenom_to_grib2_info(cube.standard_name,
                                              cube.long_name)
    if grib2_info is None:
        # for now, just allow this
        warnings.warn('Unable to determine Grib2 parameter code for cube.\n'
                      'Message data may not be correctly scaled.')
    else:
        if cube.units != grib2_info.units:
            data = cube.units.convert(data, grib2_info.units)
            if fill_value is not None:
                fill_value = cube.units.convert(fill_value, grib2_info.units)

    if fill_value is None:
        # Disable missing values in the grib message.
        gribapi.grib_set(grib, "bitmapPresent", 0)
    else:
        # Enable missing values in the grib message.
        gribapi.grib_set(grib, "bitmapPresent", 1)
        gribapi.grib_set_double(grib, "missingValue", fill_value)
    gribapi.grib_set_double_array(grib, "values", data.flatten())
Ejemplo n.º 24
0
def data_section(cube, grib):
    # Masked data?
    if isinstance(cube.data, ma.core.MaskedArray):
        # What missing value shall we use?
        if not np.isnan(cube.data.fill_value):
            # Use the data's fill value.
            fill_value = float(cube.data.fill_value)
        else:
            # We can't use the data's fill value if it's NaN,
            # the GRIB API doesn't like it.
            # Calculate an MDI outside the data range.
            min, max = cube.data.min(), cube.data.max()
            fill_value = min - (max - min) * 0.1
        # Prepare the unmaksed data array, using fill_value as the MDI.
        data = cube.data.filled(fill_value)
    else:
        fill_value = None
        data = cube.data

    # units scaling
    grib2_info = gptx.cf_phenom_to_grib2_info(cube.standard_name,
                                              cube.long_name)
    if grib2_info is None:
        # for now, just allow this
        warnings.warn('Unable to determine Grib2 parameter code for cube.\n'
                      'Message data may not be correctly scaled.')
    else:
        if cube.units != grib2_info.units:
            data = cube.units.convert(data, grib2_info.units)
            if fill_value is not None:
                fill_value = cube.units.convert(fill_value, grib2_info.units)

    if fill_value is None:
        # Disable missing values in the grib message.
        gribapi.grib_set(grib, "bitmapPresent", 0)
    else:
        # Enable missing values in the grib message.
        gribapi.grib_set(grib, "bitmapPresent", 1)
        gribapi.grib_set_double(grib, "missingValue", fill_value)
    gribapi.grib_set_double_array(grib, "values", data.flatten())
Ejemplo n.º 25
0
def grid_definition_template_5(cube, grib):
    """
    Set keys within the provided grib message based on
    Grid Definition Template 3.5.

    Template 3.5 is used to represent "variable resolution rotated
    latitude/longitude".
    The coordinates are irregularly spaced, rotated latitudes and longitudes.

    """
    # NOTE: we must set Ni=Nj=1 before establishing the template.
    # Without this, setting "gridDefinitionTemplateNumber" = 5 causes an
    # immediate error.
    # See: https://software.ecmwf.int/issues/browse/SUP-1095
    # This is acceptable, as the subsequent call to 'horizontal_grid_common'
    # will set these to the correct horizontal dimensions
    # (by calling 'grid_dims').
    gribapi.grib_set(grib, "Ni", 1)
    gribapi.grib_set(grib, "Nj", 1)
    gribapi.grib_set(grib, "gridDefinitionTemplateNumber", 5)

    # Record details of the rotated coordinate system.
    rotated_pole(cube, grib)
    # Encode the lat/lon points.
    horizontal_grid_common(cube, grib)
    latlon_points_irregular(cube, grib)
Ejemplo n.º 26
0
def grid_definition_template_5(cube, grib):
    """
    Set keys within the provided grib message based on
    Grid Definition Template 3.5.

    Template 3.5 is used to represent "variable resolution rotated
    latitude/longitude".
    The coordinates are irregularly spaced, rotated latitudes and longitudes.

    """
    # NOTE: we must set Ni=Nj=1 before establishing the template.
    # Without this, setting "gridDefinitionTemplateNumber" = 5 causes an
    # immediate error.
    # See: https://software.ecmwf.int/issues/browse/SUP-1095
    # This is acceptable, as the subsequent call to 'horizontal_grid_common'
    # will set these to the correct horizontal dimensions
    # (by calling 'grid_dims').
    gribapi.grib_set(grib, "Ni", 1)
    gribapi.grib_set(grib, "Nj", 1)
    gribapi.grib_set(grib, "gridDefinitionTemplateNumber", 5)

    # Record details of the rotated coordinate system.
    rotated_pole(cube, grib)
    # Encode the lat/lon points.
    horizontal_grid_common(cube, grib)
    latlon_points_irregular(cube, grib)
Ejemplo n.º 27
0
def data(cube, grib):
    # mdi
    if isinstance(cube.data, ma.core.MaskedArray):
        gribapi.grib_set(grib, "bitmapPresent", 1)
        gribapi.grib_set_double(grib, "missingValue",
                                float(cube.data.fill_value))
        data = cube.data.filled()
    else:
        gribapi.grib_set_double(grib, "missingValue", float(-1e9))
        data = cube.data

    # units scaling
    grib2_info = gptx.cf_phenom_to_grib2_info(cube.standard_name,
                                              cube.long_name)
    if grib2_info is None:
        # for now, just allow this
        warnings.warn('Unable to determine Grib2 parameter code for cube.\n'
                      'Message data may not be correctly scaled.')
    else:
        if cube.units != grib2_info.units:
            data = cube.units.convert(data, grib2_info.units)

    # values
    gribapi.grib_set_double_array(grib, "values", data.flatten())
Ejemplo n.º 28
0
def latlon_points_irregular(cube, grib):
    y_coord = cube.coord(dimensions=[0])
    x_coord = cube.coord(dimensions=[1])

    # Distinguish between true-north and grid-oriented vectors.
    is_grid_wind = cube.name() in ('x_wind', 'y_wind', 'grid_eastward_wind',
                                   'grid_northward_wind')
    # Encode in bit "5" of 'resolutionAndComponentFlags' (other bits unused).
    component_flags = 0
    if is_grid_wind:
        component_flags |= 2**_RESOLUTION_AND_COMPONENTS_GRID_WINDS_BIT
    gribapi.grib_set(grib, 'resolutionAndComponentFlags', component_flags)

    # Record the  X and Y coordinate values.
    # NOTE: there is currently a bug in the gribapi which means that the size
    # of the longitudes array does not equal 'Nj', as it should.
    # See : https://software.ecmwf.int/issues/browse/SUP-1096
    # So, this only works at present if the x and y dimensions are **equal**.
    lon_values = x_coord.points / _DEFAULT_DEGREES_UNITS
    lat_values = y_coord.points / _DEFAULT_DEGREES_UNITS
    gribapi.grib_set_array(grib, 'longitudes',
                           np.array(np.round(lon_values), dtype=np.int64))
    gribapi.grib_set_array(grib, 'latitudes',
                           np.array(np.round(lat_values), dtype=np.int64))
Ejemplo n.º 29
0
def latlon_points_irregular(cube, grib):
    y_coord = cube.coord(dimensions=[0])
    x_coord = cube.coord(dimensions=[1])

    # Distinguish between true-north and grid-oriented vectors.
    is_grid_wind = cube.name() in ('x_wind', 'y_wind', 'grid_eastward_wind',
                                   'grid_northward_wind')
    # Encode in bit "5" of 'resolutionAndComponentFlags' (other bits unused).
    component_flags = 0
    if is_grid_wind:
        component_flags |= 2 ** _RESOLUTION_AND_COMPONENTS_GRID_WINDS_BIT
    gribapi.grib_set(grib, 'resolutionAndComponentFlags', component_flags)

    # Record the  X and Y coordinate values.
    # NOTE: there is currently a bug in the gribapi which means that the size
    # of the longitudes array does not equal 'Nj', as it should.
    # See : https://software.ecmwf.int/issues/browse/SUP-1096
    # So, this only works at present if the x and y dimensions are **equal**.
    lon_values = x_coord.points / _DEFAULT_DEGREES_UNITS
    lat_values = y_coord.points / _DEFAULT_DEGREES_UNITS
    gribapi.grib_set_array(grib, 'longitudes',
                           np.array(np.round(lon_values), dtype=np.int64))
    gribapi.grib_set_array(grib, 'latitudes',
                           np.array(np.round(lat_values), dtype=np.int64))
Ejemplo n.º 30
0
def data(cube, grib):
    # mdi
    if isinstance(cube.data, ma.core.MaskedArray):
        gribapi.grib_set(grib, "bitmapPresent", 1)
        gribapi.grib_set_double(grib, "missingValue",
                                float(cube.data.fill_value))
        data = cube.data.filled()
    else:
        gribapi.grib_set_double(grib, "missingValue", float(-1e9))
        data = cube.data

    # units scaling
    grib2_info = gptx.cf_phenom_to_grib2_info(cube.standard_name,
                                              cube.long_name)
    if grib2_info is None:
        # for now, just allow this
        warnings.warn('Unable to determine Grib2 parameter code for cube.\n'
                      'Message data may not be correctly scaled.')
    else:
        if cube.units != grib2_info.units:
            data = cube.units.convert(data, grib2_info.units)

    # values
    gribapi.grib_set_double_array(grib, "values", data.flatten())
Ejemplo n.º 31
0
def product_definition_template_11(cube, grib):
    """
    Set keys within the provided grib message based on Product
    Definition Template 4.8.

    Template 4.8 is used to represent an aggregation over a time
    interval.

    """
    gribapi.grib_set(grib, "productDefinitionTemplateNumber", 11)
    if not (cube.coords('realization')
            and len(cube.coord('realization').points) == 1):
        raise ValueError("A cube 'realization' coordinate with one"
                         "point is required, but not present")
    gribapi.grib_set(grib, "perturbationNumber",
                     int(cube.coord('realization').points[0]))
    # no encoding at present in Iris, set to missing
    gribapi.grib_set(grib, "numberOfForecastsInEnsemble", 255)
    gribapi.grib_set(grib, "typeOfEnsembleForecast", 255)
    _product_definition_template_8_and_11(cube, grib)
Ejemplo n.º 32
0
def product_definition_template_11(cube, grib):
    """
    Set keys within the provided grib message based on Product
    Definition Template 4.11.

    Template 4.11 is used to represent an aggregation over a time
    interval for an ensemble member.

    """
    gribapi.grib_set(grib, "productDefinitionTemplateNumber", 11)
    if not (cube.coords('realization') and
            len(cube.coord('realization').points) == 1):
        raise ValueError("A cube 'realization' coordinate with one"
                         "point is required, but not present")
    gribapi.grib_set(grib, "perturbationNumber",
                     int(cube.coord('realization').points[0]))
    # no encoding at present in iris-grib, set to missing
    gribapi.grib_set(grib, "numberOfForecastsInEnsemble", 255)
    gribapi.grib_set(grib, "typeOfEnsembleForecast", 255)
    _product_definition_template_8_10_and_11(cube, grib)
Ejemplo n.º 33
0
def set_ensemble(cube, grib):
    """
    Set keys in the provided grib based message relating to ensemble
    information.

    """
    if not (cube.coords('realization')
            and len(cube.coord('realization').points) == 1):
        raise ValueError("A cube 'realization' coordinate with one "
                         "point is required, but not present")
    gribapi.grib_set(grib, "perturbationNumber",
                     int(cube.coord('realization').points[0]))
    # no encoding at present in iris, set to missing
    gribapi.grib_set(grib, "numberOfForecastsInEnsemble", 255)
    gribapi.grib_set(grib, "typeOfEnsembleForecast", 255)
Ejemplo n.º 34
0
def set_ensemble(cube, grib):
    """
    Set keys in the provided grib based message relating to ensemble
    information.

    """
    if not (cube.coords('realization') and
            len(cube.coord('realization').points) == 1):
        raise ValueError("A cube 'realization' coordinate with one "
                         "point is required, but not present")
    gribapi.grib_set(grib, "perturbationNumber",
                     int(cube.coord('realization').points[0]))
    # no encoding at present in iris, set to missing
    gribapi.grib_set(grib, "numberOfForecastsInEnsemble", 255)
    gribapi.grib_set(grib, "typeOfEnsembleForecast", 255)
Ejemplo n.º 35
0
def product_definition_template_common(cube, grib):
    """
    Set keys within the provided grib message that are common across
    all of the supported product definition templates.

    """
    set_discipline_and_parameter(cube, grib)

    # Various missing values.
    gribapi.grib_set(grib, "typeOfGeneratingProcess", 255)
    gribapi.grib_set(grib, "backgroundProcess", 255)
    gribapi.grib_set(grib, "generatingProcessIdentifier", 255)

    # Generic time handling.
    set_forecast_time(cube, grib)

    # Handle vertical coords.
    set_fixed_surfaces(cube, grib)
Ejemplo n.º 36
0
def product_definition_template_common(cube, grib):
    """
    Set keys within the provided grib message that are common across
    all of the supported product definition templates.

    """
    set_discipline_and_parameter(cube, grib)

    # Various missing values.
    gribapi.grib_set(grib, "typeOfGeneratingProcess", 255)
    gribapi.grib_set(grib, "backgroundProcess", 255)
    gribapi.grib_set(grib, "generatingProcessIdentifier", 255)

    # Generic time handling.
    set_forecast_time(cube, grib)

    # Handle vertical coords.
    set_fixed_surfaces(cube, grib)
Ejemplo n.º 37
0
def product_definition_template_common(cube, grib):
    """
    Set keys within the provided grib message that are common across
    all of the supported product definition templates.

    """
    set_discipline_and_parameter(cube, grib)

    # http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_table4-3.shtml 2 would be applicable even for analysis (since analysis also short forecast)
    gribapi.grib_set(grib, "typeOfGeneratingProcess", 2) # required for NCMRWF
    gribapi.grib_set(grib, "backgroundProcess", 255) # set missing values.
    # http://www.nco.ncep.noaa.gov/pmb/docs/on388/tablea.html 96 would be more appropriate 
    gribapi.grib_set(grib, "generatingProcessIdentifier", 96)  # required for NCMRWF

    # Generic time handling.
    set_forecast_time(cube, grib)

    # Handle vertical coords.
    set_fixed_surfaces(cube, grib)
Ejemplo n.º 38
0
def product_definition_template_common(cube, grib):
    """
    Set keys within the provided grib message that are common across
    all of the supported product definition templates.

    """
    set_discipline_and_parameter(cube, grib)

    # http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_table4-3.shtml 2 would be applicable even for analysis (since analysis also short forecast)
    gribapi.grib_set(grib, "typeOfGeneratingProcess", 2)  # required for NCMRWF
    gribapi.grib_set(grib, "backgroundProcess", 255)  # set missing values.
    # http://www.nco.ncep.noaa.gov/pmb/docs/on388/tablea.html 96 would be more appropriate
    gribapi.grib_set(grib, "generatingProcessIdentifier",
                     96)  # required for NCMRWF

    # Generic time handling.
    set_forecast_time(cube, grib)

    # Handle vertical coords.
    set_fixed_surfaces(cube, grib)
Ejemplo n.º 39
0
def rotated_pole(cube, grib):
    # Grib encoding of a rotated pole coordinate system.
    cs = cube.coord(dimensions=[0]).coord_system

    if cs.north_pole_grid_longitude != 0.0:
        raise iris.exceptions.TranslationError(
            'Grib save does not yet support Rotated-pole coordinates with '
            'a rotated prime meridian.')
# XXX Pending #1125
#    gribapi.grib_set_double(grib, "latitudeOfSouthernPoleInDegrees",
#                            float(cs.n_pole.latitude))
#    gribapi.grib_set_double(grib, "longitudeOfSouthernPoleInDegrees",
#                            float(cs.n_pole.longitude))
#    gribapi.grib_set_double(grib, "angleOfRotationInDegrees", 0)
# WORKAROUND
    latitude = cs.grid_north_pole_latitude / _DEFAULT_DEGREES_UNITS
    longitude = (((cs.grid_north_pole_longitude + 180) % 360) /
                 _DEFAULT_DEGREES_UNITS)
    gribapi.grib_set(grib, "latitudeOfSouthernPole", - int(round(latitude)))
    gribapi.grib_set(grib, "longitudeOfSouthernPole", int(round(longitude)))
    gribapi.grib_set(grib, "angleOfRotation", 0)
Ejemplo n.º 40
0
def rotated_pole(cube, grib):
    # Grib encoding of a rotated pole coordinate system.
    cs = cube.coord(dimensions=[0]).coord_system

    if cs.north_pole_grid_longitude != 0.0:
        raise iris.exceptions.TranslationError(
            'Grib save does not yet support Rotated-pole coordinates with '
            'a rotated prime meridian.')
# XXX Pending #1125
#    gribapi.grib_set_double(grib, "latitudeOfSouthernPoleInDegrees",
#                            float(cs.n_pole.latitude))
#    gribapi.grib_set_double(grib, "longitudeOfSouthernPoleInDegrees",
#                            float(cs.n_pole.longitude))
#    gribapi.grib_set_double(grib, "angleOfRotationInDegrees", 0)
# WORKAROUND
    latitude = cs.grid_north_pole_latitude / _DEFAULT_DEGREES_UNITS
    longitude = (((cs.grid_north_pole_longitude + 180) % 360) /
                 _DEFAULT_DEGREES_UNITS)
    gribapi.grib_set(grib, "latitudeOfSouthernPole", - int(round(latitude)))
    gribapi.grib_set(grib, "longitudeOfSouthernPole", int(round(longitude)))
    gribapi.grib_set(grib, "angleOfRotation", 0)
Ejemplo n.º 41
0
def set_time_increment(cell_method, grib):
    """
    Set the time increment keys in the specified message
    based on the provided cell method.

    """
    # Type of time increment, e.g incrementing forecast period, incrementing
    # forecast reference time, etc. Set to missing, but we could use the
    # cell method coord to infer a value (see code table 4.11).
    gribapi.grib_set(grib, "typeOfTimeIncrement", 255)

    # Default values for the time increment value and units type.
    inc = 0
    units_type = 255
    # Attempt to determine time increment from cell method intervals string.
    intervals = cell_method.intervals
    if intervals is not None and len(intervals) == 1:
        interval, = intervals
        try:
            inc, units = interval.split()
            inc = float(inc)
            if units in ('hr', 'hour', 'hours'):
                units_type = _TIME_RANGE_UNITS['hours']
            else:
                raise ValueError('Unable to parse units of interval')
        except ValueError:
            # Problem interpreting the interval string.
            inc = 0
            units_type = 255
        else:
            # Cast to int as timeIncrement key is a 4 byte integer.
            integer_inc = int(inc)
            if integer_inc != inc:
                warnings.warn('Truncating floating point timeIncrement {} to '
                              'integer value {}'.format(inc, integer_inc))
            inc = integer_inc

    gribapi.grib_set(grib, "indicatorOfUnitForTimeIncrement", units_type)
    gribapi.grib_set(grib, "timeIncrement", inc)
Ejemplo n.º 42
0
def set_time_increment(cell_method, grib):
    """
    Set the time increment keys in the specified message
    based on the provided cell method.

    """
    # Type of time increment, e.g incrementing forecast period, incrementing
    # forecast reference time, etc. Set to missing, but we could use the
    # cell method coord to infer a value (see code table 4.11).
    gribapi.grib_set(grib, "typeOfTimeIncrement", 255)

    # Default values for the time increment value and units type.
    inc = 0
    units_type = 255
    # Attempt to determine time increment from cell method intervals string.
    intervals = cell_method.intervals
    if intervals is not None and len(intervals) == 1:
        interval, = intervals
        try:
            inc, units = interval.split()
            inc = float(inc)
            if units in ('hr', 'hour', 'hours'):
                units_type = _TIME_RANGE_UNITS['hours']
            else:
                raise ValueError('Unable to parse units of interval')
        except ValueError:
            # Problem interpreting the interval string.
            inc = 0
            units_type = 255
        else:
            # Cast to int as timeIncrement key is a 4 byte integer.
            integer_inc = round(inc)  # required for NCMRWF
            if integer_inc != inc:
                warnings.warn('Truncating floating point timeIncrement {} to '
                              'integer value {}'.format(inc, integer_inc))
            inc = integer_inc

    gribapi.grib_set(grib, "indicatorOfUnitForTimeIncrement", units_type)
    gribapi.grib_set(grib, "timeIncrement", inc)
Ejemplo n.º 43
0
def dx_dy(x_coord, y_coord, grib):
    x_step = regular_step(x_coord)
    y_step = regular_step(y_coord)
    gribapi.grib_set(grib, "DxInDegrees", float(abs(x_step)))
    gribapi.grib_set(grib, "DyInDegrees", float(abs(y_step)))
Ejemplo n.º 44
0
def grid_definition_template_30(cube, grib):
    """
    Set keys within the provided grib message based on
    Grid Definition Template 3.30.

    Template 3.30 is used to represent a Lambert Conformal grid.

    """

    gribapi.grib_set(grib, "gridDefinitionTemplateNumber", 30)

    # Retrieve some information from the cube.
    y_coord = cube.coord(dimensions=[0])
    x_coord = cube.coord(dimensions=[1])
    cs = y_coord.coord_system

    # Normalise the coordinate values to millimetres - the resolution
    # used in the GRIB message.
    y_mm = points_in_unit(y_coord, 'mm')
    x_mm = points_in_unit(x_coord, 'mm')

    # Encode the horizontal points.

    # NB. Since we're already in millimetres, our tolerance for
    # discrepancy in the differences is 1.
    try:
        x_step = step(x_mm, atol=1)
        y_step = step(y_mm, atol=1)
    except ValueError:
        msg = ('Irregular coordinates not supported for Lambert ' 'Conformal.')
        raise iris.exceptions.TranslationError(msg)
    gribapi.grib_set(grib, 'Dx', abs(x_step))
    gribapi.grib_set(grib, 'Dy', abs(y_step))

    horizontal_grid_common(cube, grib, xy=True)

    # Transform first point into geographic CS
    geog = cs.ellipsoid if cs.ellipsoid is not None else GeogCS(1)
    first_x, first_y = geog.as_cartopy_crs().transform_point(
        x_coord.points[0], y_coord.points[0], cs.as_cartopy_crs())
    first_x = first_x % 360
    central_lon = cs.central_lon % 360

    gribapi.grib_set(grib, "latitudeOfFirstGridPoint",
                     int(np.round(first_y * 1e6)))
    gribapi.grib_set(grib, "longitudeOfFirstGridPoint",
                     int(np.round(first_x * 1e6)))
    gribapi.grib_set(grib, "LaD", cs.central_lat * 1e6)
    gribapi.grib_set(grib, "LoV", central_lon * 1e6)
    latin1, latin2 = cs.secant_latitudes
    gribapi.grib_set(grib, "Latin1", latin1 * 1e6)
    gribapi.grib_set(grib, "Latin2", latin2 * 1e6)
    gribapi.grib_set(grib, 'resolutionAndComponentFlags',
                     0x1 << _RESOLUTION_AND_COMPONENTS_GRID_WINDS_BIT)

    # Which pole are the parallels closest to? That is the direction
    # that the cone converges.
    poliest_sec = latin1 if abs(latin1) > abs(latin2) else latin2
    centre_flag = 0x0 if poliest_sec > 0 else 0x1
    gribapi.grib_set(grib, 'projectionCentreFlag', centre_flag)
    gribapi.grib_set(grib, "latitudeOfSouthernPole", 0)
    gribapi.grib_set(grib, "longitudeOfSouthernPole", 0)
Ejemplo n.º 45
0
def _product_definition_template_8_10_and_11(cube, grib):
    """
    Set keys within the provided grib message based on common aspects of
    Product Definition Templates 4.8 and 4.11.

    Templates 4.8  and 4.11 are used to represent aggregations over a time
    interval.

    """
    product_definition_template_common(cube, grib)

    # Check for time coordinate.
    time_coord = cube.coord('time')

    if len(time_coord.points) != 1:
        msg = 'Expected length one time coordinate, got {} points'
        raise ValueError(msg.format(time_coord.points))

    if time_coord.nbounds != 2:
        msg = 'Expected time coordinate with two bounds, got {} bounds'
        raise ValueError(msg.format(time_coord.nbounds))

    # Extract the datetime-like object corresponding to the end of
    # the overall processing interval.
    end = time_coord.units.num2date(time_coord.bounds[0, -1])

    # Set the associated keys for the end of the interval (octets 35-41
    # in section 4).
    gribapi.grib_set(grib, "yearOfEndOfOverallTimeInterval", end.year)
    gribapi.grib_set(grib, "monthOfEndOfOverallTimeInterval", end.month)
    gribapi.grib_set(grib, "dayOfEndOfOverallTimeInterval", end.day)
    gribapi.grib_set(grib, "hourOfEndOfOverallTimeInterval", end.hour)
    gribapi.grib_set(grib, "minuteOfEndOfOverallTimeInterval", end.minute)
    gribapi.grib_set(grib, "secondOfEndOfOverallTimeInterval", end.second)

    # Only one time range specification. If there were a series of aggregations
    # (e.g. the mean of an accumulation) one might set this to a higher value,
    # but we currently only handle a single time related cell method.
    gribapi.grib_set(grib, "numberOfTimeRange", 1)
    gribapi.grib_set(grib, "numberOfMissingInStatisticalProcess", 0)

    # Period over which statistical processing is performed.
    set_time_range(time_coord, grib)

    # Check that there is one and only one cell method related to the
    # time coord.
    if cube.cell_methods:
        time_cell_methods = [
            cell_method for cell_method in cube.cell_methods if 'time' in
            cell_method.coord_names]
        if not time_cell_methods:
            raise ValueError("Expected a cell method with a coordinate name "
                             "of 'time'")
        if len(time_cell_methods) > 1:
            raise ValueError("Cannot handle multiple 'time' cell methods")
        cell_method, = time_cell_methods

        if len(cell_method.coord_names) > 1:
            raise ValueError("Cannot handle multiple coordinate names in "
                             "the time related cell method. Expected "
                             "('time',), got {!r}".format(
                                 cell_method.coord_names))

        # Type of statistical process (see code table 4.10)
        statistic_type = _STATISTIC_TYPE_NAMES.get(cell_method.method, 255)
        gribapi.grib_set(grib, "typeOfStatisticalProcessing", statistic_type)

        # Time increment i.e. interval of cell method (if any)
        set_time_increment(cell_method, grib)
Ejemplo n.º 46
0
def _product_definition_template_8_and_11(cube, grib):
    """
    Set keys within the provided grib message based on common aspects of
    Product Definition Templates 4.8 and 4.11.

    Templates 4.8  and 4.11 are used to represent aggregations over a time
    interval.

    """
    product_definition_template_common(cube, grib)

    # Check for time coordinate.
    time_coord = cube.coord('time')

    if len(time_coord.points) != 1:
        msg = 'Expected length one time coordinate, got {} points'
        raise ValueError(msg.format(time_coord.points))

    if time_coord.nbounds != 2:
        msg = 'Expected time coordinate with two bounds, got {} bounds'
        raise ValueError(msg.format(time_coord.nbounds))

    # Check that there is one and only one cell method related to the
    # time coord.
    time_cell_methods = [
        cell_method for cell_method in cube.cell_methods
        if 'time' in cell_method.coord_names
    ]
    if not time_cell_methods:
        raise ValueError("Expected a cell method with a coordinate name "
                         "of 'time'")
    if len(time_cell_methods) > 1:
        raise ValueError("Cannot handle multiple 'time' cell methods")
    cell_method, = time_cell_methods

    if len(cell_method.coord_names) > 1:
        raise ValueError("Cannot handle multiple coordinate names in "
                         "the time related cell method. Expected ('time',), "
                         "got {!r}".format(cell_method.coord_names))

    # Extract the datetime-like object corresponding to the end of
    # the overall processing interval.
    end = time_coord.units.num2date(time_coord.bounds[0, -1])

    # Set the associated keys for the end of the interval (octets 35-41
    # in section 4).
    gribapi.grib_set(grib, "yearOfEndOfOverallTimeInterval", end.year)
    gribapi.grib_set(grib, "monthOfEndOfOverallTimeInterval", end.month)
    gribapi.grib_set(grib, "dayOfEndOfOverallTimeInterval", end.day)
    gribapi.grib_set(grib, "hourOfEndOfOverallTimeInterval", end.hour)
    gribapi.grib_set(grib, "minuteOfEndOfOverallTimeInterval", end.minute)
    gribapi.grib_set(grib, "secondOfEndOfOverallTimeInterval", end.second)

    # Only one time range specification. If there were a series of aggregations
    # (e.g. the mean of an accumulation) one might set this to a higher value,
    # but we currently only handle a single time related cell method.
    gribapi.grib_set(grib, "numberOfTimeRange", 1)
    gribapi.grib_set(grib, "numberOfMissingInStatisticalProcess", 0)

    # Type of statistical process (see code table 4.10)
    statistic_type = _STATISTIC_TYPE_NAMES.get(cell_method.method, 255)
    gribapi.grib_set(grib, "typeOfStatisticalProcessing", statistic_type)

    # Period over which statistical processing is performed.
    set_time_range(time_coord, grib)

    # Time increment i.e. interval of cell method (if any)
    set_time_increment(cell_method, grib)
Ejemplo n.º 47
0
def dx_dy(x_coord, y_coord, grib):
    x_step = regular_step(x_coord)
    y_step = regular_step(y_coord)
    gribapi.grib_set(grib, "DxInDegrees", float(abs(x_step)))
    gribapi.grib_set(grib, "DyInDegrees", float(abs(y_step)))
Ejemplo n.º 48
0
def set_fixed_surfaces(cube, grib):

    # Look for something we can export
    v_coord = grib_v_code = output_unit = None

    # pressure
    if cube.coords("air_pressure") or cube.coords("pressure"):
        grib_v_code = 100
        output_unit = cf_units.Unit("Pa")
        v_coord = (cube.coords("air_pressure") or cube.coords("pressure"))[0]

    # altitude
    elif cube.coords("altitude"):
        grib_v_code = 102
        output_unit = cf_units.Unit("m")
        v_coord = cube.coord("altitude")

    # height
    elif cube.coords("height"):
        grib_v_code = 103
        output_unit = cf_units.Unit("m")
        v_coord = cube.coord("height")

    # depth
    elif cube.coords("depth"):
        grib_v_code = 106  # required for NCMRWF NCUM 10.2
        output_unit = cf_units.Unit("m")
        v_coord = cube.coord("depth")

    # depth_below_land_surface
    elif cube.coords("depth_below_land_surface"):
        grib_v_code = 106  # required for NCMRWF NCUM 8.5
        output_unit = cf_units.Unit("m")
        v_coord = cube.coord("depth_below_land_surface")

    elif cube.coords("air_potential_temperature"):
        grib_v_code = 107
        output_unit = cf_units.Unit('K')
        v_coord = cube.coord("air_potential_temperature")

    # unknown / absent
    else:
        # check for *ANY* height coords at all...
        v_coords = cube.coords(axis='z')
        if v_coords:
            # There are vertical coordinate(s), but we don't understand them...
            v_coords_str = ' ,'.join(
                ["'{}'".format(c.name()) for c in v_coords])
            raise iris.exceptions.TranslationError(
                'The vertical-axis coordinate(s) ({}) '
                'are not recognised or handled.'.format(v_coords_str))

    # What did we find?
    if v_coord is None:
        # No vertical coordinate: record as 'surface' level (levelType=1).
        # NOTE: may *not* be truly correct, but seems to be common practice.
        # Still under investigation :
        # See https://github.com/SciTools/iris/issues/519
        gribapi.grib_set(grib, "typeOfFirstFixedSurface", 1)
        gribapi.grib_set(grib, "scaleFactorOfFirstFixedSurface", 0)
        gribapi.grib_set(grib, "scaledValueOfFirstFixedSurface", 0)
        # Set secondary surface = 'missing'.
        gribapi.grib_set(grib, "typeOfSecondFixedSurface", -1)
        gribapi.grib_set(grib, "scaleFactorOfSecondFixedSurface", 255)
        gribapi.grib_set(grib, "scaledValueOfSecondFixedSurface", -1)
    elif not v_coord.has_bounds():
        # No second surface
        output_v = v_coord.units.convert(v_coord.points[0], output_unit)
        if output_v - abs(output_v):
            warnings.warn("Vertical level encoding problem: scaling required.")
        output_v = round(
            output_v
        )  # we must round it, so that WRF/TIGGE able understand! # required for NCMRWF

        gribapi.grib_set(grib, "typeOfFirstFixedSurface", grib_v_code)
        gribapi.grib_set(grib, "scaleFactorOfFirstFixedSurface", 0)
        gribapi.grib_set(grib, "scaledValueOfFirstFixedSurface", output_v)
        gribapi.grib_set(grib, "typeOfSecondFixedSurface", -1)
        gribapi.grib_set(grib, "scaleFactorOfSecondFixedSurface", 255)
        gribapi.grib_set(grib, "scaledValueOfSecondFixedSurface", -1)
    else:
        # bounded : set lower+upper surfaces
        output_v = v_coord.units.convert(v_coord.bounds[0], output_unit)
        if output_v[0] - abs(output_v[0]) or output_v[1] - abs(output_v[1]):
            warnings.warn("Vertical level encoding problem: scaling required.")
        gribapi.grib_set(grib, "typeOfFirstFixedSurface", grib_v_code)
        gribapi.grib_set(grib, "typeOfSecondFixedSurface", grib_v_code)
        gribapi.grib_set(grib, "scaleFactorOfFirstFixedSurface", 0)
        gribapi.grib_set(grib, "scaleFactorOfSecondFixedSurface", 0)
        gribapi.grib_set(grib, "scaledValueOfFirstFixedSurface",
                         int(output_v[0]))
        gribapi.grib_set(grib, "scaledValueOfSecondFixedSurface",
                         int(output_v[1]))
Ejemplo n.º 49
0
def grid_definition_template_12(cube, grib):
    """
    Set keys within the provided grib message based on
    Grid Definition Template 3.12.

    Template 3.12 is used to represent a Transverse Mercator grid.

    """
    gribapi.grib_set(grib, "gridDefinitionTemplateNumber", 12)

    # Retrieve some information from the cube.
    y_coord = cube.coord(dimensions=[0])
    x_coord = cube.coord(dimensions=[1])
    cs = y_coord.coord_system

    # Normalise the coordinate values to centimetres - the resolution
    # used in the GRIB message.
    def points_in_cm(coord):
        points = coord.units.convert(coord.points, 'cm')
        points = np.around(points).astype(int)
        return points

    y_cm = points_in_cm(y_coord)
    x_cm = points_in_cm(x_coord)

    # Set some keys specific to GDT12.
    # Encode the horizontal points.

    # NB. Since we're already in centimetres, our tolerance for
    # discrepancy in the differences is 1.
    def step(points):
        diffs = points[1:] - points[:-1]
        mean_diff = np.mean(diffs).astype(points.dtype)
        if not np.allclose(diffs, mean_diff, atol=1):
            msg = ('Irregular coordinates not supported for transverse '
                   'Mercator.')
            raise iris.exceptions.TranslationError(msg)
        return int(mean_diff)

    gribapi.grib_set(grib, 'Di', abs(step(x_cm)))
    gribapi.grib_set(grib, 'Dj', abs(step(y_cm)))
    horizontal_grid_common(cube, grib)

    # GRIBAPI expects unsigned ints in X1, X2, Y1, Y2 but it should accept
    # signed ints, so work around this.
    # See https://software.ecmwf.int/issues/browse/SUP-1101
    ensure_set_int32_value(grib, 'Y1', int(y_cm[0]))
    ensure_set_int32_value(grib, 'Y2', int(y_cm[-1]))
    ensure_set_int32_value(grib, 'X1', int(x_cm[0]))
    ensure_set_int32_value(grib, 'X2', int(x_cm[-1]))

    # Lat and lon of reference point are measured in millionths of a degree.
    gribapi.grib_set(grib, "latitudeOfReferencePoint",
                     cs.latitude_of_projection_origin / _DEFAULT_DEGREES_UNITS)
    gribapi.grib_set(grib, "longitudeOfReferencePoint",
                     cs.longitude_of_central_meridian / _DEFAULT_DEGREES_UNITS)

    # Convert a value in metres into the closest integer number of
    # centimetres.
    def m_to_cm(value):
        return int(round(value * 100))

    # False easting and false northing are measured in units of (10^-2)m.
    gribapi.grib_set(grib, 'XR', m_to_cm(cs.false_easting))
    gribapi.grib_set(grib, 'YR', m_to_cm(cs.false_northing))

    # GRIBAPI expects a signed int for scaleFactorAtReferencePoint
    # but it should accept a float, so work around this.
    # See https://software.ecmwf.int/issues/browse/SUP-1100
    value = cs.scale_factor_at_central_meridian
    key_type = gribapi.grib_get_native_type(grib,
                                            "scaleFactorAtReferencePoint")
    if key_type is not float:
        value = fixup_float32_as_int32(value)
    gribapi.grib_set(grib, "scaleFactorAtReferencePoint", value)
Ejemplo n.º 50
0
def set_discipline_and_parameter(cube, grib):
    # NOTE: for now, can match by *either* standard_name or long_name.
    # This allows workarounds for data with no identified standard_name.
    grib2_info = gptx.cf_phenom_to_grib2_info(cube.standard_name,
                                              cube.long_name)
    if grib2_info is not None:
        gribapi.grib_set(grib, "discipline", grib2_info.discipline)
        gribapi.grib_set(grib, "parameterCategory", grib2_info.category)
        gribapi.grib_set(grib, "parameterNumber", grib2_info.number)
    else:
        gribapi.grib_set(grib, "discipline", 255)
        gribapi.grib_set(grib, "parameterCategory", 255)
        gribapi.grib_set(grib, "parameterNumber", 255)
        warnings.warn('Unable to determine Grib2 parameter code for cube.\n'
                      'discipline, parameterCategory and parameterNumber '
                      'have been set to "missing".')
Ejemplo n.º 51
0
def set_fixed_surfaces(cube, grib):

    # Look for something we can export
    v_coord = grib_v_code = output_unit = None

    # pressure
    if cube.coords("air_pressure") or cube.coords("pressure"):
        grib_v_code = 100
        output_unit = cf_units.Unit("Pa")
        v_coord = (cube.coords("air_pressure") or cube.coords("pressure"))[0]

    # altitude
    elif cube.coords("altitude"):
        grib_v_code = 102
        output_unit = cf_units.Unit("m")
        v_coord = cube.coord("altitude")

    # height
    elif cube.coords("height"):
        grib_v_code = 103
        output_unit = cf_units.Unit("m")
        v_coord = cube.coord("height")

    elif cube.coords("air_potential_temperature"):
        grib_v_code = 107
        output_unit = cf_units.Unit('K')
        v_coord = cube.coord("air_potential_temperature")

    # unknown / absent
    else:
        # check for *ANY* height coords at all...
        v_coords = cube.coords(axis='z')
        if v_coords:
            # There are vertical coordinate(s), but we don't understand them...
            v_coords_str = ' ,'.join(["'{}'".format(c.name())
                                      for c in v_coords])
            raise iris.exceptions.TranslationError(
                'The vertical-axis coordinate(s) ({}) '
                'are not recognised or handled.'.format(v_coords_str))

    # What did we find?
    if v_coord is None:
        # No vertical coordinate: record as 'surface' level (levelType=1).
        # NOTE: may *not* be truly correct, but seems to be common practice.
        # Still under investigation :
        # See https://github.com/SciTools/iris/issues/519
        gribapi.grib_set(grib, "typeOfFirstFixedSurface", 1)
        gribapi.grib_set(grib, "scaleFactorOfFirstFixedSurface", 0)
        gribapi.grib_set(grib, "scaledValueOfFirstFixedSurface", 0)
        # Set secondary surface = 'missing'.
        gribapi.grib_set(grib, "typeOfSecondFixedSurface", -1)
        gribapi.grib_set(grib, "scaleFactorOfSecondFixedSurface", 255)
        gribapi.grib_set(grib, "scaledValueOfSecondFixedSurface", -1)
    elif not v_coord.has_bounds():
        # No second surface
        output_v = v_coord.units.convert(v_coord.points[0], output_unit)
        if output_v - abs(output_v):
            warnings.warn("Vertical level encoding problem: scaling required.")
        output_v = int(output_v)

        gribapi.grib_set(grib, "typeOfFirstFixedSurface", grib_v_code)
        gribapi.grib_set(grib, "scaleFactorOfFirstFixedSurface", 0)
        gribapi.grib_set(grib, "scaledValueOfFirstFixedSurface", output_v)
        gribapi.grib_set(grib, "typeOfSecondFixedSurface", -1)
        gribapi.grib_set(grib, "scaleFactorOfSecondFixedSurface", 255)
        gribapi.grib_set(grib, "scaledValueOfSecondFixedSurface", -1)
    else:
        # bounded : set lower+upper surfaces
        output_v = v_coord.units.convert(v_coord.bounds[0], output_unit)
        if output_v[0] - abs(output_v[0]) or output_v[1] - abs(output_v[1]):
            warnings.warn("Vertical level encoding problem: scaling required.")
        gribapi.grib_set(grib, "typeOfFirstFixedSurface", grib_v_code)
        gribapi.grib_set(grib, "typeOfSecondFixedSurface", grib_v_code)
        gribapi.grib_set(grib, "scaleFactorOfFirstFixedSurface", 0)
        gribapi.grib_set(grib, "scaleFactorOfSecondFixedSurface", 0)
        gribapi.grib_set(grib, "scaledValueOfFirstFixedSurface",
                         int(output_v[0]))
        gribapi.grib_set(grib, "scaledValueOfSecondFixedSurface",
                         int(output_v[1]))
Ejemplo n.º 52
0
def grid_definition_template_12(cube, grib):
    """
    Set keys within the provided grib message based on
    Grid Definition Template 3.12.

    Template 3.12 is used to represent a Transverse Mercator grid.

    """
    gribapi.grib_set(grib, "gridDefinitionTemplateNumber", 12)

    # Retrieve some information from the cube.
    y_coord = cube.coord(dimensions=[0])
    x_coord = cube.coord(dimensions=[1])
    cs = y_coord.coord_system

    # Normalise the coordinate values to centimetres - the resolution
    # used in the GRIB message.
    def points_in_cm(coord):
        points = coord.units.convert(coord.points, 'cm')
        points = np.around(points).astype(int)
        return points
    y_cm = points_in_cm(y_coord)
    x_cm = points_in_cm(x_coord)

    # Set some keys specific to GDT12.
    # Encode the horizontal points.

    # NB. Since we're already in centimetres, our tolerance for
    # discrepancy in the differences is 1.
    def step(points):
        diffs = points[1:] - points[:-1]
        mean_diff = np.mean(diffs).astype(points.dtype)
        if not np.allclose(diffs, mean_diff, atol=1):
            msg = ('Irregular coordinates not supported for transverse '
                   'Mercator.')
            raise iris.exceptions.TranslationError(msg)
        return int(mean_diff)

    gribapi.grib_set(grib, 'Di', abs(step(x_cm)))
    gribapi.grib_set(grib, 'Dj', abs(step(y_cm)))
    horizontal_grid_common(cube, grib)

    # GRIBAPI expects unsigned ints in X1, X2, Y1, Y2 but it should accept
    # signed ints, so work around this.
    # See https://software.ecmwf.int/issues/browse/SUP-1101
    ensure_set_int32_value(grib, 'Y1', int(y_cm[0]))
    ensure_set_int32_value(grib, 'Y2', int(y_cm[-1]))
    ensure_set_int32_value(grib, 'X1', int(x_cm[0]))
    ensure_set_int32_value(grib, 'X2', int(x_cm[-1]))

    # Lat and lon of reference point are measured in millionths of a degree.
    gribapi.grib_set(grib, "latitudeOfReferencePoint",
                     cs.latitude_of_projection_origin / _DEFAULT_DEGREES_UNITS)
    gribapi.grib_set(grib, "longitudeOfReferencePoint",
                     cs.longitude_of_central_meridian / _DEFAULT_DEGREES_UNITS)

    # Convert a value in metres into the closest integer number of
    # centimetres.
    def m_to_cm(value):
        return int(round(value * 100))

    # False easting and false northing are measured in units of (10^-2)m.
    gribapi.grib_set(grib, 'XR', m_to_cm(cs.false_easting))
    gribapi.grib_set(grib, 'YR', m_to_cm(cs.false_northing))

    # GRIBAPI expects a signed int for scaleFactorAtReferencePoint
    # but it should accept a float, so work around this.
    # See https://software.ecmwf.int/issues/browse/SUP-1100
    value = cs.scale_factor_at_central_meridian
    key_type = gribapi.grib_get_native_type(grib,
                                            "scaleFactorAtReferencePoint")
    if key_type is not float:
        value = fixup_float32_as_int32(value)
    gribapi.grib_set(grib, "scaleFactorAtReferencePoint", value)
Ejemplo n.º 53
0
def _regularise(grib_message):
    """
    Transform a reduced grid to a regular grid using interpolation.

    Uses 1d linear interpolation at constant latitude to make the grid
    regular. If the longitude dimension is circular then this is taken
    into account by the interpolation. If the longitude dimension is not
    circular then extrapolation is allowed to make sure all end regular
    grid points get a value. In practice this extrapolation is likely to
    be minimal.

    """
    # Make sure to read any missing values as NaN.
    gribapi.grib_set_double(grib_message, "missingValue", np.nan)

    # Get full longitude values, these describe the longitude value of
    # *every* point in the grid, they are not 1d monotonic coordinates.
    lons = gribapi.grib_get_double_array(grib_message, "longitudes")

    # Compute the new longitude coordinate for the regular grid.
    new_nx = max(gribapi.grib_get_long_array(grib_message, "pl"))
    new_x_step = (max(lons) - min(lons)) / (new_nx - 1)
    if gribapi.grib_get_long(grib_message, "iScansNegatively"):
        new_x_step *= -1

    new_lons = np.arange(new_nx) * new_x_step + lons[0]
    # Get full latitude and data values, these describe the latitude and
    # data values of *every* point in the grid, they are not 1d monotonic
    # coordinates.
    lats = gribapi.grib_get_double_array(grib_message, "latitudes")
    values = gribapi.grib_get_double_array(grib_message, "values")

    # Retrieve the distinct latitudes from the GRIB message. GRIBAPI docs
    # don't specify if these points are guaranteed to be oriented correctly so
    # the safe option is to sort them into ascending (south-to-north) order
    # and then reverse the order if necessary.
    new_lats = gribapi.grib_get_double_array(grib_message, "distinctLatitudes")
    new_lats.sort()
    if not gribapi.grib_get_long(grib_message, "jScansPositively"):
        new_lats = new_lats[::-1]
    ny = new_lats.shape[0]

    # Use 1d linear interpolation along latitude circles to regularise the
    # reduced data.
    cyclic = _longitude_is_cyclic(new_lons)
    new_values = np.empty([ny, new_nx], dtype=values.dtype)
    for ilat, lat in enumerate(new_lats):
        idx = np.where(lats == lat)
        llons = lons[idx]
        vvalues = values[idx]
        if cyclic:
            # For cyclic data we insert dummy points at each end to ensure
            # we can interpolate to all output longitudes using pure
            # interpolation.
            cgap = (360 - llons[-1] - llons[0])
            llons = np.concatenate(
                (llons[0:1] - cgap, llons, llons[-1:] + cgap))
            vvalues = np.concatenate(
                (vvalues[-1:], vvalues, vvalues[0:1]))
            fixed_latitude_interpolator = scipy.interpolate.interp1d(
                llons, vvalues)
        else:
            # Allow extrapolation for non-cyclic data sets to ensure we can
            # interpolate to all output longitudes.
            fixed_latitude_interpolator = Linear1dExtrapolator(
                scipy.interpolate.interp1d(llons, vvalues))
        new_values[ilat] = fixed_latitude_interpolator(new_lons)
    new_values = new_values.flatten()

    # Set flags for the regularised data.
    if np.isnan(new_values).any():
        # Account for any missing data.
        gribapi.grib_set_double(grib_message, "missingValue", np.inf)
        gribapi.grib_set(grib_message, "bitmapPresent", 1)
        new_values = np.where(np.isnan(new_values), np.inf, new_values)

    gribapi.grib_set_long(grib_message, "Nx", int(new_nx))
    gribapi.grib_set_double(grib_message,
                            "iDirectionIncrementInDegrees", float(new_x_step))
    gribapi.grib_set_double_array(grib_message, "values", new_values)
    gribapi.grib_set_long(grib_message, "jPointsAreConsecutive", 0)
    gribapi.grib_set_long(grib_message, "PLPresent", 0)
Ejemplo n.º 54
0
def set_discipline_and_parameter(cube, grib):
    # NOTE: for now, can match by *either* standard_name or long_name.
    # This allows workarounds for data with no identified standard_name.
    grib2_info = gptx.cf_phenom_to_grib2_info(cube.standard_name,
                                              cube.long_name)
    if grib2_info is not None:
        gribapi.grib_set(grib, "discipline", grib2_info.discipline)
        gribapi.grib_set(grib, "parameterCategory", grib2_info.category)
        gribapi.grib_set(grib, "parameterNumber", grib2_info.number)
    else:
        gribapi.grib_set(grib, "discipline", 255)
        gribapi.grib_set(grib, "parameterCategory", 255)
        gribapi.grib_set(grib, "parameterNumber", 255)
        warnings.warn('Unable to determine Grib2 parameter code for cube.\n'
                      'discipline, parameterCategory and parameterNumber '
                      'have been set to "missing".')
Ejemplo n.º 55
0
def _regularise(grib_message):
    """
    Transform a reduced grid to a regular grid using interpolation.

    Uses 1d linear interpolation at constant latitude to make the grid
    regular. If the longitude dimension is circular then this is taken
    into account by the interpolation. If the longitude dimension is not
    circular then extrapolation is allowed to make sure all end regular
    grid points get a value. In practice this extrapolation is likely to
    be minimal.

    """
    # Make sure to read any missing values as NaN.
    gribapi.grib_set_double(grib_message, "missingValue", np.nan)

    # Get full longitude values, these describe the longitude value of
    # *every* point in the grid, they are not 1d monotonic coordinates.
    lons = gribapi.grib_get_double_array(grib_message, "longitudes")

    # Compute the new longitude coordinate for the regular grid.
    new_nx = max(gribapi.grib_get_long_array(grib_message, "pl"))
    new_x_step = (max(lons) - min(lons)) / (new_nx - 1)
    if gribapi.grib_get_long(grib_message, "iScansNegatively"):
        new_x_step *= -1

    new_lons = np.arange(new_nx) * new_x_step + lons[0]
    # Get full latitude and data values, these describe the latitude and
    # data values of *every* point in the grid, they are not 1d monotonic
    # coordinates.
    lats = gribapi.grib_get_double_array(grib_message, "latitudes")
    values = gribapi.grib_get_double_array(grib_message, "values")

    # Retrieve the distinct latitudes from the GRIB message. GRIBAPI docs
    # don't specify if these points are guaranteed to be oriented correctly so
    # the safe option is to sort them into ascending (south-to-north) order
    # and then reverse the order if necessary.
    new_lats = gribapi.grib_get_double_array(grib_message, "distinctLatitudes")
    new_lats.sort()
    if not gribapi.grib_get_long(grib_message, "jScansPositively"):
        new_lats = new_lats[::-1]
    ny = new_lats.shape[0]

    # Use 1d linear interpolation along latitude circles to regularise the
    # reduced data.
    cyclic = _longitude_is_cyclic(new_lons)
    new_values = np.empty([ny, new_nx], dtype=values.dtype)
    for ilat, lat in enumerate(new_lats):
        idx = np.where(lats == lat)
        llons = lons[idx]
        vvalues = values[idx]
        if cyclic:
            # For cyclic data we insert dummy points at each end to ensure
            # we can interpolate to all output longitudes using pure
            # interpolation.
            cgap = (360 - llons[-1] - llons[0])
            llons = np.concatenate(
                (llons[0:1] - cgap, llons, llons[-1:] + cgap))
            vvalues = np.concatenate(
                (vvalues[-1:], vvalues, vvalues[0:1]))
            fixed_latitude_interpolator = scipy.interpolate.interp1d(
                llons, vvalues)
        else:
            # Allow extrapolation for non-cyclic data sets to ensure we can
            # interpolate to all output longitudes.
            fixed_latitude_interpolator = Linear1dExtrapolator(
                scipy.interpolate.interp1d(llons, vvalues))
        new_values[ilat] = fixed_latitude_interpolator(new_lons)
    new_values = new_values.flatten()

    # Set flags for the regularised data.
    if np.isnan(new_values).any():
        # Account for any missing data.
        gribapi.grib_set_double(grib_message, "missingValue", np.inf)
        gribapi.grib_set(grib_message, "bitmapPresent", 1)
        new_values = np.where(np.isnan(new_values), np.inf, new_values)

    gribapi.grib_set_long(grib_message, "Nx", int(new_nx))
    gribapi.grib_set_double(grib_message,
                            "iDirectionIncrementInDegrees", float(new_x_step))
    gribapi.grib_set_double_array(grib_message, "values", new_values)
    gribapi.grib_set_long(grib_message, "jPointsAreConsecutive", 0)
    gribapi.grib_set_long(grib_message, "PLPresent", 0)
Ejemplo n.º 56
0
def grid_definition_template_30(cube, grib):
    """
    Set keys within the provided grib message based on
    Grid Definition Template 3.30.

    Template 3.30 is used to represent a Lambert Conformal grid.

    """

    gribapi.grib_set(grib, "gridDefinitionTemplateNumber", 30)

    # Retrieve some information from the cube.
    y_coord = cube.coord(dimensions=[0])
    x_coord = cube.coord(dimensions=[1])
    cs = y_coord.coord_system

    # Normalise the coordinate values to millimetres - the resolution
    # used in the GRIB message.
    y_mm = points_in_unit(y_coord, 'mm')
    x_mm = points_in_unit(x_coord, 'mm')

    # Encode the horizontal points.

    # NB. Since we're already in millimetres, our tolerance for
    # discrepancy in the differences is 1.
    try:
        x_step = step(x_mm, atol=1)
        y_step = step(y_mm, atol=1)
    except ValueError:
        msg = ('Irregular coordinates not supported for Lambert '
               'Conformal.')
        raise iris.exceptions.TranslationError(msg)
    gribapi.grib_set(grib, 'Dx', abs(x_step))
    gribapi.grib_set(grib, 'Dy', abs(y_step))

    horizontal_grid_common(cube, grib, xy=True)

    # Transform first point into geographic CS
    geog = cs.ellipsoid if cs.ellipsoid is not None else GeogCS(1)
    first_x, first_y = geog.as_cartopy_crs().transform_point(
        x_coord.points[0],
        y_coord.points[0],
        cs.as_cartopy_crs())
    first_x = first_x % 360
    central_lon = cs.central_lon % 360

    gribapi.grib_set(grib, "latitudeOfFirstGridPoint",
                     int(np.round(first_y * 1e6)))
    gribapi.grib_set(grib, "longitudeOfFirstGridPoint",
                     int(np.round(first_x * 1e6)))
    gribapi.grib_set(grib, "LaD", cs.central_lat * 1e6)
    gribapi.grib_set(grib, "LoV", central_lon * 1e6)
    latin1, latin2 = cs.secant_latitudes
    gribapi.grib_set(grib, "Latin1", latin1 * 1e6)
    gribapi.grib_set(grib, "Latin2", latin2 * 1e6)
    gribapi.grib_set(grib, 'resolutionAndComponentFlags',
                     0x1 << _RESOLUTION_AND_COMPONENTS_GRID_WINDS_BIT)

    # Which pole are the parallels closest to? That is the direction
    # that the cone converges.
    poliest_sec = latin1 if abs(latin1) > abs(latin2) else latin2
    centre_flag = 0x0 if poliest_sec > 0 else 0x1
    gribapi.grib_set(grib, 'projectionCentreFlag', centre_flag)
    gribapi.grib_set(grib, "latitudeOfSouthernPole", 0)
    gribapi.grib_set(grib, "longitudeOfSouthernPole", 0)
Ejemplo n.º 57
0
 def set_field(self, name, value):
     gribapi.grib_set(self.record, name, value)