Skip to content

Commit

Permalink
Enable UGRID loading always; deprecate PARSE_UGRID_ON_LOAD. (#6054)
Browse files Browse the repository at this point in the history
* Enable UGRID loading always; deprecate PARSE_UGRID_ON_LOAD.

* Fix tests that need unstructured points data, assuming test-data v2.25.

* Fix more tests for always-on ugrid loading.

---------

Co-authored-by: stephenworsley <49274989+stephenworsley@users.noreply.github.com>
  • Loading branch information
pp-mo and stephenworsley authored Jul 22, 2024
1 parent 7d26cc1 commit 2d4eda0
Show file tree
Hide file tree
Showing 19 changed files with 111 additions and 164 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
session: "tests"

env:
IRIS_TEST_DATA_VERSION: "2.22"
IRIS_TEST_DATA_VERSION: "2.25"
ENV_NAME: "ci-tests"

steps:
Expand Down
8 changes: 2 additions & 6 deletions lib/iris/experimental/geovista.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,10 @@ def cube_to_polydata(cube, **kwargs):
.. testsetup::
from iris import load_cube, sample_data_path
from iris.experimental.ugrid import PARSE_UGRID_ON_LOAD
cube = load_cube(sample_data_path("air_temp.pp"))
cube_w_time = load_cube(sample_data_path("A1B_north_america.nc"))
with PARSE_UGRID_ON_LOAD.context():
cube_mesh = load_cube(sample_data_path("mesh_C4_synthetic_float.nc"))
cube_mesh = load_cube(sample_data_path("mesh_C4_synthetic_float.nc"))
>>> from iris.experimental.geovista import cube_to_polydata
Expand Down Expand Up @@ -212,11 +210,9 @@ def extract_unstructured_region(cube, polydata, region, **kwargs):
from iris import load_cube, sample_data_path
from iris.coords import AuxCoord
from iris.cube import CubeList
from iris.experimental.ugrid import PARSE_UGRID_ON_LOAD
file_path = sample_data_path("mesh_C4_synthetic_float.nc")
with PARSE_UGRID_ON_LOAD.context():
cube_w_mesh = load_cube(file_path)
cube_w_mesh = load_cube(file_path)
level_cubes = CubeList()
for height_level in range(72):
Expand Down
6 changes: 6 additions & 0 deletions lib/iris/experimental/ugrid/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@

"""Infra-structure for unstructured mesh support.
.. deprecated:: 1.10
:data:`PARSE_UGRID_ON_LOAD` is due to be removed at next major release.
Please remove all uses of this, which are no longer needed :
UGRID loading is now **always** active for files containing a UGRID mesh.
Based on CF UGRID Conventions (v1.0), https://ugrid-conventions.github.io/ugrid-conventions/.
.. note::
Expand Down
62 changes: 41 additions & 21 deletions lib/iris/experimental/ugrid/load.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
Eventual destination: :mod:`iris.fileformats.netcdf`.
.. seealso::
The UGRID Conventions,
https://ugrid-conventions.github.io/ugrid-conventions/
"""

from contextlib import contextmanager
Expand All @@ -18,6 +23,7 @@
import threading
import warnings

from ..._deprecation import warn_deprecated
from ...config import get_logger
from ...coords import AuxCoord
from ...fileformats._nc_load_rules.helpers import get_attr_units, get_names
Expand Down Expand Up @@ -60,20 +66,20 @@ def __init__(self):
:const:`~iris.experimental.ugrid.load.PARSE_UGRID_ON_LOAD`.
Use :meth:`context` to temporarily activate.
.. seealso::
The UGRID Conventions,
https://ugrid-conventions.github.io/ugrid-conventions/
Notes
-----
.. deprecated:: 1.10
Do not use -- due to be removed at next major release :
UGRID loading is now **always** active for files containing a UGRID mesh.
"""
self._state = False

def __bool__(self):
return self._state
return True

@contextmanager
def context(self):
"""Temporarily activate experimental UGRID-aware NetCDF loading.
"""Activate UGRID-aware NetCDF loading.
Use the standard Iris loading API while within the context manager. If
the loaded file(s) include any UGRID content, this will be parsed and
Expand All @@ -89,12 +95,35 @@ def context(self):
constraint=my_constraint,
callback=my_callback)
Notes
-----
.. deprecated:: 1.10
Do not use -- due to be removed at next major release :
UGRID loading is now **always** active for files containing a UGRID mesh.
Examples
--------
Replace usage, for example:
.. code-block:: python
with iris.experimental.ugrid.PARSE_UGRID_ON_LOAD.context():
mesh_cubes = iris.load(path)
with:
.. code-block:: python
mesh_cubes = iris.load(path)
"""
try:
self._state = True
yield
finally:
self._state = False
wmsg = (
"iris.experimental.ugrid.load.PARSE_UGRID_ON_LOAD has been deprecated "
"and will be removed. Please remove all uses : these are no longer needed, "
"as UGRID loading is now applied to any file containing a mesh."
)
warn_deprecated(wmsg)
yield


#: Run-time switch for experimental UGRID-aware NetCDF loading. See :class:`~iris.experimental.ugrid.load.ParseUGridOnLoad`.
Expand Down Expand Up @@ -174,15 +203,6 @@ def load_meshes(uris, var_name=None):

from ...fileformats import FORMAT_AGENT

if not PARSE_UGRID_ON_LOAD:
# Explicit behaviour, consistent with netcdf.load_cubes(), rather than
# an invisible assumption.
message = (
f"PARSE_UGRID_ON_LOAD is {bool(PARSE_UGRID_ON_LOAD)}. Must be "
f"True to enable mesh loading."
)
raise ValueError(message)

if isinstance(uris, str):
uris = [uris]

Expand Down
4 changes: 1 addition & 3 deletions lib/iris/experimental/ugrid/mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -786,14 +786,12 @@ def from_coords(cls, *coords):
from iris import load_cube, sample_data_path
from iris.experimental.ugrid import (
PARSE_UGRID_ON_LOAD,
MeshXY,
MeshCoord,
)
file_path = sample_data_path("mesh_C4_synthetic_float.nc")
with PARSE_UGRID_ON_LOAD.context():
cube_w_mesh = load_cube(file_path)
cube_w_mesh = load_cube(file_path)
Examples
--------
Expand Down
15 changes: 3 additions & 12 deletions lib/iris/fileformats/netcdf/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,6 @@ def load_cubes(file_sources, callback=None, constraints=None):
# Deferred import to avoid circular imports.
from iris.experimental.ugrid.cf import CFUGridReader
from iris.experimental.ugrid.load import (
PARSE_UGRID_ON_LOAD,
_build_mesh_coords,
_meshes_from_cf,
)
Expand All @@ -600,15 +599,8 @@ def load_cubes(file_sources, callback=None, constraints=None):

for file_source in file_sources:
# Ingest the file. At present may be a filepath or an open netCDF4.Dataset.
meshes = {}
if PARSE_UGRID_ON_LOAD:
cf_reader_class = CFUGridReader
else:
cf_reader_class = iris.fileformats.cf.CFReader

with cf_reader_class(file_source) as cf:
if PARSE_UGRID_ON_LOAD:
meshes = _meshes_from_cf(cf)
with CFUGridReader(file_source) as cf:
meshes = _meshes_from_cf(cf)

# Process each CF data variable.
data_variables = list(cf.cf_group.data_variables.values()) + list(
Expand All @@ -626,8 +618,7 @@ def load_cubes(file_sources, callback=None, constraints=None):
mesh_name = None
mesh = None
mesh_coords, mesh_dim = [], None
if PARSE_UGRID_ON_LOAD:
mesh_name = getattr(cf_var, "mesh", None)
mesh_name = getattr(cf_var, "mesh", None)
if mesh_name is not None:
try:
mesh = meshes[mesh_name]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

from iris import load_cube
from iris.experimental.geovista import cube_to_polydata
from iris.experimental.ugrid import PARSE_UGRID_ON_LOAD
from iris.tests import get_data_path


Expand Down Expand Up @@ -57,8 +56,7 @@ def test_integration_mesh():
]
)

