-
Notifications
You must be signed in to change notification settings - Fork 314
Eccodes adopt #2607
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
Changes from 1 commit
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,7 +158,9 @@ def _unscale(v, f): | |
|
|
||
| if isinstance(value, Iterable) or isinstance(factor, Iterable): | ||
| def _masker(item): | ||
| result = ma.masked_equal(item, _MDI) | ||
| numerical_mdi = 2 ** 32 - 1 | ||
|
Member
Author
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. add comment here describing the logic process here, and the lack of examples or integration tests at present |
||
| item = [numerical_mdi if i is None else i for i in item] | ||
|
Member
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: ?
Member
Author
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 | ||
|
|
@@ -177,30 +179,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 | ||
|
Member
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?
Member
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 |
|---|---|---|
|
|
@@ -257,8 +257,21 @@ def latlon_first_last(x_coord, y_coord, grib): | |
| 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))) | ||
| # 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)))) | ||
|
Member
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 What is this rounding and multiplication by 1e6 for?
Member
Author
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. the 1e6 is a multiplier to provide values in degrees x 10 ^ -6, as per the WMO manual on codes the this is to ensure that the we ought to follow up on this with ECMWF, but I have not yet done this
Member
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. Yep. Needs raising, and would also hugely benefit from a comment. |
||
| 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)) | ||
|
|
||
|
|
||
| def scanning_mode_flags(x_coord, y_coord, grib): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -459,8 +459,12 @@ def _get_key_value(self, key): | |
| # 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) | ||
| if gribapi.grib_is_missing(self._message_id, key) == 1: | ||
|
Member
Author
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. get_key function, with inverted order, os check, then get add |
||
| res = None | ||
| else: | ||
| res = gribapi.grib_get(self._message_id, key) | ||
| if gribapi.grib_is_missing(self._message_id, key) == 1: | ||
| res = None | ||
| return res | ||
|
|
||
| def get_computed_key(self, key): | ||
|
|
@@ -482,6 +486,8 @@ def get_computed_key(self, key): | |
| res = gribapi.grib_get_array(self._message_id, key) | ||
| else: | ||
| res = gribapi.grib_get(self._message_id, key) | ||
| if gribapi.grib_is_missing(self._message_id, key) == 1: | ||
| res = None | ||
| return res | ||
|
|
||
| def keys(self): | ||
|
|
||
| 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 | ||
|
Member
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?
Member
Author
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_gribis not installed for python3, where it won't build. This is no longer required aseccodesis py3 compatible