diff --git a/lib/iris/common/metadata.py b/lib/iris/common/metadata.py index 801ba57c44..024b88d4de 100644 --- a/lib/iris/common/metadata.py +++ b/lib/iris/common/metadata.py @@ -1354,47 +1354,50 @@ def metadata_filter( ): """ Filter a collection of objects by their metadata to fit the given metadata - criteria. Criteria can be one or both of: specific properties / other objects - carrying metadata to be matched. + criteria. + + Criteria can be either specific properties or other objects with metadata + to be matched. Args: - * instances + * instances: One or more objects to be filtered. Kwargs: - * item - Either - - (a) a :attr:`standard_name`, :attr:`long_name`, or - :attr:`var_name`. Defaults to value of `default` - (which itself defaults to `unknown`) as defined in - :class:`~iris.common.CFVariableMixin`. - - (b) a 'coordinate' instance with metadata equal to that of - the desired coordinates. Accepts either a - :class:`~iris.coords.DimCoord`, :class:`~iris.coords.AuxCoord`, - :class:`~iris.aux_factory.AuxCoordFactory`, - :class:`~iris.common.CoordMetadata` or - :class:`~iris.common.DimCoordMetadata` or - :class:`~iris.experimental.ugrid.ConnectivityMetadata`. - * standard_name - The CF standard name of the desired coordinate. If None, does not - check for standard name. - * long_name - An unconstrained description of the coordinate. If None, does not - check for long_name. - * var_name - The netCDF variable name of the desired coordinate. If None, does - not check for var_name. - * attributes - A dictionary of attributes desired on the coordinates. If None, - does not check for attributes. - * axis - The desired coordinate axis, see - :func:`~iris.util.guess_coord_axis`. If None, does not check for - axis. Accepts the values 'X', 'Y', 'Z' and 'T' (case-insensitive). + * item: + Either, + + * a :attr:`~iris.common.mixin.CFVariableMixin.standard_name`, + :attr:`~iris.common.mixin.CFVariableMixin.long_name`, or + :attr:`~iris.common.mixin.CFVariableMixin.var_name` which is compared + against the :meth:`~iris.common.mixin.CFVariableMixin.name`. + + * a coordinate or metadata instance equal to that of + the desired objects e.g., :class:`~iris.coords.DimCoord` + or :class:`CoordMetadata`. + + * standard_name: + The CF standard name of the desired object. If ``None``, does not + check for ``standard_name``. + + * long_name: + An unconstrained description of the object. If ``None``, does not + check for ``long_name``. + + * var_name: + The NetCDF variable name of the desired object. If ``None``, does + not check for ``var_name``. + + * attributes: + A dictionary of attributes desired on the object. If ``None``, + does not check for ``attributes``. + + * axis: + The desired object's axis, see :func:`~iris.util.guess_coord_axis`. + If ``None``, does not check for ``axis``. Accepts the values ``X``, + ``Y``, ``Z`` and ``T`` (case-insensitive). Returns: A list of the objects supplied in the ``instances`` argument, limited diff --git a/lib/iris/coords.py b/lib/iris/coords.py index ca5dbd4593..5d192ac695 100644 --- a/lib/iris/coords.py +++ b/lib/iris/coords.py @@ -590,8 +590,7 @@ def xml_element(self, doc): Returns: The :class:`xml.dom.minidom.Element` that will describe this - :class:`_DimensionalMetadata`, and the dictionary of attributes - that require to be added to this element. + :class:`_DimensionalMetadata`. """ # Create the XML element as the camelCaseEquivalent of the @@ -2271,8 +2270,7 @@ def xml_element(self, doc): Returns: The :class:`xml.dom.minidom.Element` that will describe this - :class:`DimCoord`, and the dictionary of attributes that require - to be added to this element. + :class:`DimCoord`. """ # Create the XML element as the camelCaseEquivalent of the diff --git a/lib/iris/cube.py b/lib/iris/cube.py index e8b6d4a692..54f74bab1b 100644 --- a/lib/iris/cube.py +++ b/lib/iris/cube.py @@ -848,7 +848,7 @@ def __init__( * long_name An unconstrained description of the cube. * var_name - The netCDF variable name for the cube. + The NetCDF variable name for the cube. * units The unit of the cube, e.g. ``"m s-1"`` or ``"kelvin"``. * attributes @@ -905,7 +905,7 @@ def __init__( #: The "long name" for the Cube's phenomenon. self.long_name = long_name - #: The netCDF variable name for the Cube. + #: The NetCDF variable name for the Cube. self.var_name = var_name self.cell_methods = cell_methods @@ -1514,7 +1514,7 @@ def aux_factory( An unconstrained description of the coordinate factory. If None, does not check for long_name. * var_name - The netCDF variable name of the desired coordinate factory. + The NetCDF variable name of the desired coordinate factory. If None, does not check for var_name. .. note:: @@ -1584,57 +1584,71 @@ def coords( dim_coords=None, ): """ - Return a list of coordinates in this cube fitting the given criteria. + Return a list of coordinates from the :class:`Cube` that match the + provided criteria. + + .. seealso:: + + :meth:`Cube.coord` for matching exactly one coordinate. Kwargs: - * name_or_coord - Either + * name_or_coord: + Either, - (a) a :attr:`standard_name`, :attr:`long_name`, or - :attr:`var_name`. Defaults to value of `default` - (which itself defaults to `unknown`) as defined in - :class:`iris.common.CFVariableMixin`. + * a :attr:`~iris.common.mixin.CFVariableMixin.standard_name`, + :attr:`~iris.common.mixin.CFVariableMixin.long_name`, or + :attr:`~iris.common.mixin.CFVariableMixin.var_name` which is + compared against the :meth:`~iris.common.mixin.CFVariableMixin.name`. - (b) a coordinate instance with metadata equal to that of - the desired coordinates. Accepts either a - :class:`iris.coords.DimCoord`, :class:`iris.coords.AuxCoord`, - :class:`iris.aux_factory.AuxCoordFactory`, - :class:`iris.common.CoordMetadata` or - :class:`iris.common.DimCoordMetadata`. - * standard_name - The CF standard name of the desired coordinate. If None, does not - check for standard name. - * long_name - An unconstrained description of the coordinate. If None, does not - check for long_name. - * var_name - The netCDF variable name of the desired coordinate. If None, does - not check for var_name. - * attributes - A dictionary of attributes desired on the coordinates. If None, - does not check for attributes. - * axis - The desired coordinate axis, see - :func:`iris.util.guess_coord_axis`. If None, does not check for - axis. Accepts the values 'X', 'Y', 'Z' and 'T' (case-insensitive). - * contains_dimension - The desired coordinate contains the data dimension. If None, does + * a coordinate or metadata instance equal to that of the desired + coordinate e.g., :class:`~iris.coords.DimCoord` or + :class:`~iris.common.metadata.CoordMetadata`. + + * standard_name: + The CF standard name of the desired coordinate. If ``None``, does not + check for ``standard name``. + + * long_name: + An unconstrained description of the coordinate. If ``None``, does not + check for ``long_name``. + + * var_name: + The NetCDF variable name of the desired coordinate. If ``None``, does + not check for ``var_name``. + + * attributes: + A dictionary of attributes desired on the coordinates. If ``None``, + does not check for ``attributes``. + + * axis: + The desired coordinate axis, see :func:`iris.util.guess_coord_axis`. + If ``None``, does not check for ``axis``. Accepts the values ``X``, + ``Y``, ``Z`` and ``T`` (case-insensitive). + + * contains_dimension: + The desired coordinate contains the data dimension. If ``None``, does not check for the dimension. - * dimensions + + * dimensions: The exact data dimensions of the desired coordinate. Coordinates - with no data dimension can be found with an empty tuple or list - (i.e. ``()`` or ``[]``). If None, does not check for dimensions. - * coord_system - Whether the desired coordinates have coordinate systems equal to - the given coordinate system. If None, no check is done. - * dim_coords - Set to True to only return coordinates that are the cube's - dimension coordinates. Set to False to only return coordinates - that are the cube's auxiliary and derived coordinates. If None, + with no data dimension can be found with an empty ``tuple`` or + ``list`` i.e., ``()`` or ``[]``. If ``None``, does not check for + dimensions. + + * coord_system: + Whether the desired coordinates have a coordinate system equal to + the given coordinate system. If ``None``, no check is done. + + * dim_coords: + Set to ``True`` to only return coordinates that are the cube's + dimension coordinates. Set to ``False`` to only return coordinates + that are the cube's auxiliary and derived coordinates. If ``None``, returns all coordinates. - See also :meth:`Cube.coord()`. + Returns: + A list containing zero or more coordinates matching the provided + criteria. """ coords_and_factories = [] @@ -1715,18 +1729,75 @@ def coord( dim_coords=None, ): """ - Return a single coord given the same arguments as :meth:`Cube.coords`. + Return a single coordinate from the :class:`Cube` that matches the + provided criteria. .. note:: - If the arguments given do not result in precisely 1 coordinate - being matched, an :class:`iris.exceptions.CoordinateNotFoundError` - is raised. + If the arguments given do not result in **precisely one** coordinate, + then a :class:`~iris.exceptions.CoordinateNotFoundError` is raised. .. seealso:: - :meth:`Cube.coords()` for full keyword - documentation. + :meth:`Cube.coords` for matching zero or more coordinates. + + Kwargs: + + * name_or_coord: + Either, + + * a :attr:`~iris.common.mixin.CFVariableMixin.standard_name`, + :attr:`~iris.common.mixin.CFVariableMixin.long_name`, or + :attr:`~iris.common.mixin.CFVariableMixin.var_name` which is + compared against the :meth:`~iris.common.mixin.CFVariableMixin.name`. + + * a coordinate or metadata instance equal to that of the desired + coordinate e.g., :class:`~iris.coords.DimCoord` or + :class:`~iris.common.metadata.CoordMetadata`. + + * standard_name: + The CF standard name of the desired coordinate. If ``None``, does not + check for ``standard name``. + + * long_name: + An unconstrained description of the coordinate. If ``None``, does not + check for ``long_name``. + + * var_name: + The NetCDF variable name of the desired coordinate. If ``None``, does + not check for ``var_name``. + + * attributes: + A dictionary of attributes desired on the coordinates. If ``None``, + does not check for ``attributes``. + + * axis: + The desired coordinate axis, see :func:`iris.util.guess_coord_axis`. + If ``None``, does not check for ``axis``. Accepts the values ``X``, + ``Y``, ``Z`` and ``T`` (case-insensitive). + + * contains_dimension: + The desired coordinate contains the data dimension. If ``None``, does + not check for the dimension. + + * dimensions: + The exact data dimensions of the desired coordinate. Coordinates + with no data dimension can be found with an empty ``tuple`` or + ``list`` i.e., ``()`` or ``[]``. If ``None``, does not check for + dimensions. + + * coord_system: + Whether the desired coordinates have a coordinate system equal to + the given coordinate system. If ``None``, no check is done. + + * dim_coords: + Set to ``True`` to only return coordinates that are the cube's + dimension coordinates. Set to ``False`` to only return coordinates + that are the cube's auxiliary and derived coordinates. If ``None``, + returns all coordinates. + + Returns: + The coordinate that matches the provided criteria. """ coords = self.coords( @@ -1743,23 +1814,22 @@ def coord( ) if len(coords) > 1: - msg = ( - "Expected to find exactly 1 coordinate, but found %s. " - "They were: %s." - % (len(coords), ", ".join(coord.name() for coord in coords)) + emsg = ( + f"Expected to find exactly 1 coordinate, but found {len(coords)}. " + f"They were: {', '.join(coord.name() for coord in coords)}." ) - raise iris.exceptions.CoordinateNotFoundError(msg) + raise iris.exceptions.CoordinateNotFoundError(emsg) elif len(coords) == 0: _name = name_or_coord if name_or_coord is not None: if not isinstance(name_or_coord, str): _name = name_or_coord.name() bad_name = _name or standard_name or long_name or "" - msg = ( - "Expected to find exactly 1 %s coordinate, but found " - "none." % bad_name + emsg = ( + f"Expected to find exactly 1 {bad_name!r} coordinate, " + "but found none." ) - raise iris.exceptions.CoordinateNotFoundError(msg) + raise iris.exceptions.CoordinateNotFoundError(emsg) return coords[0] @@ -2060,7 +2130,7 @@ def data(self): .. note:: - Cubes obtained from netCDF, PP, and FieldsFile files will only + Cubes obtained from NetCDF, PP, and FieldsFile files will only populate this attribute on its first use. To obtain the shape of the data without causing it to be loaded, diff --git a/lib/iris/experimental/ugrid.py b/lib/iris/experimental/ugrid.py index 86c80bb2c1..5bb92b00f2 100644 --- a/lib/iris/experimental/ugrid.py +++ b/lib/iris/experimental/ugrid.py @@ -39,10 +39,13 @@ __all__ = [ "Connectivity", "ConnectivityMetadata", + "Mesh", "Mesh1DConnectivities", "Mesh1DCoords", + "Mesh1DNames", "Mesh2DConnectivities", "Mesh2DCoords", + "Mesh2DNames", "MeshEdgeCoords", "MeshFaceCoords", "MeshNodeCoords", @@ -60,27 +63,44 @@ # Configure the logger. logger = get_logger(__name__, fmt="[%(cls)s.%(funcName)s]") - +# # Mesh dimension names namedtuples. +# + +#: Namedtuple for 1D mesh topology NetCDF variable dimension names. Mesh1DNames = namedtuple("Mesh1DNames", ["node_dimension", "edge_dimension"]) +#: Namedtuple for 2D mesh topology NetCDF variable dimension names. Mesh2DNames = namedtuple( "Mesh2DNames", ["node_dimension", "edge_dimension", "face_dimension"] ) +# # Mesh coordinate manager namedtuples. +# + +#: Namedtuple for 1D mesh :class:`~iris.coords.AuxCoord` coordinates. Mesh1DCoords = namedtuple( "Mesh1DCoords", ["node_x", "node_y", "edge_x", "edge_y"] ) +#: Namedtuple for 2D mesh :class:`~iris.coords.AuxCoord` coordinates. Mesh2DCoords = namedtuple( "Mesh2DCoords", ["node_x", "node_y", "edge_x", "edge_y", "face_x", "face_y"], ) +#: Namedtuple for ``node`` :class:`~iris.coords.AuxCoord` coordinates. MeshNodeCoords = namedtuple("MeshNodeCoords", ["node_x", "node_y"]) +#: Namedtuple for ``edge`` :class:`~iris.coords.AuxCoord` coordinates. MeshEdgeCoords = namedtuple("MeshEdgeCoords", ["edge_x", "edge_y"]) +#: Namedtuple for ``face`` :class:`~iris.coords.AuxCoord` coordinates. MeshFaceCoords = namedtuple("MeshFaceCoords", ["face_x", "face_y"]) +# # Mesh connectivity manager namedtuples. +# + +#: Namedtuple for 1D mesh :class:`Connectivity` instances. Mesh1DConnectivities = namedtuple("Mesh1DConnectivities", ["edge_node"]) +#: Namedtuple for 2D mesh :class:`Connectivity` instances. Mesh2DConnectivities = namedtuple( "Mesh2DConnectivities", [ @@ -157,7 +177,7 @@ def __init__( * long_name (str): Descriptive name of the connectivity. * var_name (str): - The netCDF variable name for the connectivity. + The NetCDF variable name for the connectivity. * units (cf_units.Unit): The :class:`~cf_units.Unit` of the connectivity's values. Can be a string, which will be converted to a Unit object. @@ -850,41 +870,24 @@ def equal(self, other, lenient=None): class Mesh(CFVariableMixin): """ + A container representing the UGRID ``cf_role`` ``mesh_topology``, supporting + 1D network, 2D triangular, and 2D flexible mesh topologies. - .. todo:: - - .. questions:: + .. note:: - - decide on the verbose/succinct version of __str__ vs __repr__ + The 3D layered and fully 3D unstructured mesh topologies are not supported + at this time. - .. notes:: + .. seealso:: - - the mesh is location agnostic - - - no need to support volume at mesh level, yet - - - topology_dimension - - use for fast equality between Mesh instances - - checking connectivity dimensionality, specifically the highest dimensonality of the - "geometric element" being added i.e., reference the src_location/tgt_location - - used to honour and enforce the minimum UGRID connectivity contract - - - support pickling - - - copy is off the table!! - - - MeshCoord.guess_points() - - MeshCoord.to_AuxCoord() - - - don't provide public methods to return the coordinate and connectivity - managers - - - validate both managers contents e.g., shape? more...? + The UGRID Conventions, https://ugrid-conventions.github.io/ugrid-conventions/ """ # TBD: for volume and/or z-axis support include axis "z" and/or dimension "3" + #: The supported mesh axes. AXES = ("x", "y") + #: Valid range of values for ``topology_dimension``. TOPOLOGY_DIMENSIONS = (1, 2) def __init__( @@ -906,12 +909,10 @@ def __init__( """ .. note:: - :attr:`node_dimension`, :attr:`edge_dimension` and - :attr:`face_dimension` are stored to help round-tripping of UGRID - files. As such their presence in :class:`Mesh` is not a direct - mirror of that written in the UGRID specification, where - :attr:`node_dimension` is not mentioned, while - :attr:`edge_dimension` is only present for + The purpose of the :attr:`node_dimension`, :attr:`edge_dimension` and + :attr:`face_dimension` properties are to preserve the original NetCDF + variable dimension names. Note that, only :attr:`edge_dimension` and + :attr:`face_dimension` are UGRID attributes, and are only present for :attr:`topology_dimension` ``>=2``. """ @@ -926,7 +927,6 @@ def __init__( raise ValueError(emsg) self._metadata_manager.topology_dimension = topology_dimension - # TBD: these are strings, if None is provided then assign the default string. self.node_dimension = node_dimension self.edge_dimension = edge_dimension self.face_dimension = face_dimension @@ -1116,12 +1116,47 @@ def _set_dimension_names(self, node, edge, face, reset=False): return result + @property + def all_connectivities(self): + """ + All the :class:`Connectivity` instances of the :class:`Mesh`. + + """ + return self._connectivity_manager.all_members + @property def all_coords(self): + """ + All the :class:`~iris.coords.AuxCoord` coordinates of the :class:`Mesh`. + + """ return self._coord_manager.all_members + @property + def boundary_node_connectivity(self): + """ + The *optional* UGRID ``boundary_node_connectivity`` :class:`Connectivity` + of the :class:`Mesh`. + + """ + return self._connectivity_manager.boundary_node + + @property + def edge_coords(self): + """ + The *optional* UGRID ``edge`` :class:`~iris.coords.AuxCoord` coordinates + of the :class:`Mesh`. + + """ + return self._coord_manager.edge_coords + @property def edge_dimension(self): + """ + The *optionally required* UGRID NetCDF variable name for the ``edge`` + dimension. + + """ return self._metadata_manager.edge_dimension @edge_dimension.setter @@ -1133,11 +1168,41 @@ def edge_dimension(self, name): self._metadata_manager.edge_dimension = edge_dimension @property - def edge_coords(self): - return self._coord_manager.edge_coords + def edge_face_connectivity(self): + """ + The *optional* UGRID ``edge_face_connectivity`` :class:`Connectivity` + of the :class:`Mesh`. + + """ + return self._connectivity_manager.edge_face + + @property + def edge_node_connectivity(self): + """ + The UGRID ``edge_node_connectivity`` :class:`Connectivity` of the + :class:`Mesh`, which is **required** for :attr:`Mesh.topology_dimension` + of ``1``, and *optionally required* for + :attr:`Mesh.topology_dimension` ``>=2``. + + """ + return self._connectivity_manager.edge_node + + @property + def face_coords(self): + """ + The *optional* UGRID ``face`` :class:`~iris.coords.AuxCoord` coordinates + of the :class:`Mesh`. + + """ + return self._coord_manager.face_coords @property def face_dimension(self): + """ + The *optionally required* UGRID NetCDF variable name for the ``face`` + dimension. + + """ return self._metadata_manager.face_dimension @face_dimension.setter @@ -1158,11 +1223,47 @@ def face_dimension(self, name): self._metadata_manager.face_dimension = face_dimension @property - def face_coords(self): - return self._coord_manager.face_coords + def face_edge_connectivity(self): + """ + The *optional* UGRID ``face_edge_connectivity`` :class:`Connectivity` + of the :class:`Mesh`. + + """ + # optional + return self._connectivity_manager.face_edge + + @property + def face_face_connectivity(self): + """ + The *optional* UGRID ``face_face_connectivity`` :class:`Connectivity` + of the :class:`Mesh`. + + """ + return self._connectivity_manager.face_face + + @property + def face_node_connectivity(self): + """ + The UGRID ``face_node_connectivity`` :class:`Connectivity` of the + :class:`Mesh`, which is **required** for :attr:`Mesh.topology_dimension` + of ``2``, and *optionally required* for :attr:`Mesh.topology_dimension` + of ``3``. + + """ + return self._connectivity_manager.face_node + + @property + def node_coords(self): + """ + The **required** UGRID ``node`` :class:`~iris.coords.AuxCoord` coordinates + of the :class:`Mesh`. + + """ + return self._coord_manager.node_coords @property def node_dimension(self): + """The NetCDF variable name for the ``node`` dimension.""" return self._metadata_manager.node_dimension @node_dimension.setter @@ -1173,43 +1274,18 @@ def node_dimension(self, name): node_dimension = name self._metadata_manager.node_dimension = node_dimension - @property - def node_coords(self): - return self._coord_manager.node_coords - - @property - def all_connectivities(self): - return self._connectivity_manager.all_members - - @property - def face_node_connectivity(self): - # required - return self._connectivity_manager.face_node - - @property - def edge_node_connectivity(self): - # optionally required - return self._connectivity_manager.edge_node + def add_connectivities(self, *connectivities): + """ + Add one or more :class:`Connectivity` instances to the :class:`Mesh`. - @property - def face_edge_connectivity(self): - # optional - return self._connectivity_manager.face_edge + Args: - @property - def face_face_connectivity(self): - # optional - return self._connectivity_manager.face_face + * connectivities (iterable of object): + A collection of one or more :class:`Connectivity` instances to + add to the :class:`Mesh`. - @property - def edge_face_connectivity(self): - # optional - return self._connectivity_manager.edge_face - - @property - def boundary_node_connectivity(self): - # optional - return self._connectivity_manager.boundary_node + """ + self._connectivity_manager.add(*connectivities) def add_coords( self, @@ -1220,6 +1296,30 @@ def add_coords( face_x=None, face_y=None, ): + """ + Add one or more :class:`~iris.coords.AuxCoord` coordinates to the :class:`Mesh`. + + Kwargs: + + * node_x (object): + The ``x-axis`` like ``node`` :class:`~iris.coords.AuxCoord`. + + * node_y (object): + The ``y-axis`` like ``node`` :class:`~iris.coords.AuxCoord`. + + * edge_x (object): + The ``x-axis`` like ``edge`` :class:`~iris.coords.AuxCoord`. + + * edge_y (object): + The ``y-axis`` like ``edge`` :class:`~iris.coords.AuxCoord`. + + * face_x (object): + The ``x-axis`` like ``face`` :class:`~iris.coords.AuxCoord`. + + * face_y (object): + The ``y-axis`` like ``face`` :class:`~iris.coords.AuxCoord`. + + """ # Filter out absent arguments - only expecting face coords sometimes, # same will be true of volumes in future. kwargs = { @@ -1234,9 +1334,6 @@ def add_coords( self._coord_manager.add(**kwargs) - def add_connectivities(self, *connectivities): - self._connectivity_manager.add(*connectivities) - def connectivities( self, item=None, @@ -1249,6 +1346,67 @@ def connectivities( contains_edge=None, contains_face=None, ): + """ + Return all :class:`Connectivity` instances from the :class:`Mesh` that + match the provided criteria. + + Criteria can be either specific properties or other objects with + metadata to be matched. + + .. seealso:: + + :meth:`Mesh.connectivity` for matching exactly one connectivity. + + Kwargs: + + * item (str or object): + Either, + + * a :attr:`~iris.common.mixin.CFVariableMixin.standard_name`, + :attr:`~iris.common.mixin.CFVariableMixin.long_name`, or + :attr:`~iris.common.mixin.CFVariableMixin.var_name` which is + compared against the :meth:`~iris.common.mixin.CFVariableMixin.name`. + + * a connectivity or metadata instance equal to that of + the desired objects e.g., :class:`Connectivity` or + :class:`ConnectivityMetadata`. + + * standard_name (str): + The CF standard name of the desired :class:`Connectivity`. If + ``None``, does not check for ``standard_name``. + + * long_name (str): + An unconstrained description of the :class:`Connectivity`. If + ``None``, does not check for ``long_name``. + + * var_name (str): + The NetCDF variable name of the desired :class:`Connectivity`. If + ``None``, does not check for ``var_name``. + + * attributes (dict): + A dictionary of attributes desired on the :class:`Connectivity`. If + ``None``, does not check for ``attributes``. + + * cf_role (str): + The UGRID ``cf_role`` of the desired :class:`Connectivity`. + + * contains_node (bool): + Contains the ``node`` location as part of the + :attr:`ConnectivityMetadata.cf_role` in the list of objects to be matched. + + * contains_edge (bool): + Contains the ``edge`` location as part of the + :attr:`ConnectivityMetadata.cf_role` in the list of objects to be matched. + + * contains_face (bool): + Contains the ``face`` location as part of the + :attr:`ConnectivityMetadata.cf_role` in the list of objects to be matched. + + Returns: + A list of :class:`Connectivity` instances from the :class:`Mesh` + that matched the given criteria. + + """ return self._connectivity_manager.filters( item=item, standard_name=standard_name, @@ -1273,6 +1431,74 @@ def connectivity( contains_edge=None, contains_face=None, ): + """ + Return a single :class:`Connectivity` from the :class:`Mesh` that + matches the provided criteria. + + Criteria can be either specific properties or other objects with + metadata to be matched. + + .. note:: + + If the given criteria do not return **precisely one** + :class:`Connectivity`, then a + :class:`~iris.exceptions.ConnectivityNotFoundError` is raised. + + .. seealso:: + + :meth:`Mesh.connectivities` for matching zero or more connectivities. + + Kwargs: + + * item (str or object): + Either, + + * a :attr:`~iris.common.mixin.CFVariableMixin.standard_name`, + :attr:`~iris.common.mixin.CFVariableMixin.long_name`, or + :attr:`~iris.common.mixin.CFVariableMixin.var_name` which is + compared against the :meth:`~iris.common.mixin.CFVariableMixin.name`. + + * a connectivity or metadata instance equal to that of + the desired object e.g., :class:`Connectivity` or + :class:`ConnectivityMetadata`. + + * standard_name (str): + The CF standard name of the desired :class:`Connectivity`. If + ``None``, does not check for ``standard_name``. + + * long_name (str): + An unconstrained description of the :class:`Connectivity`. If + ``None``, does not check for ``long_name``. + + * var_name (str): + The NetCDF variable name of the desired :class:`Connectivity`. If + ``None``, does not check for ``var_name``. + + * attributes (dict): + A dictionary of attributes desired on the :class:`Connectivity`. If + ``None``, does not check for ``attributes``. + + * cf_role (str): + The UGRID ``cf_role`` of the desired :class:`Connectivity`. + + * contains_node (bool): + Contains the ``node`` location as part of the + :attr:`ConnectivityMetadata.cf_role` in the list of objects to be matched. + + * contains_edge (bool): + Contains the ``edge`` location as part of the + :attr:`ConnectivityMetadata.cf_role` in the list of objects to be matched. + + * contains_face (bool): + Contains the ``face`` location as part of the + :attr:`ConnectivityMetadata.cf_role` in the list of objects to be matched. + + Returns: + The :class:`Connectivity` from the :class:`Mesh` that matched the + given criteria. + + """ + return self._connectivity_manager.filter( item=item, standard_name=standard_name, @@ -1297,6 +1523,71 @@ def coord( include_edges=None, include_faces=None, ): + """ + Return a single :class:`~iris.coords.AuxCoord` coordinate from the + :class:`Mesh` that matches the provided criteria. + + Criteria can be either specific properties or other objects with + metadata to be matched. + + .. note:: + + If the given criteria do not return **precisely one** coordinate, + then a :class:`~iris.exceptions.CoordinateNotFoundError` is raised. + + .. seealso:: + + :meth:`Mesh.coords` for matching zero or more coordinates. + + Kwargs: + + * item (str or object): + Either, + + * a :attr:`~iris.common.mixin.CFVariableMixin.standard_name`, + :attr:`~iris.common.mixin.CFVariableMixin.long_name`, or + :attr:`~iris.common.mixin.CFVariableMixin.var_name` which is + compared against the :meth:`~iris.common.mixin.CFVariableMixin.name`. + + * a coordinate or metadata instance equal to that of + the desired coordinate e.g., :class:`~iris.coords.AuxCoord` or + :class:`~iris.common.metadata.CoordMetadata`. + + * standard_name (str): + The CF standard name of the desired coordinate. If ``None``, does not + check for ``standard_name``. + + * long_name (str): + An unconstrained description of the coordinate. If ``None``, does not + check for ``long_name``. + + * var_name (str): + The NetCDF variable name of the desired coordinate. If ``None``, does + not check for ``var_name``. + + * attributes (dict): + A dictionary of attributes desired on the coordinates. If ``None``, + does not check for ``attributes``. + + * axis (str): + The desired coordinate axis, see :func:`~iris.util.guess_coord_axis`. + If ``None``, does not check for ``axis``. Accepts the values ``X``, + ``Y``, ``Z`` and ``T`` (case-insensitive). + + * include_node (bool): + Include all ``node`` coordinates in the list of objects to be matched. + + * include_edge (bool): + Include all ``edge`` coordinates in the list of objects to be matched. + + * include_face (bool): + Include all ``face`` coordinates in the list of objects to be matched. + + Returns: + The :class:`~iris.coords.AuxCoord` coordinate from the :class:`Mesh` + that matched the given criteria. + + """ return self._coord_manager.filter( item=item, standard_name=standard_name, @@ -1321,6 +1612,66 @@ def coords( include_edges=None, include_faces=None, ): + """ + Return all :class:`~iris.coords.AuxCoord` coordinates from the :class:`Mesh` that + match the provided criteria. + + Criteria can be either specific properties or other objects with + metadata to be matched. + + .. seealso:: + + :meth:`Mesh.coord` for matching exactly one coordinate. + + Kwargs: + + * item (str or object): + Either, + + * a :attr:`~iris.common.mixin.CFVariableMixin.standard_name`, + :attr:`~iris.common.mixin.CFVariableMixin.long_name`, or + :attr:`~iris.common.mixin.CFVariableMixin.var_name` which is + compared against the :meth:`~iris.common.mixin.CFVariableMixin.name`. + + * a coordinate or metadata instance equal to that of + the desired coordinates e.g., :class:`~iris.coords.AuxCoord` or + :class:`~iris.common.metadata.CoordMetadata`. + + * standard_name (str): + The CF standard name of the desired coordinate. If ``None``, does not + check for ``standard_name``. + + * long_name (str): + An unconstrained description of the coordinate. If ``None``, does not + check for ``long_name``. + + * var_name (str): + The NetCDF variable name of the desired coordinate. If ``None``, does + not check for ``var_name``. + + * attributes (dict): + A dictionary of attributes desired on the coordinates. If ``None``, + does not check for ``attributes``. + + * axis (str): + The desired coordinate axis, see :func:`~iris.util.guess_coord_axis`. + If ``None``, does not check for ``axis``. Accepts the values ``X``, + ``Y``, ``Z`` and ``T`` (case-insensitive). + + * include_node (bool): + Include all ``node`` coordinates in the list of objects to be matched. + + * include_edge (bool): + Include all ``edge`` coordinates in the list of objects to be matched. + + * include_face (bool): + Include all ``face`` coordinates in the list of objects to be matched. + + Returns: + A list of :class:`~iris.coords.AuxCoord` coordinates from the + :class:`Mesh` that matched the given criteria. + + """ return self._coord_manager.filters( item=item, standard_name=standard_name, @@ -1345,6 +1696,66 @@ def remove_connectivities( contains_edge=None, contains_face=None, ): + """ + Remove one or more :class:`Connectivity` from the :class:`Mesh` that + match the provided criteria. + + Criteria can be either specific properties or other objects with + metadata to be matched. + + Kwargs: + + * item (str or object): + Either, + + * a :attr:`~iris.common.mixin.CFVariableMixin.standard_name`, + :attr:`~iris.common.mixin.CFVariableMixin.long_name`, or + :attr:`~iris.common.mixin.CFVariableMixin.var_name` which is + compared against the :meth:`~iris.common.mixin.CFVariableMixin.name`. + + * a connectivity or metadata instance equal to that of + the desired objects e.g., :class:`Connectivity` or + :class:`ConnectivityMetadata`. + + * standard_name (str): + The CF standard name of the desired :class:`Connectivity`. If + ``None``, does not check for ``standard_name``. + + * long_name (str): + An unconstrained description of the :class:`Connectivity. If + ``None``, does not check for ``long_name``. + + * var_name (str): + The NetCDF variable name of the desired :class:`Connectivity`. If + ``None``, does not check for ``var_name``. + + * attributes (dict): + A dictionary of attributes desired on the :class:`Connectivity`. If + ``None``, does not check for ``attributes``. + + * cf_role (str): + The UGRID ``cf_role`` of the desired :class:`Connectivity`. + + * contains_node (bool): + Contains the ``node`` location as part of the + :attr:`ConnectivityMetadata.cf_role` in the list of objects to be matched + for potential removal. + + * contains_edge (bool): + Contains the ``edge`` location as part of the + :attr:`ConnectivityMetadata.cf_role` in the list of objects to be matched + for potential removal. + + * contains_face (bool): + Contains the ``face`` location as part of the + :attr:`ConnectivityMetadata.cf_role` in the list of objects to be matched + for potential removal. + + Returns: + A list of :class:`Connectivity` instances removed from the :class:`Mesh` + that matched the given criteria. + + """ return self._connectivity_manager.remove( item=item, standard_name=standard_name, @@ -1369,6 +1780,65 @@ def remove_coords( include_edges=None, include_faces=None, ): + """ + Remove one or more :class:`~iris.coords.AuxCoord` from the :class:`Mesh` + that match the provided criteria. + + Criteria can be either specific properties or other objects with + metadata to be matched. + + Kwargs: + + * item (str or object): + Either, + + * a :attr:`~iris.common.mixin.CFVariableMixin.standard_name`, + :attr:`~iris.common.mixin.CFVariableMixin.long_name`, or + :attr:`~iris.common.mixin.CFVariableMixin.var_name` which is + compared against the :meth:`~iris.common.mixin.CFVariableMixin.name`. + + * a coordinate or metadata instance equal to that of + the desired coordinates e.g., :class:`~iris.coords.AuxCoord` or + :class:`~iris.common.metadata.CoordMetadata`. + + * standard_name (str): + The CF standard name of the desired coordinate. If ``None``, does not + check for ``standard_name``. + + * long_name (str): + An unconstrained description of the coordinate. If ``None``, does not + check for ``long_name``. + + * var_name (str): + The NetCDF variable name of the desired coordinate. If ``None``, does + not check for ``var_name``. + + * attributes (dict): + A dictionary of attributes desired on the coordinates. If ``None``, + does not check for ``attributes``. + + * axis (str): + The desired coordinate axis, see :func:`~iris.util.guess_coord_axis`. + If ``None``, does not check for ``axis``. Accepts the values ``X``, + ``Y``, ``Z`` and ``T`` (case-insensitive). + + * include_node (bool): + Include all ``node`` coordinates in the list of objects to be matched + for potential removal. + + * include_edge (bool): + Include all ``edge`` coordinates in the list of objects to be matched + for potential removal. + + * include_face (bool): + Include all ``face`` coordinates in the list of objects to be matched + for potential removal. + + Returns: + A list of :class:`~iris.coords.AuxCoord` coordinates removed from + the :class:`Mesh` that matched the given criteria. + + """ # Filter out absent arguments - only expecting face coords sometimes, # same will be true of volumes in future. kwargs = { @@ -1386,8 +1856,22 @@ def remove_coords( return self._coord_manager.remove(**kwargs) - def xml_element(self): - # TBD + def xml_element(self, doc): + """ + Create the :class:`xml.dom.minidom.Element` that describes this + :class:`Mesh`. + + Args: + + * doc (object): + The parent :class:`xml.dom.minidom.Document`. + + Returns: + The :class:`xml.dom.minidom.Element` that will describe this + :class:`Mesh`, and the dictionary of attributes that require + to be added to this element. + + """ pass # the MeshCoord will always have bounds, perhaps points. However the MeshCoord.guess_points() may @@ -1414,17 +1898,66 @@ def xml_element(self): # # use Connectivity.indices_by_src for fetching indices, passing in the lazy_indices() result as an argument. def dimension_names_reset(self, node=False, edge=False, face=False): + """ + Reset the name used for the NetCDF variable representing the ``node``, + ``edge`` and/or ``face`` dimension to ``None``. + + Kwargs: + + * node (bool): + Reset the name of the ``node`` dimension if ``True``. Default + is ``False``. + + * edge (bool): + Reset the name of the ``edge`` dimension if ``True``. Default + is ``False``. + + * face (bool): + Reset the name of the ``face`` dimension if ``True``. Default + is ``False``. + + """ return self._set_dimension_names(node, edge, face, reset=True) def dimension_names(self, node=None, edge=None, face=None): + """ + Assign the name to be used for the NetCDF variable representing + the ``node``, ``edge`` and ``face`` dimension. + + The default value of ``None`` will not be assigned to clear the + associated ``node``, ``edge`` or ``face``. Instead use + :meth:`Mesh.dimension_names_reset`. + + Kwargs: + + * node (str): + The name to be used for the NetCDF variable representing the + ``node`` dimension. + + * edge (str): + The name to be used for the NetCDF variable representing the + ``edge`` dimension. + + * face (str): + The name to be used for the NetCDF variable representing the + ``face`` dimension. + + """ return self._set_dimension_names(node, edge, face, reset=False) @property def cf_role(self): + """The UGRID ``cf_role`` attribute of the :class:`Mesh`.""" return "mesh_topology" @property def topology_dimension(self): + """ + The UGRID ``topology_dimension`` attribute represents the highest + dimensionality of all the geometric elements (node, edge, face) represented + within the :class:`Mesh`. + + """ return self._metadata_manager.topology_dimension diff --git a/lib/iris/tests/unit/util/test_reverse.py b/lib/iris/tests/unit/util/test_reverse.py index 5bc58e1ced..74422ee335 100644 --- a/lib/iris/tests/unit/util/test_reverse.py +++ b/lib/iris/tests/unit/util/test_reverse.py @@ -172,7 +172,9 @@ def test_cube_coord(self): cube1_reverse_spanning.coord("spanning").points, ) - msg = "Expected to find exactly 1 latitude coordinate, but found none." + msg = ( + "Expected to find exactly 1 'latitude' coordinate, but found none." + ) with self.assertRaisesRegex( iris.exceptions.CoordinateNotFoundError, msg ):