-
Notifications
You must be signed in to change notification settings - Fork 286
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Eccodes adopt #2607
Eccodes adopt #2607
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -158,11 +158,21 @@ def _unscale(v, f): | |
|
||
if isinstance(value, Iterable) or isinstance(factor, Iterable): | ||
def _masker(item): | ||
result = ma.masked_equal(item, _MDI) | ||
# This is a small work around for an edge case, which is not | ||
# evident in any of our sample GRIB2 messages, where an array | ||
# of header elements contains missing values. | ||
# iris.fileformats.grib.message returns these as None, but they | ||
# are wanted as a numerical masked array, so a temporary mdi | ||
# value is used, selected from a legacy implementation of iris, | ||
# to construct the masked array. The valure is transient, only in | ||
# scope for this function. | ||
numerical_mdi = 2 ** 32 - 1 | ||
item = [numerical_mdi if i is None else i for i in item] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't this be better done as:
? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe not. If this were implemented, then a mixed type underlying array would be created, with floats and None, so NumPy would have to make an object array. This would be a behaviour change, which I don't think is desired. |
||
result = ma.masked_equal(item, numerical_mdi) | ||
if ma.count_masked(result): | ||
# Circumvent downstream NumPy "RuntimeWarning" | ||
# of "overflow encountered in power" in _unscale | ||
# for data containing _MDI. | ||
# for data containing _MDI. Remove transient _MDI value. | ||
result.data[result.mask] = 0 | ||
return result | ||
value = _masker(value) | ||
|
@@ -177,30 +187,8 @@ def _masker(item): | |
return result | ||
|
||
|
||
# Regulations 92.1.4 and 92.1.5. | ||
_MDI = 2 ** 32 - 1 | ||
# Note: | ||
# 1. Integer "on-disk" values (aka. coded keys) in GRIB messages: | ||
# - Are 8-, 16-, or 32-bit. | ||
# - Are either signed or unsigned, with signed values stored as | ||
# sign-and-magnitude (*not* twos-complement). | ||
# - Use all bits set to indicate a missing value (MDI). | ||
# 2. Irrespective of the on-disk form, the ECMWF GRIB API *always*: | ||
# - Returns values as 64-bit signed integers, either as native | ||
# Python 'int' or numpy 'int64'. | ||
# - Returns missing values as 2**32 - 1, but not all keys are | ||
# defined as supporting missing values. | ||
# NB. For keys which support missing values, the MDI value is reliably | ||
# distinct from the valid range of either signed or unsigned 8-, 16-, | ||
# or 32-bit values. For example: | ||
# unsigned 32-bit: | ||
# min = 0b000...000 = 0 | ||
# max = 0b111...110 = 2**32 - 2 | ||
# MDI = 0b111...111 = 2**32 - 1 | ||
# signed 32-bit: | ||
# MDI = 0b111...111 = 2**32 - 1 | ||
# min = 0b111...110 = -(2**31 - 2) | ||
# max = 0b011...111 = 2**31 - 1 | ||
# Use ECCodes gribapi to recognise missing value | ||
_MDI = None | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we now remove this altogether? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So the answer is - some of the tests assert that a thing is missing, and this is used for that purpose. |
||
|
||
|
||
# Non-standardised usage for negative forecast times. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -458,9 +458,9 @@ def _get_key_value(self, key): | |
elif key in ('typeOfFirstFixedSurface', 'typeOfSecondFixedSurface'): | ||
# By default these values are returned as unhelpful strings but | ||
# we can use int representation to compare against instead. | ||
res = gribapi.grib_get(self._message_id, key, int) | ||
res = self._get_value_or_missing(key, use_int=True) | ||
else: | ||
res = gribapi.grib_get(self._message_id, key) | ||
res = self._get_value_or_missing(key) | ||
return res | ||
|
||
def get_computed_key(self, key): | ||
|
@@ -481,9 +481,24 @@ def get_computed_key(self, key): | |
if key in vector_keys: | ||
res = gribapi.grib_get_array(self._message_id, key) | ||
else: | ||
res = gribapi.grib_get(self._message_id, key) | ||
res = self._get_value_or_missing(key) | ||
return res | ||
|
||
def keys(self): | ||
"""Return coded keys available in this Section.""" | ||
return self._keys | ||
|
||
def _get_value_or_missing(self, key, use_int=False): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 for having factored this out |
||
""" | ||
Return value of header element, or None if value is encoded as missing. | ||
Implementation of Regulations 92.1.4 and 92.1.5 via ECCodes. | ||
|
||
""" | ||
if gribapi.grib_is_missing(self._message_id, key): | ||
result = None | ||
else: | ||
if use_int: | ||
result = gribapi.grib_get(self._message_id, key, int) | ||
else: | ||
result = gribapi.grib_get(self._message_id, key) | ||
return result |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
# (C) British Crown Copyright 2010 - 2016, Met Office | ||
# (C) British Crown Copyright 2010 - 2017, Met Office | ||
# | ||
# This file is part of Iris. | ||
# | ||
|
@@ -34,6 +34,7 @@ | |
|
||
if tests.GRIB_AVAILABLE: | ||
import gribapi | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @marqh I don't understand why this import is still here. Is this not being replaced? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ECCodes provides a module called gribapi, it is a replacement for GRIB_API see thus, the import name is unchanged |
||
from iris.fileformats.grib._load_convert import _MDI as MDI | ||
|
||
|
||
@tests.skip_data | ||
|
@@ -50,10 +51,10 @@ def test_latlon_forecast_plev(self): | |
iris.save(cubes, temp_file_path) | ||
expect_diffs = {'totalLength': (4837, 4832), | ||
'productionStatusOfProcessedData': (0, 255), | ||
'scaleFactorOfRadiusOfSphericalEarth': (4294967295, | ||
'scaleFactorOfRadiusOfSphericalEarth': (MDI, | ||
0), | ||
'shapeOfTheEarth': (0, 1), | ||
'scaledValueOfRadiusOfSphericalEarth': (4294967295, | ||
'scaledValueOfRadiusOfSphericalEarth': (MDI, | ||
6367470), | ||
'typeOfGeneratingProcess': (0, 255), | ||
'generatingProcessIdentifier': (128, 255), | ||
|
@@ -70,10 +71,10 @@ def test_rotated_latlon(self): | |
iris.save(cubes, temp_file_path) | ||
expect_diffs = {'totalLength': (648196, 648191), | ||
'productionStatusOfProcessedData': (0, 255), | ||
'scaleFactorOfRadiusOfSphericalEarth': (4294967295, | ||
'scaleFactorOfRadiusOfSphericalEarth': (MDI, | ||
0), | ||
'shapeOfTheEarth': (0, 1), | ||
'scaledValueOfRadiusOfSphericalEarth': (4294967295, | ||
'scaledValueOfRadiusOfSphericalEarth': (MDI, | ||
6367470), | ||
'longitudeOfLastGridPoint': (392109982, 32106370), | ||
'latitudeOfLastGridPoint': (19419996, 19419285), | ||
|
@@ -91,10 +92,10 @@ def test_time_mean(self): | |
cubes = iris.load(source_grib) | ||
expect_diffs = {'totalLength': (21232, 21227), | ||
'productionStatusOfProcessedData': (0, 255), | ||
'scaleFactorOfRadiusOfSphericalEarth': (4294967295, | ||
'scaleFactorOfRadiusOfSphericalEarth': (MDI, | ||
0), | ||
'shapeOfTheEarth': (0, 1), | ||
'scaledValueOfRadiusOfSphericalEarth': (4294967295, | ||
'scaledValueOfRadiusOfSphericalEarth': (MDI, | ||
6367470), | ||
'longitudeOfLastGridPoint': (356249908, 356249809), | ||
'latitudeOfLastGridPoint': (-89999938, -89999944), | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@marqh @bjlittle @pp-mo Can anybody explain to me what difference this makes please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a nasty hack to make sure
ecmwf_grib
is not installed for python3, where it won't build. This is no longer required aseccodes
is py3 compatible