with PARSE_UGRID_ON_LOAD.context():
cube = load_cube(file_path, "conv_rain")
cube = load_cube(file_path, "conv_rain")

polydata = cube_to_polydata(cube[0, :])
# This is a known good output, we have plotted the result and checked it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

from iris import load_cube
from iris.experimental.geovista import cube_to_polydata, extract_unstructured_region
from iris.experimental.ugrid import PARSE_UGRID_ON_LOAD
from iris.tests import get_data_path


Expand All @@ -21,8 +20,7 @@ def test_face_region_extraction():
]
)

with PARSE_UGRID_ON_LOAD.context():
global_cube = load_cube(file_path, "conv_rain")
global_cube = load_cube(file_path, "conv_rain")
polydata = cube_to_polydata(global_cube[0, :])
region = BBox(lons=[0, 70, 70, 0], lats=[-25, -25, 45, 45])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
class TestProjectedUnstructured(tests.IrisTest):
def setUp(self):
path = tests.get_data_path(
("NetCDF", "unstructured_grid", "theta_nodal_xios.nc")
("NetCDF", "unstructured_grid", "theta_nodal_not_ugrid.nc")
)
self.src = iris.load_cube(path, "Potential Temperature")

Expand Down
30 changes: 13 additions & 17 deletions lib/iris/tests/integration/experimental/test_ugrid_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import pytest

