diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index b44e740724..ea4b52ce2a 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -38,6 +38,15 @@ This document explains the changes made to Iris for this release #. `@trexfeathers`_ and `Julian Heming`_ added new mappings between CF standard names and UK Met Office LBFC codes. (:pull:`4859`) +#. `@pp-mo`_ changed the metadata of a face/edge-type + :class:`~iris.experimental.ugrid.mesh.MeshCoord`, to be same as the face/edge + coordinate in the mesh from which it takes its ``.points``. Previously, all MeshCoords + took their metadata from the node coord, but only a node-type MeshCoord now does + that. Also, the MeshCoord ``.var_name`` is now that of the underlying coord, whereas + previously this was always None. These changes make MeshCoord more like an ordinary + :class:`~iris.coords.AuxCoord`, which avoids some specific known usage problems. + (:issue:`4860`, :pull:`5020`) + 🐛 Bugs Fixed ============= diff --git a/lib/iris/experimental/ugrid/mesh.py b/lib/iris/experimental/ugrid/mesh.py index 974a563046..4fd09175af 100644 --- a/lib/iris/experimental/ugrid/mesh.py +++ b/lib/iris/experimental/ugrid/mesh.py @@ -2841,16 +2841,60 @@ def __init__( # Get the 'coord identity' metadata from the relevant node-coordinate. node_coord = self.mesh.coord(include_nodes=True, axis=self.axis) + node_metadict = node_coord.metadata._asdict() + # Use node metadata, unless location is face/edge. + use_metadict = node_metadict.copy() + if location != "node": + # Location is either "edge" or "face" - get the relevant coord. + kwargs = {f"include_{location}s": True, "axis": axis} + location_coord = self.mesh.coord(**kwargs) + + # Take the MeshCoord metadata from the 'location' coord. + use_metadict = location_coord.metadata._asdict() + unit_unknown = Unit(None) + + # N.B. at present, coords in a Mesh are stored+accessed by 'axis', which + # means they must have a standard_name. So ... + # (a) the 'location' (face/edge) coord *always* has a useable phenomenon + # identity. + # (b) we still want to check that location+node coords have the same + # phenomenon (i.e. physical meaning identity + units), **but** ... + # (c) we will accept/ignore some differences : not just "var_name", but + # also "long_name" *and* "attributes". So it is *only* "standard_name" + # and "units" that cause an error if they differ. + for key in ("standard_name", "units"): + bounds_value = use_metadict[key] + nodes_value = node_metadict[key] + if key == "units" and ( + bounds_value == unit_unknown or nodes_value == unit_unknown + ): + # Allow "any" unit to match no-units (for now) + continue + if bounds_value != nodes_value: + + def fix_repr(val): + # Tidy values appearance by converting Unit to string, and + # wrapping strings in '', but leaving other types as a + # plain str() representation. + if isinstance(val, Unit): + val = str(val) + if isinstance(val, str): + val = repr(val) + return val + + nodes_value, bounds_value = [ + fix_repr(val) for val in (nodes_value, bounds_value) + ] + msg = ( + f"Node coordinate {node_coord!r} disagrees with the " + f"{location} coordinate {location_coord!r}, " + f'in having a "{key}" value of {nodes_value} ' + f"instead of {bounds_value}." + ) + raise ValueError(msg) + # Call parent constructor to handle the common constructor args. - super().__init__( - points, - bounds=bounds, - standard_name=node_coord.standard_name, - long_name=node_coord.long_name, - var_name=None, # We *don't* "represent" the underlying node var - units=node_coord.units, - attributes=node_coord.attributes, - ) + super().__init__(points, bounds=bounds, **use_metadict) # Define accessors for MeshCoord-specific properties mesh/location/axis. # These are all read-only. diff --git a/lib/iris/tests/results/experimental/ugrid/2D_1t_face_half_levels.cml b/lib/iris/tests/results/experimental/ugrid/2D_1t_face_half_levels.cml index b863adcf55..7422bfe044 100644 --- a/lib/iris/tests/results/experimental/ugrid/2D_1t_face_half_levels.cml +++ b/lib/iris/tests/results/experimental/ugrid/2D_1t_face_half_levels.cml @@ -20,8 +20,8 @@ ..., [-42.7342, -40.8934, -46.161, -48.912], [-40.8934, -38.4268, -42.6612, -46.161], - [-38.4268, -35.2644, -38.4268, -42.6612]]" id="21594c35" long_name="Latitude of mesh nodes." points="[33.4328, 36.1226, 38.2012, ..., -44.791, - -42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-38.4268, -35.2644, -38.4268, -42.6612]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[33.4328, 36.1226, 38.2012, ..., -44.791, + -42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [-127.5, -135.0, -142.5, -135.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[-41.3152, -33.8068, -26.296, ..., -119.377, + -127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> diff --git a/lib/iris/tests/results/experimental/ugrid/2D_72t_face_half_levels.cml b/lib/iris/tests/results/experimental/ugrid/2D_72t_face_half_levels.cml index b46908a648..f9e0511ccb 100644 --- a/lib/iris/tests/results/experimental/ugrid/2D_72t_face_half_levels.cml +++ b/lib/iris/tests/results/experimental/ugrid/2D_72t_face_half_levels.cml @@ -20,8 +20,8 @@ ..., [-42.7342, -40.8934, -46.161, -48.912], [-40.8934, -38.4268, -42.6612, -46.161], - [-38.4268, -35.2644, -38.4268, -42.6612]]" id="21594c35" long_name="Latitude of mesh nodes." points="[33.4328, 36.1226, 38.2012, ..., -44.791, - -42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-38.4268, -35.2644, -38.4268, -42.6612]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[33.4328, 36.1226, 38.2012, ..., -44.791, + -42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [-127.5, -135.0, -142.5, -135.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[-41.3152, -33.8068, -26.296, ..., -119.377, + -127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> + [-38.4268, -35.2644, -38.4268, -42.6612]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[33.4328, 36.1226, 38.2012, ..., -44.791, + -42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_full_levels_face_y"/> + [-127.5, -135.0, -142.5, -135.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[-41.3152, -33.8068, -26.296, ..., -119.377, + -127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_full_levels_face_x"/> diff --git a/lib/iris/tests/results/experimental/ugrid/3D_1t_face_half_levels.cml b/lib/iris/tests/results/experimental/ugrid/3D_1t_face_half_levels.cml index c260587921..9a819eee9e 100644 --- a/lib/iris/tests/results/experimental/ugrid/3D_1t_face_half_levels.cml +++ b/lib/iris/tests/results/experimental/ugrid/3D_1t_face_half_levels.cml @@ -31,8 +31,8 @@ ..., [-42.7342, -40.8934, -46.161, -48.912], [-40.8934, -38.4268, -42.6612, -46.161], - [-38.4268, -35.2644, -38.4268, -42.6612]]" id="21594c35" long_name="Latitude of mesh nodes." points="[33.4328, 36.1226, 38.2012, ..., -44.791, - -42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-38.4268, -35.2644, -38.4268, -42.6612]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[33.4328, 36.1226, 38.2012, ..., -44.791, + -42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [-127.5, -135.0, -142.5, -135.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[-41.3152, -33.8068, -26.296, ..., -119.377, + -127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> diff --git a/lib/iris/tests/results/experimental/ugrid/3D_snow_pseudo_levels.cml b/lib/iris/tests/results/experimental/ugrid/3D_snow_pseudo_levels.cml index e545e05fdc..9133d98e73 100644 --- a/lib/iris/tests/results/experimental/ugrid/3D_snow_pseudo_levels.cml +++ b/lib/iris/tests/results/experimental/ugrid/3D_snow_pseudo_levels.cml @@ -20,8 +20,8 @@ ..., [-42.7342, -40.8934, -46.161, -48.912], [-40.8934, -38.4268, -42.6612, -46.161], - [-38.4268, -35.2644, -38.4268, -42.6612]]" id="21594c35" long_name="Latitude of mesh nodes." points="[33.4328, 36.1226, 38.2012, ..., -44.791, - -42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-38.4268, -35.2644, -38.4268, -42.6612]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[33.4328, 36.1226, 38.2012, ..., -44.791, + -42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [-127.5, -135.0, -142.5, -135.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[-41.3152, -33.8068, -26.296, ..., -119.377, + -127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> diff --git a/lib/iris/tests/results/experimental/ugrid/3D_soil_pseudo_levels.cml b/lib/iris/tests/results/experimental/ugrid/3D_soil_pseudo_levels.cml index 4eedfc21b3..05aeab9ccb 100644 --- a/lib/iris/tests/results/experimental/ugrid/3D_soil_pseudo_levels.cml +++ b/lib/iris/tests/results/experimental/ugrid/3D_soil_pseudo_levels.cml @@ -20,8 +20,8 @@ ..., [-42.7342, -40.8934, -46.161, -48.912], [-40.8934, -38.4268, -42.6612, -46.161], - [-38.4268, -35.2644, -38.4268, -42.6612]]" id="21594c35" long_name="Latitude of mesh nodes." points="[33.4328, 36.1226, 38.2012, ..., -44.791, - -42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-38.4268, -35.2644, -38.4268, -42.6612]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[33.4328, 36.1226, 38.2012, ..., -44.791, + -42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [-127.5, -135.0, -142.5, -135.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[-41.3152, -33.8068, -26.296, ..., -119.377, + -127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> diff --git a/lib/iris/tests/results/experimental/ugrid/3D_tile_pseudo_levels.cml b/lib/iris/tests/results/experimental/ugrid/3D_tile_pseudo_levels.cml index 55155047bb..9dc3e08ee6 100644 --- a/lib/iris/tests/results/experimental/ugrid/3D_tile_pseudo_levels.cml +++ b/lib/iris/tests/results/experimental/ugrid/3D_tile_pseudo_levels.cml @@ -20,8 +20,8 @@ ..., [-42.7342, -40.8934, -46.161, -48.912], [-40.8934, -38.4268, -42.6612, -46.161], - [-38.4268, -35.2644, -38.4268, -42.6612]]" id="21594c35" long_name="Latitude of mesh nodes." points="[33.4328, 36.1226, 38.2012, ..., -44.791, - -42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-38.4268, -35.2644, -38.4268, -42.6612]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[33.4328, 36.1226, 38.2012, ..., -44.791, + -42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [-127.5, -135.0, -142.5, -135.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[-41.3152, -33.8068, -26.296, ..., -119.377, + -127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> diff --git a/lib/iris/tests/results/experimental/ugrid/3D_veg_pseudo_levels.cml b/lib/iris/tests/results/experimental/ugrid/3D_veg_pseudo_levels.cml index fc52fce0b3..7bb47c5296 100644 --- a/lib/iris/tests/results/experimental/ugrid/3D_veg_pseudo_levels.cml +++ b/lib/iris/tests/results/experimental/ugrid/3D_veg_pseudo_levels.cml @@ -20,8 +20,8 @@ ..., [-42.7342, -40.8934, -46.161, -48.912], [-40.8934, -38.4268, -42.6612, -46.161], - [-38.4268, -35.2644, -38.4268, -42.6612]]" id="21594c35" long_name="Latitude of mesh nodes." points="[33.4328, 36.1226, 38.2012, ..., -44.791, - -42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-38.4268, -35.2644, -38.4268, -42.6612]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[33.4328, 36.1226, 38.2012, ..., -44.791, + -42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [-127.5, -135.0, -142.5, -135.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[-41.3152, -33.8068, -26.296, ..., -119.377, + -127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> diff --git a/lib/iris/tests/results/experimental/ugrid/surface_mean.cml b/lib/iris/tests/results/experimental/ugrid/surface_mean.cml index 368b3508e3..8ccd602c11 100644 --- a/lib/iris/tests/results/experimental/ugrid/surface_mean.cml +++ b/lib/iris/tests/results/experimental/ugrid/surface_mean.cml @@ -20,8 +20,8 @@ ..., [-37.7044, -36.9373, -37.9318, -38.7655], [-36.9373, -36.1244, -37.0517, -37.9318], - [-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, - -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, + -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759, + 226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> @@ -71,8 +71,8 @@ ..., [-37.7044, -36.9373, -37.9318, -38.7655], [-36.9373, -36.1244, -37.0517, -37.9318], - [-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, - -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, + -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759, + 226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> @@ -122,8 +122,8 @@ ..., [-37.7044, -36.9373, -37.9318, -38.7655], [-36.9373, -36.1244, -37.0517, -37.9318], - [-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, - -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, + -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759, + 226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> @@ -173,8 +173,8 @@ ..., [-37.7044, -36.9373, -37.9318, -38.7655], [-36.9373, -36.1244, -37.0517, -37.9318], - [-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, - -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, + -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759, + 226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> @@ -224,8 +224,8 @@ ..., [-37.7044, -36.9373, -37.9318, -38.7655], [-36.9373, -36.1244, -37.0517, -37.9318], - [-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, - -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, + -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759, + 226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> @@ -275,8 +275,8 @@ ..., [-37.7044, -36.9373, -37.9318, -38.7655], [-36.9373, -36.1244, -37.0517, -37.9318], - [-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, - -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, + -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759, + 226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> @@ -326,8 +326,8 @@ ..., [-37.7044, -36.9373, -37.9318, -38.7655], [-36.9373, -36.1244, -37.0517, -37.9318], - [-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, - -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, + -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759, + 226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> @@ -377,8 +377,8 @@ ..., [-37.7044, -36.9373, -37.9318, -38.7655], [-36.9373, -36.1244, -37.0517, -37.9318], - [-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, - -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, + -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759, + 226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> @@ -428,8 +428,8 @@ ..., [-37.7044, -36.9373, -37.9318, -38.7655], [-36.9373, -36.1244, -37.0517, -37.9318], - [-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, - -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, + -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759, + 226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> @@ -479,8 +479,8 @@ ..., [-37.7044, -36.9373, -37.9318, -38.7655], [-36.9373, -36.1244, -37.0517, -37.9318], - [-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, - -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, + -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759, + 226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> @@ -530,8 +530,8 @@ ..., [-37.7044, -36.9373, -37.9318, -38.7655], [-36.9373, -36.1244, -37.0517, -37.9318], - [-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, - -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, + -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759, + 226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> @@ -581,8 +581,8 @@ ..., [-37.7044, -36.9373, -37.9318, -38.7655], [-36.9373, -36.1244, -37.0517, -37.9318], - [-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, - -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, + -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759, + 226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> @@ -632,8 +632,8 @@ ..., [-37.7044, -36.9373, -37.9318, -38.7655], [-36.9373, -36.1244, -37.0517, -37.9318], - [-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, - -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, + -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759, + 226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> @@ -683,8 +683,8 @@ ..., [-37.7044, -36.9373, -37.9318, -38.7655], [-36.9373, -36.1244, -37.0517, -37.9318], - [-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, - -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, + -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759, + 226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> @@ -734,8 +734,8 @@ ..., [-37.7044, -36.9373, -37.9318, -38.7655], [-36.9373, -36.1244, -37.0517, -37.9318], - [-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, - -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, + -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759, + 226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> @@ -785,8 +785,8 @@ ..., [-37.7044, -36.9373, -37.9318, -38.7655], [-36.9373, -36.1244, -37.0517, -37.9318], - [-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, - -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, + -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759, + 226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> @@ -836,8 +836,8 @@ ..., [-37.7044, -36.9373, -37.9318, -38.7655], [-36.9373, -36.1244, -37.0517, -37.9318], - [-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, - -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, + -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759, + 226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> @@ -887,8 +887,8 @@ ..., [-37.7044, -36.9373, -37.9318, -38.7655], [-36.9373, -36.1244, -37.0517, -37.9318], - [-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, - -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/> + [-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421, + -37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/> + [226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759, + 226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/> diff --git a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_all_dims.cml b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_all_dims.cml index 9fc80a0e4d..e318abad67 100644 --- a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_all_dims.cml +++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_all_dims.cml @@ -50,12 +50,7 @@ - - - - - - + - - - - - + [1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/> - - - - - + - - - - - + [1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/> - - - - - + [1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/> - - - - - + [1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/> - - - - - + [1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/> - - - - - + [1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/> - - - - - + - - - - - + [1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/> - - - - - + - - - - - + [1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/> - - - - - + [1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/> - - - - - + [1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/> - - - - - + [1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/> - - - - - + [1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/> " ), - "MeshCoord : longitude / (degrees_east)", + "MeshCoord : longitude / (unknown)", " mesh: ", " location: 'face'", " points: [3100, 3101, 3102]", @@ -926,10 +926,6 @@ def test_meshcoord(self): " shape: (3,) bounds(3, 4)", " dtype: int64", " standard_name: 'longitude'", - " long_name: 'long-name'", - " attributes:", - " a 1", - " b 'c'", " axis: 'x'", ] self.assertLines(expected, result) diff --git a/lib/iris/tests/unit/experimental/ugrid/mesh/test_MeshCoord.py b/lib/iris/tests/unit/experimental/ugrid/mesh/test_MeshCoord.py index ce99a8b4be..538cecdc7d 100644 --- a/lib/iris/tests/unit/experimental/ugrid/mesh/test_MeshCoord.py +++ b/lib/iris/tests/unit/experimental/ugrid/mesh/test_MeshCoord.py @@ -16,9 +16,10 @@ import dask.array as da import numpy as np +import pytest from iris._lazy_data import as_lazy_data, is_lazy_data -from iris.common.metadata import BaseMetadata +from iris.common.metadata import BaseMetadata, CoordMetadata from iris.coords import AuxCoord, Coord from iris.cube import Cube from iris.experimental.ugrid.mesh import Connectivity, Mesh, MeshCoord @@ -45,16 +46,11 @@ def test_derived_properties(self): # underlying mesh coordinate. for axis in Mesh.AXES: meshcoord = sample_meshcoord(axis=axis) - # N.B. - node_x_coord = meshcoord.mesh.coord(include_nodes=True, axis=axis) - for key in node_x_coord.metadata._fields: + face_x_coord = meshcoord.mesh.coord(include_faces=True, axis=axis) + for key in face_x_coord.metadata._fields: meshval = getattr(meshcoord, key) - if key == "var_name": - # var_name is unused. - self.assertIsNone(meshval) - else: - # names, units and attributes are derived from the node coord. - self.assertEqual(meshval, getattr(node_x_coord, key)) + # All relevant attributes are derived from the face coord. + self.assertEqual(meshval, getattr(face_x_coord, key)) def test_fail_bad_mesh(self): with self.assertRaisesRegex(TypeError, "must be a.*Mesh"): @@ -270,10 +266,11 @@ def setUp(self): def _expected_elements_regexp( self, standard_name="longitude", - long_name="long-name", - attributes=True, + long_name=None, + attributes=False, location="face", axis="x", + var_name=None, ): # Printed name is standard or long -- we don't have a case with neither coord_name = standard_name or long_name @@ -282,24 +279,30 @@ def _expected_elements_regexp( regexp = f"MeshCoord : {coord_name} / [^\n]+\n *" regexp += r"mesh: \\n *" regexp += f"location: '{location}'\n *" + # Now some optional sections : whichever comes first will match # arbitrary content leading up to it. - matched_any_upto = False - if standard_name: - regexp += ".*" - matched_any_upto = True - regexp += f"standard_name: '{standard_name}'\n *" - if long_name: + matched_upto = False + + def upto_first_expected(regexp, matched_any_upto): if not matched_any_upto: regexp += ".*" matched_any_upto = True + return regexp, matched_any_upto + + if standard_name: + regexp, matched_upto = upto_first_expected(regexp, matched_upto) + regexp += f"standard_name: '{standard_name}'\n *" + if long_name: + regexp, matched_upto = upto_first_expected(regexp, matched_upto) regexp += f"long_name: '{long_name}'\n *" + if var_name: + regexp, matched_upto = upto_first_expected(regexp, matched_upto) + regexp += f"var_name: '{var_name}'\n *" if attributes: # if we expected attributes, they should come next # TODO: change this when each attribute goes on a new line - if not matched_any_upto: - regexp += ".*" - matched_any_upto = True + regexp, matched_upto = upto_first_expected(regexp, matched_upto) # match 'attributes:' followed by N*lines with larger indent regexp += "attributes:(\n [^ \n]+ +[^ \n]+)+\n " # After those items, expect 'axis' next @@ -314,7 +317,7 @@ def test_repr(self): # A simple check for the condensed form. result = repr(self.meshcoord) expected = ( - "" ) self.assertEqual(expected, result) @@ -331,7 +334,7 @@ def test_repr_lazy(self): self.assertTrue(self.meshcoord.has_lazy_bounds()) expected = ( - "+bounds shape(3,)>" ) self.assertEqual(expected, result) @@ -342,7 +345,7 @@ def test_repr__nameless_mesh(self): assert self.mesh.name() == "unknown" result = repr(self.meshcoord) re_expected = ( - r".MeshCoord: longitude / \(degrees_east\) " + r".MeshCoord: longitude / \(unknown\) " r"mesh\(.Mesh object at 0x[^>]+.\) location\(face\) " ) self.assertRegex(result, re_expected) @@ -392,18 +395,6 @@ def test_str_no_long_name(self): re_expected = self._expected_elements_regexp(long_name=False) self.assertRegex(result, re_expected) - def test_str_no_standard_name(self): - mesh = self.mesh - # Remove the standard_name of the node coord in the mesh. - node_coord = mesh.coord(include_nodes=True, axis="x") - node_coord.standard_name = None - node_coord.axis = "x" # This is required : but it's a kludge !! - # Make a new meshcoord, based on the modified mesh. - meshcoord = sample_meshcoord(mesh=self.mesh) - result = str(meshcoord) - re_expected = self._expected_elements_regexp(standard_name=False) - self.assertRegex(result, re_expected) - def test_str_no_attributes(self): mesh = self.mesh # No attributes on the node coord in the mesh. @@ -451,9 +442,11 @@ def test_cube_dims(self): def test_find_by_name(self): meshcoord = self.meshcoord + # hack to give it a long name + meshcoord.long_name = "odd_case" cube = self.cube self.assertIs(cube.coord(standard_name="longitude"), meshcoord) - self.assertIs(cube.coord(long_name="long-name"), meshcoord) + self.assertIs(cube.coord(long_name="odd_case"), meshcoord) def test_find_by_axis(self): meshcoord = self.meshcoord @@ -796,5 +789,157 @@ def test_bounds_badvalues__lazy(self): self._check_bounds_bad_index_values(lazy=True) +class Test__metadata: + def setup_mesh(self, location, axis): + # Create a standard test mesh + attach it to the test instance. + mesh = sample_mesh() + + # Modify the metadata of specific coordinates used in this test. + def select_coord(location, axis): + kwargs = {f"include_{location}s": True, "axis": axis} + return mesh.coord(**kwargs) + + node_coord = select_coord("node", axis) + location_coord = select_coord(location, axis) + for i_place, coord in enumerate((node_coord, location_coord)): + coord.standard_name = "longitude" if axis == "x" else "latitude" + coord.units = "degrees" + coord.long_name = f"long_name_{i_place}" + coord.var_name = f"var_name_{i_place}" + coord.attributes = {"att": i_place} + + # attach all the relevant testcase context to the test instance. + self.mesh = mesh + self.location = location + self.axis = axis + self.location_coord = location_coord + self.node_coord = node_coord + + def coord_metadata_matches(self, test_coord, ref_coord): + # Check that two coords match, in all the basic Coord identity/phenomenon + # metadata fields -- so it works even between coords of different subclasses. + for key in CoordMetadata._fields: + assert getattr(test_coord, key) == getattr(ref_coord, key) + + @pytest.fixture(params=["face", "edge"]) + def location_face_or_edge(self, request): + # Fixture to parametrise over location = face/edge + return request.param + + @pytest.fixture(params=["x", "y"]) + def axis_x_or_y(self, request): + # Fixture to parametrise over axis = X/Y + return request.param + + def test_node_meshcoord(self, axis_x_or_y): + # MeshCoord metadata matches that of the relevant node coord. + self.setup_mesh(location="node", axis=axis_x_or_y) + meshcoord = self.mesh.to_MeshCoord( + location=self.location, axis=self.axis + ) + self.coord_metadata_matches(meshcoord, self.node_coord) + + def test_faceedge_basic(self, location_face_or_edge, axis_x_or_y): + # MeshCoord metadata matches that of the face/edge ("points") coord. + self.setup_mesh(location_face_or_edge, axis_x_or_y) + meshcoord = self.mesh.to_MeshCoord( + location=self.location, axis=self.axis + ) + self.coord_metadata_matches(meshcoord, self.location_coord) + + @pytest.mark.parametrize( + "fieldname", ["long_name", "var_name", "attributes"] + ) + def test_faceedge_dontcare_fields( + self, location_face_or_edge, axis_x_or_y, fieldname + ): + # Check that it's ok for the face/edge and node coords to have different + # long-name, var-name or attributes. + self.setup_mesh(location_face_or_edge, axis_x_or_y) + if fieldname == "attributes": + different_value = {"myattrib": "different attributes"} + else: + # others are just arbitrary strings. + different_value = "different" + setattr(self.location_coord, fieldname, different_value) + # Mostly.. just check this does not cause an error, as it would do if we + # modified "standard_name" or "units" (see other tests) ... + meshcoord = self.mesh.to_MeshCoord( + location=self.location, axis=self.axis + ) + # ... but also, check that the result matches the expected face/edge coord. + self.coord_metadata_matches(meshcoord, self.location_coord) + + def test_faceedge_fail_mismatched_stdnames( + self, location_face_or_edge, axis_x_or_y + ): + # Different "standard_name" for node and face/edge causes an error. + self.setup_mesh(location_face_or_edge, axis_x_or_y) + node_name = f"projection_{axis_x_or_y}_coordinate" + self.node_coord.standard_name = node_name + location_name = "longitude" if axis_x_or_y == "x" else "latitude" + msg = ( + "Node coordinate .*" + f"disagrees with the {location_face_or_edge} coordinate .*, " + 'in having a "standard_name" value of ' + f"'{node_name}' instead of '{location_name}'" + ) + with pytest.raises(ValueError, match=msg): + self.mesh.to_MeshCoord( + location=location_face_or_edge, axis=axis_x_or_y + ) + + def test_faceedge_fail_missing_stdnames( + self, location_face_or_edge, axis_x_or_y + ): + # "standard_name" compared with None also causes an error. + self.setup_mesh(location_face_or_edge, axis_x_or_y) + self.node_coord.standard_name = None + # N.B. in the absence of a standard-name, we **must** provide an extra ".axis" + # property, or the coordinate cannot be correctly identified in the Mesh. + # This is a bit of a kludge, but works with current code. + self.node_coord.axis = axis_x_or_y + + location_name = "longitude" if axis_x_or_y == "x" else "latitude" + msg = ( + "Node coordinate .*" + f"disagrees with the {location_face_or_edge} coordinate .*, " + 'in having a "standard_name" value of ' + f"None instead of '{location_name}'" + ) + with pytest.raises(ValueError, match=msg): + self.mesh.to_MeshCoord( + location=location_face_or_edge, axis=axis_x_or_y + ) + + def test_faceedge_fail_mismatched_units( + self, location_face_or_edge, axis_x_or_y + ): + # Different "units" for node and face/edge causes an error. + self.setup_mesh(location_face_or_edge, axis_x_or_y) + self.node_coord.units = "hPa" + msg = ( + "Node coordinate .*" + f"disagrees with the {location_face_or_edge} coordinate .*, " + 'in having a "units" value of ' + "'hPa' instead of 'degrees'" + ) + with pytest.raises(ValueError, match=msg): + self.mesh.to_MeshCoord( + location=location_face_or_edge, axis=axis_x_or_y + ) + + def test_faceedge_missing_units(self, location_face_or_edge, axis_x_or_y): + # Units compared with a None ("unknown") is not an error. + self.setup_mesh(location_face_or_edge, axis_x_or_y) + self.node_coord.units = None + # This is OK + meshcoord = self.mesh.to_MeshCoord( + location=self.location, axis=self.axis + ) + # ... but also, check that the result matches the expected face/edge coord. + self.coord_metadata_matches(meshcoord, self.location_coord) + + if __name__ == "__main__": tests.main()