diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index 638683a8..d266bb1a 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -36,13 +36,14 @@ jobs: matrix: os: ["ubuntu-latest"] python-version: ["3.11"] - session: ["doctest"] + session: ["doctest", "linkcheck"] iris-source : ["conda-forge"] include: - os: "ubuntu-latest" python-version: "3.11" session: "tests" iris-source: "conda-forge" + coverage: "--coverage" - os: "ubuntu-latest" python-version: "3.11" session: "tests" @@ -129,3 +130,7 @@ jobs: PY_VER: ${{ matrix.python-version }} run: | nox --session ${{ matrix.session }} -- --test-data-dir ${HOME}/iris-test-data/test_data + + - name: "Upload coverage report" + uses: codecov/codecov-action@v4 + if: ${{ matrix.coverage }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2cab3a2f..ccb41249 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ *.py[co] +# setuptools-scm +_version.py + # Packages *.egg *.egg-info diff --git a/.readthedocs.yml b/.readthedocs.yml index d9e6d5ce..af304cb2 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -4,6 +4,19 @@ build: os: ubuntu-20.04 tools: python: mambaforge-4.10 + jobs: + post_checkout: + # The SciTools/iris-grib repository is shallow i.e., has a .git/shallow, + # therefore complete the repository with a full history in order + # to allow setuptools-scm to correctly auto-discover the version. + - git fetch --unshallow + - git fetch --all + # Need to stash the local changes that Read the Docs makes so that + # setuptools_scm can generate the correct Iris-grib version. + pre_install: + - git stash + post_install: + - git stash pop conda: environment: requirements/readthedocs.yml diff --git a/docs/conf.py b/docs/conf.py index 6e27a766..d70991db 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -105,6 +105,14 @@ # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False +# url link checker. Some links registering as faulty, despite being functional. +# See https://www.sphinx-doc.org/en/1.2/config.html[...] +# [...]#options-for-the-linkcheck-builder + +linkcheck_ignore = [ + "https://github.com/Scitools/iris-grib/compare" + "/c4243ae..5c314e3#diff-cf46b46880cae59e82a91c7ab6bb81ba" +] # -- Autodoc ------------------------------------------------------------------ diff --git a/docs/index.rst b/docs/index.rst index 15f18fae..d021c57d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -214,7 +214,7 @@ Getting Started To ensure all ``iris-grib`` dependencies, it is sufficient to have installed :mod:`Iris ` itself, and -`ecCodes `_ . +`ecCodes `_ . The simplest way to install is with `conda `_ , using the @@ -234,7 +234,7 @@ Development sources are hosted at ``_ . Releases ======== -For recent changes, see `Release Notes `_ . +For recent changes, see :ref:`release_notes` . Indices and tables diff --git a/docs/ref/release_notes.rst b/docs/ref/release_notes.rst index 52d7bda7..9ab84444 100644 --- a/docs/ref/release_notes.rst +++ b/docs/ref/release_notes.rst @@ -1,3 +1,5 @@ +.. _release_notes: + Release Notes ============= @@ -21,6 +23,11 @@ Features `(ISSUE#330) `_, `(PR#402) `_ +* `@DPeterK `_ and + `@trexfeathers `_ added saving support for + grid definition template 20 - polar stereographic. + `(PR#405) `_ + Documentation ^^^^^^^^^^^^^ * `@pp-mo `_ reworked the main docs page to : @@ -128,7 +135,7 @@ Bugs Fixed iris-grib to work with the latest versions of `iris `_, `cf-units `_, - `ecCodes `_ and + `ecCodes `_ and `cartopy `_, including casting the usage of :meth:`cf_units.Unit.date2num` as float. setting and setting the values of some missing keys using ``gribapi.GRIB_MISSING_LONG``. @@ -247,15 +254,15 @@ Bugs Fixed * `@pp-mo `_ fixed loading of grid definition template 3.90, "Space view perspective or orthographic grid", which was **broken since Iris 2.3**. This now produces data with an iris - `Geostationary `_ + `Geostationary `_ coordinate system. Prior to Iris 2.3, what is now the Iris 'Geostationary' class was (incorrectly) named "VerticalPerspective" : When that was `corrected in Iris 2.3 `_ , it broke the iris-grib loading, since the data was now incorrectly assigned the "new-style" Iris - `VerticalPerspective `_ + `VerticalPerspective `_ coordinate system, equivalent to the Cartopy - `NearsidePerspective `_ + `NearsidePerspective `_ and Proj `"nsper" `_ . The plotting behaviour of this is now **the same again as before Iris 2.3** : @@ -264,7 +271,7 @@ Bugs Fixed * `@pp-mo `_ fixed a problem where cubes were loading from GRIB 1 with a changed coordinate system, since eccodes versions >= 1.19. This resulted from a change to eccodes, which now returns a different - 'shapeOfTheEarth' parameter : see `eccodes issue ECC-811 `_ . This resulted + 'shapeOfTheEarth' parameter. This resulted in a coordinate system with a different earth radius. For backwards compatibilty, the earth radius has now been fixed to the same value as previously. However, pending further investigation, this value may be technically incorrect and we may @@ -293,8 +300,7 @@ Bugs Fixed * `@pp-mo `_ fixed a problem that caused very slow loading, and possible memory overflows, with Dask versions >= 2.0. **This requires Iris >= 2.4**, as a new minimum dependency. - ( This problem was shared with UM file access in Iris : see - https://scitools.org.uk/iris/docs/v2.4.0/whatsnew/2.4.html#bugs-fixed ). + ( This problem was shared with UM file access in Iris, fixed in Iris 2.4. `(PR#190) `_ * `@trexfeathers `_ fixed all the tests to @@ -451,7 +457,7 @@ What's new in iris-grib v0.12 :Date: 25 Oct 2017 Updated to work with -`ecCodes `_ as its +`ecCodes `_ as its interface to GRIB files. This is ECMWF's replacement for the older GRIB-API, which is now deprecated. diff --git a/iris_grib/__init__.py b/iris_grib/__init__.py index de2a5e0f..bb02e15b 100644 --- a/iris_grib/__init__.py +++ b/iris_grib/__init__.py @@ -5,7 +5,7 @@ """ Conversion of cubes to/from GRIB. -See: `ECMWF GRIB API `_. +See: `ECMWF GRIB API `_. """ @@ -30,7 +30,10 @@ from .message import GribMessage -__version__ = "0.20.dev0" +try: + from ._version import version as __version__ +except ModuleNotFoundError: + __version__ = "unknown" __all__ = [ "load_cubes", @@ -410,8 +413,11 @@ def _compute_extra_keys(self): else: raise TranslationError("Unhandled projectionCentreFlag") + # Always load PolarStereographic - never Stereographic. + # Stereographic is a CF/Iris concept and not something described + # in GRIB. # Note: I think the grib api defaults LaDInDegrees to 60 for grib1. - self.extra_keys["_coord_system"] = coord_systems.Stereographic( + self.extra_keys["_coord_system"] = coord_systems.PolarStereographic( pole_lat, self.orientationOfTheGridInDegrees, 0, diff --git a/iris_grib/_load_convert.py b/iris_grib/_load_convert.py index c4ea31ec..9b30eaf2 100644 --- a/iris_grib/_load_convert.py +++ b/iris_grib/_load_convert.py @@ -225,40 +225,6 @@ def _hindcast_fix(forecast_time): return forecast_time -def fixup_float32_from_int32(value): - """ - Workaround for use when reading an IEEE 32-bit floating-point value - which the ECMWF GRIB API has erroneously treated as a 4-byte signed - integer. - - """ - # Convert from two's complement to sign-and-magnitude. - # NB. The bit patterns 0x00000000 and 0x80000000 will both be - # returned by the ECMWF GRIB API as an integer 0. Because they - # correspond to positive and negative zero respectively it is safe - # to treat an integer 0 as a positive zero. - if value < 0: - value = 0x80000000 - value - value_as_uint32 = np.array(value, dtype="u4") - value_as_float32 = value_as_uint32.view(dtype="f4") - return float(value_as_float32) - - -def fixup_int32_from_uint32(value): - """ - Workaround for use when reading a signed, 4-byte integer which the - ECMWF GRIB API has erroneously treated as an unsigned, 4-byte - integer. - - NB. This workaround is safe to use with values which are already - treated as signed, 4-byte integers. - - """ - if value >= 0x80000000: - value = 0x80000000 - value - return value - - ############################################################################### # # Identification Section 1 @@ -846,21 +812,16 @@ def grid_definition_template_12(section, metadata): lat = section["latitudeOfReferencePoint"] * _GRID_ACCURACY_IN_DEGREES lon = section["longitudeOfReferencePoint"] * _GRID_ACCURACY_IN_DEGREES scale = section["scaleFactorAtReferencePoint"] - # Catch bug in ECMWF GRIB API (present at 1.12.1) where the scale - # is treated as a signed, 4-byte integer. - if isinstance(scale, int): - scale = fixup_float32_from_int32(scale) CM_TO_M = 0.01 easting = section["XR"] * CM_TO_M northing = section["YR"] * CM_TO_M cs = icoord_systems.TransverseMercator(lat, lon, easting, northing, scale, geog_cs) - # Deal with bug in ECMWF GRIB API (present at 1.12.1) where these - # values are treated as unsigned, 4-byte integers. - x1 = fixup_int32_from_uint32(section["X1"]) - y1 = fixup_int32_from_uint32(section["Y1"]) - x2 = fixup_int32_from_uint32(section["X2"]) - y2 = fixup_int32_from_uint32(section["Y2"]) + # Fetch grid extents + x1 = section["X1"] + y1 = section["Y1"] + x2 = section["X2"] + y2 = section["Y2"] # Rather unhelpfully this grid definition template seems to be # overspecified, and thus open to inconsistency. But for determining @@ -951,7 +912,10 @@ def grid_definition_template_20(section, metadata): central_lat = 90.0 central_lon = section["orientationOfTheGrid"] * _GRID_ACCURACY_IN_DEGREES true_scale_lat = section["LaD"] * _GRID_ACCURACY_IN_DEGREES - cs = icoord_systems.Stereographic( + # Always load PolarStereographic - never Stereographic. + # Stereographic is a CF/Iris concept and not something described + # in GRIB. + cs = icoord_systems.PolarStereographic( central_lat=central_lat, central_lon=central_lon, true_scale_lat=true_scale_lat, diff --git a/iris_grib/_save_rules.py b/iris_grib/_save_rules.py index a47bd453..3e8cb295 100644 --- a/iris_grib/_save_rules.py +++ b/iris_grib/_save_rules.py @@ -27,6 +27,7 @@ TransverseMercator, LambertConformal, LambertAzimuthalEqualArea, + Stereographic, ) from iris.exceptions import TranslationError @@ -48,65 +49,6 @@ _TIME_RANGE_UNITS_INVERTED = {val: key for key, val in _TIME_RANGE_UNITS.items()} -def fixup_float32_as_int32(value): - """ - Workaround for use when the ECMWF GRIB API treats an IEEE 32-bit - floating-point value as a signed, 4-byte integer. - - Returns the integer value which will result in the on-disk - representation corresponding to the IEEE 32-bit floating-point - value. - - """ - value_as_float32 = np.array(value, dtype="f4") - value_as_uint32 = value_as_float32.view(dtype="u4") - if value_as_uint32 >= 0x80000000: - # Convert from two's-complement to sign-and-magnitude. - # NB. Because of the silly representation of negative - # integers in GRIB2, there is no value we can pass to - # codes_set that will result in the bit pattern 0x80000000. - # But since that bit pattern corresponds to a floating - # point value of negative-zero, we can safely treat it as - # positive-zero instead. - value_as_grib_int = 0x80000000 - int(value_as_uint32) - else: - value_as_grib_int = int(value_as_uint32) - return value_as_grib_int - - -def fixup_int32_as_uint32(value): - """ - Workaround for use when the ECMWF GRIB API treats a signed, 4-byte - integer value as an unsigned, 4-byte integer. - - Returns the unsigned integer value which will result in the on-disk - representation corresponding to the signed, 4-byte integer value. - - """ - value = int(value) - if -0x7FFFFFFF <= value <= 0x7FFFFFFF: - if value < 0: - # Convert from two's-complement to sign-and-magnitude. - value = 0x80000000 - value - else: - msg = "{} out of range -2147483647 to 2147483647.".format(value) - raise ValueError(msg) - return value - - -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: - eccodes.codes_set(grib, key, value) - except eccodes.CodesInternalError: - value = fixup_int32_as_uint32(value) - eccodes.codes_set(grib, key, value) - - ############################################################################### # # Constants @@ -574,14 +516,10 @@ def grid_definition_template_12(cube, grib): eccodes.codes_set(grib, "Di", abs(x_step)) eccodes.codes_set(grib, "Dj", abs(y_step)) 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])) + eccodes.codes_set(grib, "Y1", int(y_cm[0])) + eccodes.codes_set(grib, "Y2", int(y_cm[-1])) + eccodes.codes_set(grib, "X1", int(x_cm[0])) + eccodes.codes_set(grib, "X2", int(x_cm[-1])) # Lat and lon of reference point are measured in millionths of a degree. eccodes.codes_set( @@ -603,32 +541,32 @@ def m_to_cm(value): # False easting and false northing are measured in units of (10^-2)m. eccodes.codes_set(grib, "XR", m_to_cm(cs.false_easting)) eccodes.codes_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 = eccodes.codes_get_native_type(grib, "scaleFactorAtReferencePoint") - if key_type is not float: - value = fixup_float32_as_int32(value) eccodes.codes_set(grib, "scaleFactorAtReferencePoint", value) -def grid_definition_template_30(cube, grib): +def _perspective_projection_common(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. + Common functionality for setting grid keys for the perspective projection + grid definition templates (20 and 30; Polar Stereographic and Lambert + Conformal. """ - - eccodes.codes_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 + cs_name = cs.grid_mapping_name.replace("_", " ").title() + + both_falses_zero = all( + np.isclose(f, 0.0, atol=1e-6) for f in (cs.false_easting, cs.false_northing) + ) + if not both_falses_zero: + message = ( + f"{cs.false_easting=}, {cs.false_northing=} . Non-zero " + f"unsupported for {cs_name}." + ) + raise TranslationError(message) # Normalise the coordinate values to millimetres - the resolution # used in the GRIB message. @@ -636,21 +574,26 @@ def grid_definition_template_30(cube, grib): 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 TranslationError(msg) + msg = "Irregular coordinates not supported for {!r}." + raise TranslationError(msg.format(cs_name)) eccodes.codes_set(grib, "Dx", abs(x_step)) eccodes.codes_set(grib, "Dy", abs(y_step)) horizontal_grid_common(cube, grib, xy=True) - # Transform first point into geographic CS + eccodes.codes_set( + grib, + "resolutionAndComponentFlags", + 0x1 << _RESOLUTION_AND_COMPONENTS_GRID_WINDS_BIT, + ) + + # 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() @@ -670,20 +613,85 @@ def grid_definition_template_30(cube, grib): ) eccodes.codes_set(grib, "LaD", cs.central_lat / _DEFAULT_DEGREES_UNITS) eccodes.codes_set(grib, "LoV", central_lon / _DEFAULT_DEGREES_UNITS) + + +def grid_definition_template_20(cube, grib): + """ + Set keys within the provided GRIB message based on + Grid Definition Template 3.20. + + Template 3.20 is used to represent a Polar Stereographic grid. + + """ + eccodes.codes_set(grib, "gridDefinitionTemplateNumber", 20) + + # Retrieve the cube coord system. + cs = cube.coord(dimensions=[0]).coord_system + + _perspective_projection_common(cube, grib) + + # Is this a north or south polar stereographic projection? + if np.isclose(cs.central_lat, -90.0): + centre_flag = 0x80 + elif np.isclose(cs.central_lat, 90.0): + centre_flag = 0x0 + else: + message = ( + f"{cs.central_lat=} . GRIB Template 3.20 only supports the polar " + "stereographic projection; central_lat must be 90.0 or -90.0." + ) + raise TranslationError(message) + + def non_standard_scale_factor_error(message: str): + message = f"Non-standard scale factor specified. {message}" + raise TranslationError(message) + + if cs.true_scale_lat and cs.true_scale_lat != cs.central_lat: + non_standard_scale_factor_error( + f"{cs.true_scale_lat=}, {cs.central_lat=} . iris_grib can " + "only write a GRIB Template 3.20 file where these are identical." + ) + + if cs.scale_factor_at_projection_origin and not np.isclose( + cs.scale_factor_at_projection_origin, 1.0 + ): + non_standard_scale_factor_error( + f"{cs.scale_factor_at_projection_origin=} . iris_grib cannot " + "write scale_factor_at_projection_origin to a GRIB Template 3.20 " + "file." + ) + + eccodes.codes_set(grib, "projectionCentreFlag", centre_flag) + + +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. + + """ + + eccodes.codes_set(grib, "gridDefinitionTemplateNumber", 30) + + # Retrieve the cube coord system. + cs = cube.coord(dimensions=[0]).coord_system + + _perspective_projection_common(cube, grib) + + # Which pole are the parallels closest to? That is the direction + # that the cone converges. latin1, latin2 = cs.secant_latitudes eccodes.codes_set(grib, "Latin1", latin1 / _DEFAULT_DEGREES_UNITS) eccodes.codes_set(grib, "Latin2", latin2 / _DEFAULT_DEGREES_UNITS) - eccodes.codes_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 + poliest_secant = latin1 if abs(latin1) > abs(latin2) else latin2 + centre_flag = 0x0 if poliest_secant > 0 else 0x80 eccodes.codes_set(grib, "projectionCentreFlag", centre_flag) + eccodes.codes_set(grib, "latitudeOfSouthernPole", 0) eccodes.codes_set(grib, "longitudeOfSouthernPole", 0) @@ -799,6 +807,11 @@ def grid_definition_section(cube, grib): # Transverse Mercator coordinate system (template 3.12). grid_definition_template_12(cube, grib) + elif isinstance(cs, Stereographic): + # Stereographic coordinate system (template 3.20). + # This will also handle the PolarStereographic subclass. + grid_definition_template_20(cube, grib) + elif isinstance(cs, LambertConformal): # Lambert Conformal coordinate system (template 3.30). grid_definition_template_30(cube, grib) diff --git a/iris_grib/tests/integration/load_convert/test_sample_file_loads.py b/iris_grib/tests/integration/load_convert/test_sample_file_loads.py index aeb5526e..43d04a50 100644 --- a/iris_grib/tests/integration/load_convert/test_sample_file_loads.py +++ b/iris_grib/tests/integration/load_convert/test_sample_file_loads.py @@ -75,7 +75,10 @@ def test_polar_stereo_grib2_grid_definition(self): self.assertAlmostEqual(pyc.points.max(), -216.1459, places=4) self.assertAlmostEqual(pyc.points.min(), -5970216.1459, places=4) self.assertEqual(pyc.coord_system, pxc.coord_system) - self.assertEqual(pyc.coord_system.grid_mapping_name, "stereographic") + self.assertEqual( + pyc.coord_system.grid_mapping_name, + "polar_stereographic", + ) self.assertEqual(pyc.coord_system.central_lat, 90.0) self.assertEqual(pyc.coord_system.central_lon, 249.0) self.assertEqual(pyc.coord_system.false_easting, 0.0) diff --git a/iris_grib/tests/integration/round_trip/test_grid_definition_section.py b/iris_grib/tests/integration/round_trip/test_grid_definition_section.py index 8988a7de..77e5668e 100644 --- a/iris_grib/tests/integration/round_trip/test_grid_definition_section.py +++ b/iris_grib/tests/integration/round_trip/test_grid_definition_section.py @@ -11,8 +11,14 @@ # before importing anything else. import iris_grib.tests as tests +from typing import Literal + +import numpy as np + from iris import load_cube, save -from iris.coord_systems import RotatedGeogCS +from iris.coord_systems import GeogCS, PolarStereographic, RotatedGeogCS, Stereographic +from iris.coords import DimCoord +from iris.cube import Cube from iris.fileformats.pp import EARTH_RADIUS as UM_DEFAULT_EARTH_RADIUS from iris.util import is_regular @@ -124,5 +130,106 @@ def test_save_load(self): self.assertArrayAllClose(cube.data, cube_loaded_from_saved.data) +class GDT20Common: + """'Wrapper' to avoid class being detected during test runs.""" + + class GDT20Common(tests.TestGribMessage): + """Roundtrip testing that attributes save+load correctly.""" + + # Enable subclassing to test different permutations. + coord_system_class: type[Stereographic] + pole: Literal["north", "south"] + + def setUp(self): + # Create a Cube to save, inspired by + # iris-sample-data toa_brightness_stereographic.nc + if self.pole == "north": + central_lat = 90 + elif self.pole == "south": + central_lat = -90 + else: + raise NotImplementedError(f"Invalid pole: {self.pole}") + + self.coord_system_kwargs = dict( + central_lat=central_lat, + central_lon=325, + true_scale_lat=central_lat, + ellipsoid=GeogCS(6378169.0), + ) + coord_system = self.coord_system_class(**self.coord_system_kwargs) + + coord_kwargs = dict( + units="m", + coord_system=coord_system, + ) + coord_x = DimCoord( + np.linspace(-2250000, 6750192, 256, endpoint=False), + standard_name="projection_x_coordinate", + **coord_kwargs, + ) + coord_y = DimCoord( + np.linspace(-980000, -6600000, 160, endpoint=False), + standard_name="projection_y_coordinate", + **coord_kwargs, + ) + coord_t = DimCoord( + 0, standard_name="time", units="hours since 1970-01-01 00:00:00" + ) + coord_fp = DimCoord(0, standard_name="forecast_period", units="hours") + coord_frt = DimCoord( + 0, standard_name="forecast_reference_time", units=coord_t.units + ) + shape = (coord_y.shape[0], coord_x.shape[0]) + self.cube = Cube( + np.arange(np.prod(shape), dtype=float).reshape(shape), + dim_coords_and_dims=[(coord_y, 0), (coord_x, 1)], + aux_coords_and_dims=[ + (coord_t, None), + (coord_fp, None), + (coord_frt, None), + ], + ) + + def test_save_load(self): + with self.temp_filename("polar_stereo.grib2") as temp_file_path: + save(self.cube, temp_file_path) + cube_reloaded = load_cube(temp_file_path) + # Touch the data before destroying the file. + _ = cube_reloaded.data + + cube_expected = self.cube.copy() + for coord in cube_expected.dim_coords: + # GRIB only describes PolarStereographic, so we always expect + # that system even when we started with Stereographic. + coord.coord_system = PolarStereographic(**self.coord_system_kwargs) + + # Modifications to remove irrelevant inequalities. + del cube_reloaded.attributes["GRIB_PARAM"] + for coord in cube_reloaded.dim_coords: + coord.points = np.round(coord.points) + + self.assertEqual(cube_expected, cube_reloaded) + + +class TestGDT20StereoNorth(GDT20Common.GDT20Common): + coord_system_class = Stereographic + pole = "north" + + +class TestGDT20StereoSouth(GDT20Common.GDT20Common): + coord_system_class = Stereographic + pole = "south" + + +class TestGDT20PolarNorth(GDT20Common.GDT20Common): + coord_system_class = PolarStereographic + pole = "north" + + +class TestGDT20PolarSouth(GDT20Common.GDT20Common): + coord_system_class = PolarStereographic + pole = "south" + + if __name__ == "__main__": tests.main() diff --git a/iris_grib/tests/integration/save_rules/test_grid_definition_section.py b/iris_grib/tests/integration/save_rules/test_grid_definition_section.py index b7b0f3bc..0ca62403 100644 --- a/iris_grib/tests/integration/save_rules/test_grid_definition_section.py +++ b/iris_grib/tests/integration/save_rules/test_grid_definition_section.py @@ -20,6 +20,8 @@ LambertConformal, AlbersEqualArea, LambertAzimuthalEqualArea, + Stereographic, + PolarStereographic, ) import numpy as np @@ -92,6 +94,26 @@ def test_grid_definition_template_12(self): grid_definition_section(test_cube, self.mock_grib) self._check_key("gridDefinitionTemplateNumber", 12) + def grid_definition_template_20_common(self, coord_system: type[Stereographic]): + # Stereographic grid. + # Common code to allow testing PolarStereographic and Stereographic. + if not issubclass(coord_system, Stereographic): + raise ValueError("coord_system must be Stereographic type.") + + x_points = np.arange(3) + y_points = np.arange(3) + coord_units = "m" + cs = coord_system(90.0, 0, ellipsoid=self.ellipsoid) + test_cube = self._make_test_cube(cs, x_points, y_points, coord_units) + grid_definition_section(test_cube, self.mock_grib) + self._check_key("gridDefinitionTemplateNumber", 20) + + def test_grid_definition_template_20_s(self): + self.grid_definition_template_20_common(Stereographic) + + def test_grid_definition_template_20_ps(self): + self.grid_definition_template_20_common(PolarStereographic) + def test_grid_definition_template_30(self): # Lambert Conformal grid. x_points = np.arange(3) diff --git a/iris_grib/tests/results/integration/load_convert/sample_file_loads/polar_stereo_grib1.cml b/iris_grib/tests/results/integration/load_convert/sample_file_loads/polar_stereo_grib1.cml index c1c845ae..27a002fb 100644 --- a/iris_grib/tests/results/integration/load_convert/sample_file_loads/polar_stereo_grib1.cml +++ b/iris_grib/tests/results/integration/load_convert/sample_file_loads/polar_stereo_grib1.cml @@ -12,17 +12,17 @@ - - + - - + diff --git a/iris_grib/tests/unit/load_convert/test_fixup_float32_from_int32.py b/iris_grib/tests/unit/load_convert/test_fixup_float32_from_int32.py deleted file mode 100644 index e5d187b0..00000000 --- a/iris_grib/tests/unit/load_convert/test_fixup_float32_from_int32.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright iris-grib contributors -# -# This file is part of iris-grib and is released under the BSD license. -# See LICENSE in the root of the repository for full licensing details. -""" -Unit tests for `iris_grib._load_convert.fixup_float32_from_int32`. - -""" - -# Import iris_grib.tests first so that some things can be initialised before -# importing anything else. -import iris_grib.tests as tests - -from iris_grib._load_convert import fixup_float32_from_int32 - - -class Test(tests.IrisGribTest): - def test_negative(self): - result = fixup_float32_from_int32(-0x3F000000) - self.assertEqual(result, -0.5) - - def test_zero(self): - result = fixup_float32_from_int32(0) - self.assertEqual(result, 0) - - def test_positive(self): - result = fixup_float32_from_int32(0x3F000000) - self.assertEqual(result, 0.5) - - -if __name__ == "__main__": - tests.main() diff --git a/iris_grib/tests/unit/load_convert/test_fixup_int32_from_uint32.py b/iris_grib/tests/unit/load_convert/test_fixup_int32_from_uint32.py deleted file mode 100644 index b4c3f8f0..00000000 --- a/iris_grib/tests/unit/load_convert/test_fixup_int32_from_uint32.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright iris-grib contributors -# -# This file is part of iris-grib and is released under the BSD license. -# See LICENSE in the root of the repository for full licensing details. -""" -Unit tests for `iris_grib._load_convert.fixup_int32_from_uint32`. - -""" - -# Import iris_grib.tests first so that some things can be initialised before -# importing anything else. -import iris_grib.tests as tests - -from iris_grib._load_convert import fixup_int32_from_uint32 - - -class Test(tests.IrisGribTest): - def test_negative(self): - result = fixup_int32_from_uint32(0x80000005) - self.assertEqual(result, -5) - - def test_negative_zero(self): - result = fixup_int32_from_uint32(0x80000000) - self.assertEqual(result, 0) - - def test_zero(self): - result = fixup_int32_from_uint32(0) - self.assertEqual(result, 0) - - def test_positive(self): - result = fixup_int32_from_uint32(200000) - self.assertEqual(result, 200000) - - def test_already_negative(self): - # If we *already* have a negative value the fixup routine should - # leave it alone. - result = fixup_int32_from_uint32(-7) - self.assertEqual(result, -7) - - -if __name__ == "__main__": - tests.main() diff --git a/iris_grib/tests/unit/load_convert/test_grid_definition_template_12.py b/iris_grib/tests/unit/load_convert/test_grid_definition_template_12.py index 09d46a64..02485d14 100644 --- a/iris_grib/tests/unit/load_convert/test_grid_definition_template_12.py +++ b/iris_grib/tests/unit/load_convert/test_grid_definition_template_12.py @@ -184,17 +184,6 @@ def test_incompatible_grid_extent(self): with self.assertRaisesRegex(iris.exceptions.TranslationError, "grid"): grid_definition_template_12(section, metadata) - def test_scale_workaround(self): - section = self.section_3() - section["scaleFactorAtReferencePoint"] = 1065346526 - metadata = empty_metadata() - grid_definition_template_12(section, metadata) - expected = self.expected(0, 1) - # A float32 can't hold exactly the same value. - cs = expected["dim_coords_and_dims"][0][0].coord_system - cs.scale_factor_at_central_meridian = 0.9996012449264526 - self.assertEqual(metadata, expected) - if __name__ == "__main__": tests.main() diff --git a/iris_grib/tests/unit/load_convert/test_grid_definition_template_20.py b/iris_grib/tests/unit/load_convert/test_grid_definition_template_20.py index 949ec045..3aeaed43 100644 --- a/iris_grib/tests/unit/load_convert/test_grid_definition_template_20.py +++ b/iris_grib/tests/unit/load_convert/test_grid_definition_template_20.py @@ -52,7 +52,10 @@ def expected(self, y_dim, x_dim): # Prepare the expectation. expected = empty_metadata() ellipsoid = iris.coord_systems.GeogCS(6367470) - cs = iris.coord_systems.Stereographic( + # Always expect PolarStereographic - never Stereographic. + # Stereographic is a CF/Iris concept and not something described in + # GRIB. + cs = iris.coord_systems.PolarStereographic( central_lat=90.0, central_lon=262.0, false_easting=0, diff --git a/iris_grib/tests/unit/save_rules/test_fixup_float32_as_int32.py b/iris_grib/tests/unit/save_rules/test_fixup_float32_as_int32.py deleted file mode 100644 index 5e576e04..00000000 --- a/iris_grib/tests/unit/save_rules/test_fixup_float32_as_int32.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright iris-grib contributors -# -# This file is part of iris-grib and is released under the BSD license. -# See LICENSE in the root of the repository for full licensing details. -""" -Unit tests for `iris_grib._save_rules.fixup_float32_as_int32`. - -""" - -# Import iris_grib.tests first so that some things can be initialised before -# importing anything else. -import iris_grib.tests as tests - -from iris_grib._save_rules import fixup_float32_as_int32 - - -class Test(tests.IrisGribTest): - def test_positive_zero(self): - result = fixup_float32_as_int32(0.0) - self.assertEqual(result, 0) - - def test_negative_zero(self): - result = fixup_float32_as_int32(-0.0) - self.assertEqual(result, 0) - - def test_high_bit_clear_1(self): - # Start with the float32 value for the bit pattern 0x00000001. - result = fixup_float32_as_int32(1.401298464324817e-45) - self.assertEqual(result, 1) - - def test_high_bit_clear_2(self): - # Start with the float32 value for the bit pattern 0x00000002. - result = fixup_float32_as_int32(2.802596928649634e-45) - self.assertEqual(result, 2) - - def test_high_bit_set_1(self): - # Start with the float32 value for the bit pattern 0x80000001. - result = fixup_float32_as_int32(-1.401298464324817e-45) - self.assertEqual(result, -1) - - def test_high_bit_set_2(self): - # Start with the float32 value for the bit pattern 0x80000002. - result = fixup_float32_as_int32(-2.802596928649634e-45) - self.assertEqual(result, -2) - - -if __name__ == "__main__": - tests.main() diff --git a/iris_grib/tests/unit/save_rules/test_fixup_int32_as_uint32.py b/iris_grib/tests/unit/save_rules/test_fixup_int32_as_uint32.py deleted file mode 100644 index cc8e87c4..00000000 --- a/iris_grib/tests/unit/save_rules/test_fixup_int32_as_uint32.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright iris-grib contributors -# -# This file is part of iris-grib and is released under the BSD license. -# See LICENSE in the root of the repository for full licensing details. -""" -Unit tests for `iris_grib._save_rules.fixup_int32_as_uint32`. - -""" - -# Import iris.tests first so that some things can be initialised before -# importing anything else. -import iris_grib.tests as tests - -from iris_grib._save_rules import fixup_int32_as_uint32 - - -class Test(tests.IrisGribTest): - def test_very_negative(self): - with self.assertRaises(ValueError): - fixup_int32_as_uint32(-0x80000000) - - def test_negative(self): - result = fixup_int32_as_uint32(-3) - self.assertEqual(result, 0x80000003) - - def test_zero(self): - result = fixup_int32_as_uint32(0) - self.assertEqual(result, 0) - - def test_positive(self): - result = fixup_int32_as_uint32(5) - self.assertEqual(result, 5) - - def test_very_positive(self): - with self.assertRaises(ValueError): - fixup_int32_as_uint32(0x80000000) - - -if __name__ == "__main__": - tests.main() diff --git a/iris_grib/tests/unit/save_rules/test_grid_definition_template_12.py b/iris_grib/tests/unit/save_rules/test_grid_definition_template_12.py index ad42bff6..3b841e54 100644 --- a/iris_grib/tests/unit/save_rules/test_grid_definition_template_12.py +++ b/iris_grib/tests/unit/save_rules/test_grid_definition_template_12.py @@ -105,24 +105,6 @@ def test__grid_points_approx(self): self._check_key("Di", 200) self._check_key("Dj", 500) - def test__negative_grid_points_eccodes_broken(self): - self.mock_eccodes.CodesInternalError = FakeGribError - - # Force the test to run the signed int --> unsigned int workaround. - def set(grib, key, value): - if key in ["X1", "X2", "Y1", "Y2"] and value < 0: - raise self.mock_eccodes.CodesInternalError() - grib.keys[key] = value - - self.mock_eccodes.codes_set = set - - test_cube = self._make_test_cube(x_points=[-1, 1, 3, 5, 7], y_points=[-4, 9]) - grid_definition_template_12(test_cube, self.mock_grib) - self._check_key("X1", 0x80000064) - self._check_key("X2", 700) - self._check_key("Y1", 0x80000190) - self._check_key("Y2", 900) - def test__negative_grid_points_eccodes_fixed(self): test_cube = self._make_test_cube(x_points=[-1, 1, 3, 5, 7], y_points=[-4, 9]) grid_definition_template_12(test_cube, self.mock_grib) @@ -138,25 +120,7 @@ def test__template_specifics(self): self._check_key("XR", 40000000.0) self._check_key("YR", -10000000.0) - def test__scale_factor_eccodes_broken(self): - # 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 - - def get_native_type(grib, key): - assert key == "scaleFactorAtReferencePoint" - return int - - self.mock_eccodes.codes_get_native_type = get_native_type - grid_definition_template_12(self.test_cube, self.mock_grib) - self._check_key("scaleFactorAtReferencePoint", 1065346526) - - def test__scale_factor_eccodes_fixed(self): - def get_native_type(grib, key): - assert key == "scaleFactorAtReferencePoint" - return float - - self.mock_eccodes.codes_get_native_type = get_native_type + def test__scale_factor(self): grid_definition_template_12(self.test_cube, self.mock_grib) self._check_key("scaleFactorAtReferencePoint", 0.9996012717) diff --git a/iris_grib/tests/unit/save_rules/test_grid_definition_template_20.py b/iris_grib/tests/unit/save_rules/test_grid_definition_template_20.py new file mode 100644 index 00000000..fb7db975 --- /dev/null +++ b/iris_grib/tests/unit/save_rules/test_grid_definition_template_20.py @@ -0,0 +1,188 @@ +# Copyright iris-grib contributors +# +# This file is part of iris-grib and is released under the BSD license. +# See LICENSE in the root of the repository for full licensing details. +""" +Unit tests for :meth:`iris_grib._save_rules.grid_definition_template_20`. + +""" + +# Import iris_grib.tests first so that some things can be initialised before +# importing anything else. +import iris_grib.tests as tests + +from iris.coord_systems import GeogCS, PolarStereographic, Stereographic +from iris.coords import AuxCoord +from iris.exceptions import TranslationError +import numpy as np + +from iris_grib._save_rules import grid_definition_template_20 +from iris_grib.tests.unit.save_rules import GdtTestMixin + + +class Test(tests.IrisGribTest, GdtTestMixin): + def setUp(self): + self.default_ellipsoid = GeogCS(semi_major_axis=6371200.0) + self.stereo_test_cube = self._make_test_cube(coord_units="m") + + GdtTestMixin.setUp(self) + + def _default_coord_system(self, false_easting=0, false_northing=0): + return PolarStereographic( + 90.0, + 0, + false_easting=false_easting, + false_northing=false_northing, + ellipsoid=self.default_ellipsoid, + ) + + def test__template_number(self): + grid_definition_template_20(self.stereo_test_cube, self.mock_grib) + self._check_key("gridDefinitionTemplateNumber", 20) + + def test__shape_of_earth(self): + grid_definition_template_20(self.stereo_test_cube, self.mock_grib) + self._check_key("shapeOfTheEarth", 1) + self._check_key("scaleFactorOfRadiusOfSphericalEarth", 0) + self._check_key("scaleFactorOfEarthMajorAxis", 0) + self._check_key("scaledValueOfEarthMajorAxis", 0) + self._check_key("scaleFactorOfEarthMinorAxis", 0) + self._check_key("scaledValueOfEarthMinorAxis", 0) + + def test__grid_shape(self): + stereo_test_cube = self._make_test_cube( + x_points=np.arange(13), y_points=np.arange(6), coord_units="m" + ) + grid_definition_template_20(stereo_test_cube, self.mock_grib) + self._check_key("Nx", 13) + self._check_key("Ny", 6) + + def test__grid_points(self): + stereo_test_cube = self._make_test_cube( + x_points=[1e6, 3e6, 5e6, 7e6], y_points=[4e6, 9e6], coord_units="m" + ) + grid_definition_template_20(stereo_test_cube, self.mock_grib) + self._check_key("latitudeOfFirstGridPoint", 54139565) + self._check_key("longitudeOfFirstGridPoint", 165963757) + self._check_key("Dx", 2e9) + self._check_key("Dy", 5e9) + + def test__template_specifics(self): + grid_definition_template_20(self.stereo_test_cube, self.mock_grib) + self._check_key("LaD", 90e6) + self._check_key("LoV", 0) + + def test__scanmode(self): + grid_definition_template_20(self.stereo_test_cube, self.mock_grib) + self._check_key("iScansPositively", 1) + self._check_key("jScansPositively", 1) + + def test__scanmode_reverse(self): + stereo_test_cube = self._make_test_cube( + x_points=np.arange(7e6, 0, -1e6), coord_units="m" + ) + grid_definition_template_20(stereo_test_cube, self.mock_grib) + self._check_key("iScansPositively", 0) + self._check_key("jScansPositively", 1) + + def test_projection_centre(self): + grid_definition_template_20(self.stereo_test_cube, self.mock_grib) + self._check_key("projectionCentreFlag", 0) + + def test_projection_centre_south_pole(self): + cs = PolarStereographic(-90.0, 0, ellipsoid=self.default_ellipsoid) + stereo_test_cube = self._make_test_cube(cs=cs, coord_units="m") + grid_definition_template_20(stereo_test_cube, self.mock_grib) + self._check_key("projectionCentreFlag", 128) + + def test_projection_centre_south_pole_parent(self): + # Saving should be able to handle either class (PolarStereographic + # being a subclass of Stereographic). + cs = Stereographic(-90.0, 0, ellipsoid=self.default_ellipsoid) + stereo_test_cube = self._make_test_cube(cs=cs, coord_units="m") + grid_definition_template_20(stereo_test_cube, self.mock_grib) + self._check_key("projectionCentreFlag", 128) + + def test_projection_centre_bad(self): + cs = Stereographic(0, 0, ellipsoid=self.default_ellipsoid) + stereo_test_cube = self._make_test_cube(cs=cs, coord_units="m") + exp_emsg = "must be 90.0 or -90.0" + with self.assertRaisesRegex(TranslationError, exp_emsg): + grid_definition_template_20(stereo_test_cube, self.mock_grib) + + def __fail_false_easting_northing(self, false_easting, false_northing): + cs = self._default_coord_system( + false_easting=false_easting, false_northing=false_northing + ) + test_cube = self._make_test_cube(cs=cs) + message = "Non-zero unsupported" + with self.assertRaisesRegex(TranslationError, message): + grid_definition_template_20(test_cube, self.mock_grib) + + def test__fail_false_easting(self): + self.__fail_false_easting_northing(10.0, 0.0) + + def test__fail_false_northing(self): + self.__fail_false_easting_northing(0.0, 10.0) + + def test__fail_false_easting_northing(self): + self.__fail_false_easting_northing(10.0, 10.0) + + def __fail_irregular_coords(self, x=False, y=False): + def irregular_coord(coord): + coord = AuxCoord.from_coord(coord) + coord.points[1] = coord.points[0] + return coord + + test_cube = self._make_test_cube( + # Make the Y dimension longer to make irregularity is possible. + y_points=[7.0, 8.0, 9.0], + coord_units="m", + ) + coord_lon = test_cube.coord("longitude") + coord_lat = test_cube.coord("latitude") + if x: + test_cube.replace_coord(irregular_coord(coord_lon)) + if y: + test_cube.replace_coord(irregular_coord(coord_lat)) + + message = "Irregular coordinates not supported" + with self.assertRaisesRegex(TranslationError, message): + grid_definition_template_20(test_cube, self.mock_grib) + + def test__fail_irregular_x_coords(self): + self.__fail_irregular_coords(x=True) + + def test__fail_irregular_y_coords(self): + self.__fail_irregular_coords(y=True) + + def test__fail_irregular_coords(self): + self.__fail_irregular_coords(x=True, y=True) + + def test__fail_non_identical_lats(self): + coord_system = PolarStereographic( + 90.0, + 0, + true_scale_lat=60.0, + ellipsoid=self.default_ellipsoid, + ) + test_cube = self._make_test_cube(cs=coord_system, coord_units="m") + message = "only write a GRIB Template 3.20 file where these are identical" + with self.assertRaisesRegex(TranslationError, message): + grid_definition_template_20(test_cube, self.mock_grib) + + def test__fail_scale_factor(self): + coord_system = PolarStereographic( + 90.0, + 0, + scale_factor_at_projection_origin=0.5, + ellipsoid=self.default_ellipsoid, + ) + test_cube = self._make_test_cube(cs=coord_system, coord_units="m") + message = "cannot write scale_factor_at_projection_origin" + with self.assertRaisesRegex(TranslationError, message): + grid_definition_template_20(test_cube, self.mock_grib) + + +if __name__ == "__main__": + tests.main() diff --git a/iris_grib/tests/unit/save_rules/test_grid_definition_template_30.py b/iris_grib/tests/unit/save_rules/test_grid_definition_template_30.py index 38a5cad9..96a02b02 100644 --- a/iris_grib/tests/unit/save_rules/test_grid_definition_template_30.py +++ b/iris_grib/tests/unit/save_rules/test_grid_definition_template_30.py @@ -15,6 +15,7 @@ import iris.coords from iris.coord_systems import GeogCS, LambertConformal +from iris.exceptions import TranslationError from iris_grib._save_rules import grid_definition_template_30 from iris_grib.tests.unit.save_rules import GdtTestMixin @@ -53,12 +54,12 @@ def _make_test_cube(self, cs=None, x_points=None, y_points=None): test_cube.add_dim_coord(x_coord, 1) return test_cube - def _default_coord_system(self): + def _default_coord_system(self, false_easting=0.0, false_northing=0.0): return LambertConformal( central_lat=39.0, central_lon=-96.0, - false_easting=0.0, - false_northing=0.0, + false_easting=false_easting, + false_northing=false_northing, secant_latitudes=(33, 45), ellipsoid=self.default_ellipsoid, ) @@ -126,7 +127,55 @@ def test_projection_centre_south_pole(self): ) test_cube = self._make_test_cube(cs=cs) grid_definition_template_30(test_cube, self.mock_grib) - self._check_key("projectionCentreFlag", 1) + self._check_key("projectionCentreFlag", 128) + + def __fail_false_easting_northing(self, false_easting, false_northing): + cs = self._default_coord_system( + false_easting=false_easting, false_northing=false_northing + ) + test_cube = self._make_test_cube(cs=cs) + message = "Non-zero unsupported" + with self.assertRaisesRegex(TranslationError, message): + grid_definition_template_30(test_cube, self.mock_grib) + + def test__fail_false_easting(self): + self.__fail_false_easting_northing(10.0, 0.0) + + def test__fail_false_northing(self): + self.__fail_false_easting_northing(0.0, 10.0) + + def test__fail_false_easting_northing(self): + self.__fail_false_easting_northing(10.0, 10.0) + + def __fail_irregular_coords(self, x=False, y=False): + def irregular_coord(coord): + coord = iris.coords.AuxCoord.from_coord(coord) + coord.points[1] = coord.points[0] + return coord + + test_cube = self._make_test_cube( + # Make the Y dimension longer to make irregularity is possible. + y_points=[7.0, 8.0, 9.0], + ) + coord_x = test_cube.coord("projection_x_coordinate") + coord_y = test_cube.coord("projection_y_coordinate") + if x: + test_cube.replace_coord(irregular_coord(coord_x)) + if y: + test_cube.replace_coord(irregular_coord(coord_y)) + + message = "Irregular coordinates not supported" + with self.assertRaisesRegex(TranslationError, message): + grid_definition_template_30(test_cube, self.mock_grib) + + def test__fail_irregular_x_coords(self): + self.__fail_irregular_coords(x=True) + + def test__fail_irregular_y_coords(self): + self.__fail_irregular_coords(y=True) + + def test__fail_irregular_coords(self): + self.__fail_irregular_coords(x=True, y=True) if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index 536ce012..41cf7284 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,6 +4,7 @@ # Defined by PEP 518 requires = [ "setuptools>=64", + "setuptools_scm>=7.0", "wheel", ] # Defined by PEP 517 @@ -60,13 +61,17 @@ license-files = ["LICENSE"] [tool.setuptools.dynamic] dependencies = {file = "requirements/core.txt"} readme = {file = "README.md", content-type = "text/markdown"} -version = {attr = "iris_grib.__version__"} optional-dependencies.all = {file = "requirements/all.txt"} optional-dependencies.test = {file = "requirements/test.txt"} [tool.setuptools.packages.find] include = ["iris_grib*"] +[tool.setuptools_scm] +write_to = "iris_grib/_version.py" +local_scheme = "dirty-tag" +version_scheme = "release-branch-semver" + #------------------------------------------------------------------------------ [tool.coverage.run] @@ -91,6 +96,11 @@ exclude_lines = [ ignore-words-list = "alpha-numeric,degreee,discontiguities,lazyness,meaned,nin" skip = "_build,*.css,*.ipynb,*.js,*.html,*.svg,*.xml,.git,generated" +[tool.check-manifest] +ignore = [ + "iris_grib/_version.py", +] + [tool.mypy] # See https://mypy.readthedocs.io/en/stable/config_file.html ignore_missing_imports = true diff --git a/requirements/locks/py310-linux-64.lock b/requirements/locks/py310-linux-64.lock index af5cc02d..c4c6f495 100644 --- a/requirements/locks/py310-linux-64.lock +++ b/requirements/locks/py310-linux-64.lock @@ -1,6 +1,6 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: 9e797137467cb0eae651a9bc93dc4bec8f140aabfb83ba31bdbfd6e25e21a64e +# input_hash: 8b47e0a77f6ce801a2c484d9a593594c1998ce2d1b9fe3dc1e03225261de4b51 @EXPLICIT https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.2.2-hbcca054_0.conda#2f4327a1cbe7f022401b236e915a5fef @@ -53,11 +53,11 @@ https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2. https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-13.2.0-h69a702a_5.conda#e73e9cfd1191783392131e6238bdb3e9 https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.58.0-h47da74e_1.conda#700ac6ea6d53d5510591c4344d5c989a https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.43-h2797004_0.conda#009981dd9cfcaa4dbfa25ffaed86bcae -https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.45.2-h2797004_0.conda#866983a220e27a80cb75e85cb30466a1 +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.45.3-h2797004_0.conda#b3316cbe90249da4f8e84cd66e1cc55b https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.0-h0841786_0.conda#1f5a58e686b13bcfde88b93f547d23fe https://conda.anaconda.org/conda-forge/linux-64/libudunits2-2.2.28-h40f5838_3.conda#4bdace082e911a3e1f1f0b721bed5b56 https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.15-h0b41bf4_0.conda#33277193f5b92bad9fdd230eb700929c -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.12.6-h232c23b_1.conda#6853448e9ca1cfd5f15382afd2a6d123 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.12.6-h232c23b_2.conda#9a3a42df8a95f65334dfc7b80da1195d https://conda.anaconda.org/conda-forge/linux-64/libzip-1.10.1-h2629f0a_3.conda#ac79812548e7e8cf61f7b0abdef01d3b https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda#47d31b792659ce70f470b5c82fdfb7a4 https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda#d453b98d9c83e71da0741bb0ff4d76bc @@ -71,7 +71,7 @@ https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.2-h659d440_0.conda#cd9 https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.27-pthreads_h413a1c8_0.conda#a356024784da6dfd4683dc5ecf45b155 https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.6.0-h1dd3fc0_3.conda#66f03896ffbe1a110ffda05c7a856504 https://conda.anaconda.org/conda-forge/linux-64/python-3.10.14-hd12c33a_0_cpython.conda#2b4ba962994e8bd4be9ff5b64b75aff2 -https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.45.2-h2c6b66d_0.conda#1423efca06ed343c1da0fc429bae0779 +https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.45.3-h2c6b66d_0.conda#be7d70f2db41b674733667bdd69bd000 https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-h40f5838_3.conda#6bb8deb138f87c9d48320ac21b87e7a1 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.9-h8ee46fc_0.conda#077b6e8ad6a3ddb741fce2496dd01bec https://conda.anaconda.org/conda-forge/noarch/antlr-python-runtime-4.11.1-pyhd8ed1ab_0.tar.bz2#15109c4977d39ad7aa3423f57243e286 @@ -88,7 +88,7 @@ https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.0-pyhd8ed1ab_2. https://conda.anaconda.org/conda-forge/noarch/filelock-3.13.4-pyhd8ed1ab_0.conda#6baa2e7fc09bd2c7c82cb6662d5f1d36 https://conda.anaconda.org/conda-forge/noarch/findlibs-0.0.5-pyhd8ed1ab_0.conda#8f325f63020af6f7acbe2c4cb4c920db https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.3.1-pyhca7485f_0.conda#b7f0662ef2c9d4404f0af9eef5ed2fde -https://conda.anaconda.org/conda-forge/noarch/idna-3.6-pyhd8ed1ab_0.conda#1a76f09108576397c41c0b0c5bd84134 +https://conda.anaconda.org/conda-forge/noarch/idna-3.7-pyhd8ed1ab_0.conda#c0cc1420498b17414d8617d0b9f506ca https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda#f800d2da156d08e289b14e87e43c1ae5 https://conda.anaconda.org/conda-forge/noarch/iris-sample-data-2.4.0-pyhd8ed1ab_0.tar.bz2#18ee9c07cf945a33f92caf1ee3d23ad9 https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.5-py310hd41b1e2_1.conda#b8d67603d43b23ce7e988a5d81a7ab79 @@ -111,6 +111,7 @@ https://conda.anaconda.org/conda-forge/noarch/setuptools-69.5.1-pyhd8ed1ab_0.con https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2 https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96 https://conda.anaconda.org/conda-forge/noarch/toolz-0.12.1-pyhd8ed1ab_0.conda#2fcb582444635e2c402e8569bb94e039 +https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.11.0-pyha770c72_0.conda#6ef2fc37559256cf682d8b3375e89b80 https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-15.1.0-py310h2372a71_0.conda#72637c58d36d9475fda24700c9796f19 https://conda.anaconda.org/conda-forge/noarch/wheel-0.43.0-pyhd8ed1ab_1.conda#0b5293a157c2b5cd513dd1b03d8d3aae https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.4-h0b41bf4_2.conda#82b6df12252e6f32402b96dacc656fec @@ -130,6 +131,7 @@ https://conda.anaconda.org/conda-forge/noarch/pip-24.0-pyhd8ed1ab_0.conda#f586ac https://conda.anaconda.org/conda-forge/linux-64/proj-9.3.1-h1d62c97_0.conda#44ec51d0857d9be26158bb85caa74fdb https://conda.anaconda.org/conda-forge/noarch/pytest-8.1.1-pyhd8ed1ab_0.conda#94ff09cdedcb7b17e9cd5097ee2cfcff https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c +https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.11.0-hd8ed1ab_0.conda#471e3988f8ca5e9eb3ce6be7eac3bcee https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.1-pyhd8ed1ab_0.conda#08807a87fa7af10754d46f63b368e016 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.7.10-h7f98852_0.tar.bz2#e77615e5141cad5a2acaa043d1cf0ca5 https://conda.anaconda.org/conda-forge/linux-64/freeglut-3.2.2-hac7e632_2.conda#6e553df297f6e64668efb54302e0f139 @@ -139,13 +141,15 @@ https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py310hb13e2d6_0.con https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.6.1-py310hd5c30f3_5.conda#dc2ee770a2299307f3c127af79160d25 https://conda.anaconda.org/conda-forge/noarch/pytest-mock-3.14.0-pyhd8ed1ab_0.conda#4b9b5e086812283c052a9105ab1e254e https://conda.anaconda.org/conda-forge/noarch/requests-2.31.0-pyhd8ed1ab_0.conda#a30144e4156cdbb236f99ebb49828f8b +https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-8.0.4-pyhd8ed1ab_1.conda#a1986ad21c766ff22f7bae93f0641020 https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.3-py310h1f7b6fc_0.conda#31beda75384647959d5792a1a7dc571a https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.2.1-py310hd41b1e2_0.conda#60ee50b1968f802f2a487ba36d4cce0d https://conda.anaconda.org/conda-forge/noarch/dask-core-2024.4.1-pyhd8ed1ab_0.conda#52387f00fee8dcd5cf75f8886025293f https://conda.anaconda.org/conda-forge/linux-64/jasper-4.2.3-he6dfbbe_0.conda#05c1609de571f7a9a40e6d7d4116c21a https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.3.0-py310h2372a71_1.conda#dfcf64f67961eb9686676f96fdb4b4d1 https://conda.anaconda.org/conda-forge/linux-64/scipy-1.13.0-py310hb13e2d6_0.conda#512cfc0369e247e3993a76030671cce0 -https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.3-py310hc3e127f_0.conda#fbc825d13cbcb2d5d3fbba22c83fd203 +https://conda.anaconda.org/conda-forge/noarch/setuptools_scm-8.0.4-hd8ed1ab_1.conda#b065535ca8ca824703ae5537087e2206 +https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.4-py310hc3e127f_0.conda#5cc13dc3d6981b2187616b824e1c5fae https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.2.0-py310h1f7b6fc_4.conda#0ca55ca20891d393846695354b32ebc5 https://conda.anaconda.org/conda-forge/linux-64/eccodes-2.35.0-he84ddb8_0.conda#2d01dfbe537478e7a1a495b9639edd45 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.8.4-py310h62c0568_0.conda#bdfa3aee52579c6b3dde12f52e266ef2 @@ -153,3 +157,4 @@ https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.6.5-nompi_py310hba70d5 https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.23.0-py310hcc13569_0.conda#89a13de526ea3008341e3a2aeab4da7e https://conda.anaconda.org/conda-forge/linux-64/python-eccodes-1.7.0-py310h1f7b6fc_1.conda#f020ac07b7df9c4b0bd0ece3055b5bd2 https://conda.anaconda.org/conda-forge/noarch/iris-3.8.1-pyha770c72_0.conda#b08a116ef1607e7e960a4caa902e3a90 + diff --git a/requirements/locks/py311-linux-64.lock b/requirements/locks/py311-linux-64.lock index 25664a3d..06177d00 100644 --- a/requirements/locks/py311-linux-64.lock +++ b/requirements/locks/py311-linux-64.lock @@ -1,6 +1,6 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: 27ab0ac641e4e7eca8a844e030e7e96ae85c91bc70dc3c0682a6f08459719d5a +# input_hash: 86ed0486ae4fc60e1f46ac8a62982bc103c806ab18fe04e57c4c75fe1eb391d3 @EXPLICIT https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.2.2-hbcca054_0.conda#2f4327a1cbe7f022401b236e915a5fef @@ -53,11 +53,11 @@ https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2. https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-13.2.0-h69a702a_5.conda#e73e9cfd1191783392131e6238bdb3e9 https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.58.0-h47da74e_1.conda#700ac6ea6d53d5510591c4344d5c989a https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.43-h2797004_0.conda#009981dd9cfcaa4dbfa25ffaed86bcae -https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.45.2-h2797004_0.conda#866983a220e27a80cb75e85cb30466a1 +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.45.3-h2797004_0.conda#b3316cbe90249da4f8e84cd66e1cc55b https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.0-h0841786_0.conda#1f5a58e686b13bcfde88b93f547d23fe https://conda.anaconda.org/conda-forge/linux-64/libudunits2-2.2.28-h40f5838_3.conda#4bdace082e911a3e1f1f0b721bed5b56 https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.15-h0b41bf4_0.conda#33277193f5b92bad9fdd230eb700929c -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.12.6-h232c23b_1.conda#6853448e9ca1cfd5f15382afd2a6d123 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.12.6-h232c23b_2.conda#9a3a42df8a95f65334dfc7b80da1195d https://conda.anaconda.org/conda-forge/linux-64/libzip-1.10.1-h2629f0a_3.conda#ac79812548e7e8cf61f7b0abdef01d3b https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda#47d31b792659ce70f470b5c82fdfb7a4 https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda#d453b98d9c83e71da0741bb0ff4d76bc @@ -71,7 +71,7 @@ https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.2-h659d440_0.conda#cd9 https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.27-pthreads_h413a1c8_0.conda#a356024784da6dfd4683dc5ecf45b155 https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.6.0-h1dd3fc0_3.conda#66f03896ffbe1a110ffda05c7a856504 https://conda.anaconda.org/conda-forge/linux-64/python-3.11.8-hab00c5b_0_cpython.conda#2fdc314ee058eda0114738a9309d3683 -https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.45.2-h2c6b66d_0.conda#1423efca06ed343c1da0fc429bae0779 +https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.45.3-h2c6b66d_0.conda#be7d70f2db41b674733667bdd69bd000 https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-h40f5838_3.conda#6bb8deb138f87c9d48320ac21b87e7a1 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.9-h8ee46fc_0.conda#077b6e8ad6a3ddb741fce2496dd01bec https://conda.anaconda.org/conda-forge/noarch/alabaster-0.7.16-pyhd8ed1ab_0.conda#def531a3ac77b7fb8c21d17bb5d0badb @@ -90,7 +90,7 @@ https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.0-pyhd8ed1ab_2. https://conda.anaconda.org/conda-forge/noarch/filelock-3.13.4-pyhd8ed1ab_0.conda#6baa2e7fc09bd2c7c82cb6662d5f1d36 https://conda.anaconda.org/conda-forge/noarch/findlibs-0.0.5-pyhd8ed1ab_0.conda#8f325f63020af6f7acbe2c4cb4c920db https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.3.1-pyhca7485f_0.conda#b7f0662ef2c9d4404f0af9eef5ed2fde -https://conda.anaconda.org/conda-forge/noarch/idna-3.6-pyhd8ed1ab_0.conda#1a76f09108576397c41c0b0c5bd84134 +https://conda.anaconda.org/conda-forge/noarch/idna-3.7-pyhd8ed1ab_0.conda#c0cc1420498b17414d8617d0b9f506ca https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2#7de5386c8fea29e76b303f37dde4c352 https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda#f800d2da156d08e289b14e87e43c1ae5 https://conda.anaconda.org/conda-forge/noarch/iris-sample-data-2.4.0-pyhd8ed1ab_0.tar.bz2#18ee9c07cf945a33f92caf1ee3d23ad9 @@ -118,6 +118,7 @@ https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-2.2.0-pyhd8ed1ab_0 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_0.conda#da1d979339e2714c30a8e806a33ec087 https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96 https://conda.anaconda.org/conda-forge/noarch/toolz-0.12.1-pyhd8ed1ab_0.conda#2fcb582444635e2c402e8569bb94e039 +https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.11.0-pyha770c72_0.conda#6ef2fc37559256cf682d8b3375e89b80 https://conda.anaconda.org/conda-forge/noarch/wheel-0.43.0-pyhd8ed1ab_1.conda#0b5293a157c2b5cd513dd1b03d8d3aae https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.4-h0b41bf4_2.conda#82b6df12252e6f32402b96dacc656fec https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-5.0.3-h7f98852_1004.tar.bz2#e9a21aa4d5e3e5f1aed71e8cefd46b6a @@ -137,6 +138,7 @@ https://conda.anaconda.org/conda-forge/noarch/pip-24.0-pyhd8ed1ab_0.conda#f586ac https://conda.anaconda.org/conda-forge/linux-64/proj-9.3.1-h1d62c97_0.conda#44ec51d0857d9be26158bb85caa74fdb https://conda.anaconda.org/conda-forge/noarch/pytest-8.1.1-pyhd8ed1ab_0.conda#94ff09cdedcb7b17e9cd5097ee2cfcff https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c +https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.11.0-hd8ed1ab_0.conda#471e3988f8ca5e9eb3ce6be7eac3bcee https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.1-pyhd8ed1ab_0.conda#08807a87fa7af10754d46f63b368e016 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.7.10-h7f98852_0.tar.bz2#e77615e5141cad5a2acaa043d1cf0ca5 https://conda.anaconda.org/conda-forge/linux-64/freeglut-3.2.2-hac7e632_2.conda#6e553df297f6e64668efb54302e0f139 @@ -146,13 +148,15 @@ https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py311h64a7726_0.con https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.6.1-py311hca0b8b9_5.conda#cac429fcb9126d5e6f02c8ba61c2a811 https://conda.anaconda.org/conda-forge/noarch/pytest-mock-3.14.0-pyhd8ed1ab_0.conda#4b9b5e086812283c052a9105ab1e254e https://conda.anaconda.org/conda-forge/noarch/requests-2.31.0-pyhd8ed1ab_0.conda#a30144e4156cdbb236f99ebb49828f8b +https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-8.0.4-pyhd8ed1ab_1.conda#a1986ad21c766ff22f7bae93f0641020 https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.3-py311h1f0f07a_0.conda#b7e6d52b39e199238c3400cafaabafb3 https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.2.1-py311h9547e67_0.conda#74ad0ae64f1ef565e27eda87fa749e84 https://conda.anaconda.org/conda-forge/noarch/dask-core-2024.4.1-pyhd8ed1ab_0.conda#52387f00fee8dcd5cf75f8886025293f https://conda.anaconda.org/conda-forge/linux-64/jasper-4.2.3-he6dfbbe_0.conda#05c1609de571f7a9a40e6d7d4116c21a https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.3.0-py311h459d7ec_1.conda#45b8d355bbcdd27588c2d266bcfdff84 https://conda.anaconda.org/conda-forge/linux-64/scipy-1.13.0-py311h64a7726_0.conda#d443c70b4a05f50236c70b9c79beff64 -https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.3-py311h2032efe_0.conda#e982956906078eeac9feb3b8db10d011 +https://conda.anaconda.org/conda-forge/noarch/setuptools_scm-8.0.4-hd8ed1ab_1.conda#b065535ca8ca824703ae5537087e2206 +https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.4-py311h2032efe_0.conda#c99302680ce37b15bcda8152976cb3ba https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.2.0-py311h1f0f07a_4.conda#1e105c1a8ea2163507726144b401eb1b https://conda.anaconda.org/conda-forge/linux-64/eccodes-2.35.0-he84ddb8_0.conda#2d01dfbe537478e7a1a495b9639edd45 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.8.4-py311h54ef318_0.conda#150186110f111b458f86c04361351337 @@ -166,5 +170,6 @@ https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.0.5-pyhd8 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jquery-4.1-pyhd8ed1ab_0.conda#914897066d5873acfb13e75705276ad1 https://conda.anaconda.org/conda-forge/noarch/sphinx_rtd_theme-2.0.0-pyha770c72_0.conda#baf6d9a33df1a789ca55e3b404c7ea28 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-1.0.7-pyhd8ed1ab_0.conda#26acae54b06f178681bfb551760f5dd1 -https://conda.anaconda.org/conda-forge/noarch/sphinx-7.2.6-pyhd8ed1ab_0.conda#bbfd1120d1824d2d073bc65935f0e4c0 +https://conda.anaconda.org/conda-forge/noarch/sphinx-7.3.5-pyhd8ed1ab_1.conda#519304a917d39ff828de7da4b6d21f54 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.10-pyhd8ed1ab_0.conda#e507335cb4ca9cff4c3d0fa9cdab255e + diff --git a/requirements/locks/py39-linux-64.lock b/requirements/locks/py39-linux-64.lock index c3809e15..a6e2a526 100644 --- a/requirements/locks/py39-linux-64.lock +++ b/requirements/locks/py39-linux-64.lock @@ -1,6 +1,6 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: 0ea2e1429acfb3b6481755808a3eb8abce4ab84479aefa81da3fc40044655afb +# input_hash: 7e7dd4cf74148655349102a6a27dccab372cc945b64fe069cbb5be59c905fb6d @EXPLICIT https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.2.2-hbcca054_0.conda#2f4327a1cbe7f022401b236e915a5fef @@ -53,11 +53,11 @@ https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2. https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-13.2.0-h69a702a_5.conda#e73e9cfd1191783392131e6238bdb3e9 https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.58.0-h47da74e_1.conda#700ac6ea6d53d5510591c4344d5c989a https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.43-h2797004_0.conda#009981dd9cfcaa4dbfa25ffaed86bcae -https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.45.2-h2797004_0.conda#866983a220e27a80cb75e85cb30466a1 +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.45.3-h2797004_0.conda#b3316cbe90249da4f8e84cd66e1cc55b https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.0-h0841786_0.conda#1f5a58e686b13bcfde88b93f547d23fe https://conda.anaconda.org/conda-forge/linux-64/libudunits2-2.2.28-h40f5838_3.conda#4bdace082e911a3e1f1f0b721bed5b56 https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.15-h0b41bf4_0.conda#33277193f5b92bad9fdd230eb700929c -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.12.6-h232c23b_1.conda#6853448e9ca1cfd5f15382afd2a6d123 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.12.6-h232c23b_2.conda#9a3a42df8a95f65334dfc7b80da1195d https://conda.anaconda.org/conda-forge/linux-64/libzip-1.10.1-h2629f0a_3.conda#ac79812548e7e8cf61f7b0abdef01d3b https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda#47d31b792659ce70f470b5c82fdfb7a4 https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda#d453b98d9c83e71da0741bb0ff4d76bc @@ -71,7 +71,7 @@ https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.2-h659d440_0.conda#cd9 https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.27-pthreads_h413a1c8_0.conda#a356024784da6dfd4683dc5ecf45b155 https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.6.0-h1dd3fc0_3.conda#66f03896ffbe1a110ffda05c7a856504 https://conda.anaconda.org/conda-forge/linux-64/python-3.9.19-h0755675_0_cpython.conda#d9ee3647fbd9e8595b8df759b2bbefb8 -https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.45.2-h2c6b66d_0.conda#1423efca06ed343c1da0fc429bae0779 +https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.45.3-h2c6b66d_0.conda#be7d70f2db41b674733667bdd69bd000 https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-h40f5838_3.conda#6bb8deb138f87c9d48320ac21b87e7a1 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.9-h8ee46fc_0.conda#077b6e8ad6a3ddb741fce2496dd01bec https://conda.anaconda.org/conda-forge/noarch/antlr-python-runtime-4.11.1-pyhd8ed1ab_0.tar.bz2#15109c4977d39ad7aa3423f57243e286 @@ -88,7 +88,7 @@ https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.0-pyhd8ed1ab_2. https://conda.anaconda.org/conda-forge/noarch/filelock-3.13.4-pyhd8ed1ab_0.conda#6baa2e7fc09bd2c7c82cb6662d5f1d36 https://conda.anaconda.org/conda-forge/noarch/findlibs-0.0.5-pyhd8ed1ab_0.conda#8f325f63020af6f7acbe2c4cb4c920db https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.3.1-pyhca7485f_0.conda#b7f0662ef2c9d4404f0af9eef5ed2fde -https://conda.anaconda.org/conda-forge/noarch/idna-3.6-pyhd8ed1ab_0.conda#1a76f09108576397c41c0b0c5bd84134 +https://conda.anaconda.org/conda-forge/noarch/idna-3.7-pyhd8ed1ab_0.conda#c0cc1420498b17414d8617d0b9f506ca https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda#f800d2da156d08e289b14e87e43c1ae5 https://conda.anaconda.org/conda-forge/noarch/iris-sample-data-2.4.0-pyhd8ed1ab_0.tar.bz2#18ee9c07cf945a33f92caf1ee3d23ad9 https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.5-py39h7633fee_1.conda#c9f74d717e5a2847a9f8b779c54130f2 @@ -111,6 +111,7 @@ https://conda.anaconda.org/conda-forge/noarch/setuptools-69.5.1-pyhd8ed1ab_0.con https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2 https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96 https://conda.anaconda.org/conda-forge/noarch/toolz-0.12.1-pyhd8ed1ab_0.conda#2fcb582444635e2c402e8569bb94e039 +https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.11.0-pyha770c72_0.conda#6ef2fc37559256cf682d8b3375e89b80 https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-15.1.0-py39hd1e30aa_0.conda#1da984bbb6e765743e13388ba7b7b2c8 https://conda.anaconda.org/conda-forge/noarch/wheel-0.43.0-pyhd8ed1ab_1.conda#0b5293a157c2b5cd513dd1b03d8d3aae https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.4-h0b41bf4_2.conda#82b6df12252e6f32402b96dacc656fec @@ -131,6 +132,7 @@ https://conda.anaconda.org/conda-forge/noarch/pip-24.0-pyhd8ed1ab_0.conda#f586ac https://conda.anaconda.org/conda-forge/linux-64/proj-9.3.1-h1d62c97_0.conda#44ec51d0857d9be26158bb85caa74fdb https://conda.anaconda.org/conda-forge/noarch/pytest-8.1.1-pyhd8ed1ab_0.conda#94ff09cdedcb7b17e9cd5097ee2cfcff https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c +https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.11.0-hd8ed1ab_0.conda#471e3988f8ca5e9eb3ce6be7eac3bcee https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.1-pyhd8ed1ab_0.conda#08807a87fa7af10754d46f63b368e016 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.7.10-h7f98852_0.tar.bz2#e77615e5141cad5a2acaa043d1cf0ca5 https://conda.anaconda.org/conda-forge/linux-64/freeglut-3.2.2-hac7e632_2.conda#6e553df297f6e64668efb54302e0f139 @@ -141,13 +143,15 @@ https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py39h474f0d3_0.cond https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.6.1-py39h15b0fa6_5.conda#85e186c7ff673b0d0026782ec353fb2a https://conda.anaconda.org/conda-forge/noarch/pytest-mock-3.14.0-pyhd8ed1ab_0.conda#4b9b5e086812283c052a9105ab1e254e https://conda.anaconda.org/conda-forge/noarch/requests-2.31.0-pyhd8ed1ab_0.conda#a30144e4156cdbb236f99ebb49828f8b +https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-8.0.4-pyhd8ed1ab_1.conda#a1986ad21c766ff22f7bae93f0641020 https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.3-py39h44dd56e_0.conda#baea2f5dfb3ab7b1c836385d2e1daca7 https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.2.1-py39h7633fee_0.conda#bdc188e59857d6efab332714e0d01d93 https://conda.anaconda.org/conda-forge/noarch/dask-core-2024.4.1-pyhd8ed1ab_0.conda#52387f00fee8dcd5cf75f8886025293f https://conda.anaconda.org/conda-forge/linux-64/jasper-4.2.3-he6dfbbe_0.conda#05c1609de571f7a9a40e6d7d4116c21a https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.3.0-py39hd1e30aa_1.conda#ca63612907462c8e36edcc9bbacc253e https://conda.anaconda.org/conda-forge/linux-64/scipy-1.13.0-py39h474f0d3_0.conda#46ae0ecba9726ab4fa44c78fefa522cf -https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.3-py39h6404dd3_0.conda#1520f039123452cd2de847068449a618 +https://conda.anaconda.org/conda-forge/noarch/setuptools_scm-8.0.4-hd8ed1ab_1.conda#b065535ca8ca824703ae5537087e2206 +https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.4-py39h6404dd3_0.conda#c33c60a88dad5df6960c4da482f9e313 https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.2.0-py39h44dd56e_4.conda#81310d21bf9d91754c1220c585bb72d6 https://conda.anaconda.org/conda-forge/linux-64/eccodes-2.35.0-he84ddb8_0.conda#2d01dfbe537478e7a1a495b9639edd45 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.8.4-py39he9076e7_0.conda#1919384a8420e7bb25f6c3a582e0857c @@ -155,3 +159,4 @@ https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.6.5-nompi_py39h4282601 https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.23.0-py39hddac248_0.conda#aa02350ccb542c83272f9cb7e4dfe15e https://conda.anaconda.org/conda-forge/linux-64/python-eccodes-1.7.0-py39h44dd56e_1.conda#629e8d066a330f729d0085d86681fd01 https://conda.anaconda.org/conda-forge/noarch/iris-3.8.1-pyha770c72_0.conda#b08a116ef1607e7e960a4caa902e3a90 + diff --git a/requirements/py310.yml b/requirements/py310.yml index 019bb5af..6e24376a 100644 --- a/requirements/py310.yml +++ b/requirements/py310.yml @@ -8,6 +8,7 @@ dependencies: # Setup dependencies. - setuptools + - setuptools_scm >=7 # Core dependencies. - iris>=3.0.2 diff --git a/requirements/py311.yml b/requirements/py311.yml index 133ba2da..61600305 100644 --- a/requirements/py311.yml +++ b/requirements/py311.yml @@ -8,6 +8,7 @@ dependencies: # Setup dependencies. - setuptools + - setuptools_scm >=7 # Core dependencies. - iris>=3.0.2 diff --git a/requirements/py39.yml b/requirements/py39.yml index 5aedb9fa..302f8305 100644 --- a/requirements/py39.yml +++ b/requirements/py39.yml @@ -8,6 +8,7 @@ dependencies: # Setup dependencies. - setuptools + - setuptools_scm >=7 # Core dependencies. - iris>=3.0.2