from iris import Constraint, load
from iris.experimental.ugrid.load import PARSE_UGRID_ON_LOAD, load_mesh, load_meshes
from iris.experimental.ugrid.load import load_mesh, load_meshes
from iris.experimental.ugrid.mesh import MeshXY
from iris.tests.stock.netcdf import (
_file_from_cdl_template as create_file_from_cdl_template,
Expand Down Expand Up @@ -47,8 +47,7 @@ def ugrid_load(uris, constraints=None, callback=None):
constraints = [constraints]
constraints.append(filter_orphan_connectivities)

with PARSE_UGRID_ON_LOAD.context():
return load(uris, constraints, callback)
return load(uris, constraints, callback)


@tests.skip_data
Expand Down Expand Up @@ -110,12 +109,11 @@ def test_3D_veg_pseudo_levels(self):
)

def test_no_mesh(self):
with PARSE_UGRID_ON_LOAD.context():
cube_list = load(
tests.get_data_path(
["NetCDF", "unstructured_grid", "theta_nodal_not_ugrid.nc"]
)
cube_list = load(
tests.get_data_path(
["NetCDF", "unstructured_grid", "theta_nodal_not_ugrid.nc"]
)
)
self.assertTrue(all([cube.mesh is None for cube in cube_list]))


Expand Down Expand Up @@ -207,10 +205,9 @@ def test_mesh_no_cf_role(self):
@tests.skip_data
class Test_load_mesh(tests.IrisTest):
def common_test(self, file_name, mesh_var_name):
with PARSE_UGRID_ON_LOAD.context():
mesh = load_mesh(
tests.get_data_path(["NetCDF", "unstructured_grid", file_name])
)
mesh = load_mesh(
tests.get_data_path(["NetCDF", "unstructured_grid", file_name])
)
# NOTE: cannot use CML tests as this isn't supported for non-Cubes.
self.assertIsInstance(mesh, MeshXY)
self.assertEqual(mesh.var_name, mesh_var_name)
Expand All @@ -225,12 +222,11 @@ def test_mesh_file(self):
self.common_test("mesh_C12.nc", "dynamics")

def test_no_mesh(self):
with PARSE_UGRID_ON_LOAD.context():
meshes = load_meshes(
tests.get_data_path(
["NetCDF", "unstructured_grid", "theta_nodal_not_ugrid.nc"]
)
meshes = load_meshes(
tests.get_data_path(
["NetCDF", "unstructured_grid", "theta_nodal_not_ugrid.nc"]
)
)
self.assertDictEqual({}, meshes)


Expand Down
10 changes: 3 additions & 7 deletions lib/iris/tests/integration/experimental/test_ugrid_save.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import tempfile

import iris
from iris.experimental.ugrid.load import PARSE_UGRID_ON_LOAD
import iris.fileformats.netcdf
from iris.tests.stock.netcdf import _add_standard_data, ncgen_from_cdl

Expand Down Expand Up @@ -48,8 +47,7 @@ def test_example_result_cdls(self):
# Fill in blank data-variables.
_add_standard_data(target_ncfile_path)
# Load as Iris data
with PARSE_UGRID_ON_LOAD.context():
cubes = iris.load(target_ncfile_path)
cubes = iris.load(target_ncfile_path)
# Re-save, to check the save behaviour.
resave_ncfile_path = str(self.temp_dir / f"{ex_name}_resaved.nc")
iris.save(cubes, resave_ncfile_path)
Expand All @@ -69,8 +67,7 @@ def test_example_roundtrips(self):
# Fill in blank data-variables.
_add_standard_data(target_ncfile_path)
# Load the original as Iris data
with PARSE_UGRID_ON_LOAD.context():
orig_cubes = iris.load(target_ncfile_path)
orig_cubes = iris.load(target_ncfile_path)

if "ex4" in ex_name:
# Discard the extra formula terms component cubes
Expand All @@ -80,8 +77,7 @@ def test_example_roundtrips(self):
# Save-and-load-back to compare the Iris saved result.
resave_ncfile_path = str(self.temp_dir / f"{ex_name}_resaved.nc")
iris.save(orig_cubes, resave_ncfile_path)
with PARSE_UGRID_ON_LOAD.context():
savedloaded_cubes = iris.load(resave_ncfile_path)
savedloaded_cubes = iris.load(resave_ncfile_path)

# This should match the original exactly
# ..EXCEPT for our inability to compare meshes.
Expand Down
2 changes: 1 addition & 1 deletion lib/iris/tests/integration/test_regridding.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def test_nearest(self):
class TestUnstructured(tests.IrisTest):
def setUp(self):
path = tests.get_data_path(
("NetCDF", "unstructured_grid", "theta_nodal_xios.nc")
("NetCDF", "unstructured_grid", "theta_nodal_not_ugrid.nc")
)
self.src = iris.load_cube(path, "Potential Temperature")
self.grid = simple_3d()[0, :, :]
Expand Down
Loading

0 comments on commit 2d4eda0

Please sign in to comment.