Skip to content
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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mesh saveload fix #6004

Merged
merged 8 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add tests for more intelligible errors.
  • Loading branch information
pp-mo committed Jun 12, 2024
commit a4dad3aeb0428c954fe255133f017f0680d16495
2 changes: 1 addition & 1 deletion lib/iris/experimental/ugrid/load.py
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ def _build_mesh_coords(mesh, cf_var):
# We should probably issue warnings and recover, but that is too much
# work. Raising a more intelligible error is easy to do though.
msg = (
f"mesh data variable {cf_var.name!s} has an invalid "
f"mesh data variable {cf_var.name!r} has an invalid "
f"location={location!r}."
)
raise ValueError(msg)
Expand Down
84 changes: 46 additions & 38 deletions lib/iris/tests/unit/experimental/ugrid/load/test_load_meshes.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,50 +27,58 @@ def tearDownModule():
rmtree(TMP_DIR)


def cdl_to_nc(cdl):
cdl_path = str(TMP_DIR / "tst.cdl")
nc_path = str(TMP_DIR / f"{uuid4()}.nc")
def cdl_to_nc(cdl, tmpdir=None):
if tmpdir is None:
tmpdir = TMP_DIR
cdl_path = str(tmpdir / "tst.cdl")
nc_path = str(tmpdir / f"{uuid4()}.nc")
# Use ncgen to convert this into an actual (temporary) netCDF file.
ncgen_from_cdl(cdl_str=cdl, cdl_path=cdl_path, nc_path=nc_path)
return nc_path


class TestsBasic(tests.IrisTest):
_TEST_CDL_HEAD = """
netcdf mesh_test {
dimensions:
node = 3 ;
face = 1 ;
vertex = 3 ;
levels = 2 ;
variables:
int mesh ;
mesh:cf_role = "mesh_topology" ;
mesh:topology_dimension = 2 ;
mesh:node_coordinates = "node_x node_y" ;
mesh:face_node_connectivity = "face_nodes" ;
float node_x(node) ;
node_x:standard_name = "longitude" ;
float node_y(node) ;
node_y:standard_name = "latitude" ;
int face_nodes(face, vertex) ;
face_nodes:cf_role = "face_node_connectivity" ;
face_nodes:start_index = 0 ;
int levels(levels) ;
float node_data(levels, node) ;
node_data:coordinates = "node_x node_y" ;
node_data:location = "node" ;
node_data:mesh = "mesh" ;
"""

_TEST_CDL_TAIL = """
data:
mesh = 0;
node_x = 0., 2., 1.;
node_y = 0., 0., 1.;
face_nodes = 0, 1, 2;
levels = 1, 2;
node_data = 0., 0., 0.;
}
"""


class TestLoadErrors(tests.IrisTest):
def setUp(self):
self.ref_cdl = """
netcdf mesh_test {
dimensions:
node = 3 ;
face = 1 ;
vertex = 3 ;
levels = 2 ;
variables:
int mesh ;
mesh:cf_role = "mesh_topology" ;
mesh:topology_dimension = 2 ;
mesh:node_coordinates = "node_x node_y" ;
mesh:face_node_connectivity = "face_nodes" ;
float node_x(node) ;
node_x:standard_name = "longitude" ;
float node_y(node) ;
node_y:standard_name = "latitude" ;
int face_nodes(face, vertex) ;
face_nodes:cf_role = "face_node_connectivity" ;
face_nodes:start_index = 0 ;
int levels(levels) ;
float node_data(levels, node) ;
node_data:coordinates = "node_x node_y" ;
node_data:location = "node" ;
node_data:mesh = "mesh" ;
data:
mesh = 0;
node_x = 0., 2., 1.;
node_y = 0., 0., 1.;
face_nodes = 0, 1, 2;
levels = 1, 2;
node_data = 0., 0., 0.;
}
"""
self.ref_cdl = _TEST_CDL_HEAD + _TEST_CDL_TAIL
self.nc_path = cdl_to_nc(self.ref_cdl)

def add_second_mesh(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import pytest

import iris
from iris.experimental.ugrid.load import PARSE_UGRID_ON_LOAD

from .test_load_meshes import (
_TEST_CDL_HEAD,
_TEST_CDL_TAIL,
cdl_to_nc,
)


class TestMeshLoad:
def _create_testnc(self, location="node", meshdim="node"):
# Add an extra (possibly mal-formed) mesh data to the testfile.
if location is None:
location_cdl = ""
else:
location_cdl = f'extra_data:location = "{location}" ;'

extra_cdl = f"""
float extra_data(levels, {meshdim}) ;
extra_data:coordinates = "node_x node_y" ;
{location_cdl}
extra_data:mesh = "mesh" ;
"""
# Insert this into the definitions part of the 'standard' testfile CDL
extended_cdl = _TEST_CDL_HEAD + extra_cdl + _TEST_CDL_TAIL
testfile_path = cdl_to_nc(extended_cdl, tmpdir=self.tmpdir)
return testfile_path

@pytest.fixture(params=["nolocation", "badlocation", "baddim"])
def failnc(self, request, tmp_path_factory):
self.param = request.param
kwargs = {}
if self.param == "nolocation":
kwargs["location"] = None
elif self.param == "badlocation":
kwargs["location"] = "invalid_location"
elif self.param == "baddim":
kwargs["meshdim"] = "vertex"
else:
raise ValueError(f"unexpected param: {self.param}")

self.tmpdir = tmp_path_factory.mktemp("meshload")
return self._create_testnc(**kwargs)

def test_extrameshvar__ok(self, tmp_path_factory):
# Check that the default cdl construction loads OK
self.tmpdir = tmp_path_factory.mktemp("meshload")
testnc = self._create_testnc()
with PARSE_UGRID_ON_LOAD.context():
iris.load(testnc)

def test_extrameshvar__fail(self, failnc):
# Check that the expected errors are raised in various cases.
param = self.param
if param == "nolocation":
match_msg = (
"mesh data variable 'extra_data' has an " "invalid location='<empty>'."
)
elif param == "badlocation":
match_msg = (
"mesh data variable 'extra_data' has an "
"invalid location='invalid_location'."
)
elif param == "baddim":
match_msg = (
"mesh data variable 'extra_data' does not have the node mesh "
"dimension 'node', in its dimensions."
)
else:
raise ValueError(f"unexpected param: {param}")

with PARSE_UGRID_ON_LOAD.context():
with pytest.raises(ValueError, match=match_msg):
iris.load(failnc)
Loading