You are viewing the latest unreleased documentation 3.9.0.dev35. You can switch to a stable version.

Source code for iris.experimental.ugrid.metadata

# Copyright Iris contributors
#
# This file is part of Iris and is released under the BSD license.
# See LICENSE in the root of the repository for full licensing details.

"""The common metadata API classes for :mod:`iris.experimental.ugrid.mesh`.

Eventual destination: :mod:`iris.common.metadata`.

"""

from functools import wraps

from ...common import BaseMetadata
from ...common.lenient import _lenient_service as lenient_service
from ...common.metadata import (
    SERVICES,
    SERVICES_COMBINE,
    SERVICES_DIFFERENCE,
    SERVICES_EQUAL,
)


[docs]class ConnectivityMetadata(BaseMetadata): """Metadata container for a :class:`~iris.experimental.ugrid.mesh.Connectivity`.""" # The "location_axis" member is stateful only, and does not participate in # lenient/strict equivalence. _members = ("cf_role", "start_index", "location_axis") __slots__ = () @wraps(BaseMetadata.__eq__, assigned=("__doc__",), updated=()) @lenient_service def __eq__(self, other): return super().__eq__(other) def _combine_lenient(self, other): """Perform lenient combination of metadata members for connectivities. Parameters ---------- other : ConnectivityMetadata The other connectivity metadata participating in the lenient combination. Returns ------- A list of combined metadata member values. """ # Perform "strict" combination for "cf_role", "start_index", "location_axis". def func(field): left = getattr(self, field) right = getattr(other, field) return left if left == right else None # Note that, we use "_members" not "_fields". values = [func(field) for field in ConnectivityMetadata._members] # Perform lenient combination of the other parent members. result = super()._combine_lenient(other) result.extend(values) return result def _compare_lenient(self, other): """Perform lenient equality of metadata members for connectivities. Parameters ---------- other : ConnectivityMetadata The other connectivity metadata participating in the lenient comparison. Returns ------- bool """ # Perform "strict" comparison for "cf_role", "start_index". # The "location_axis" member is not part of lenient equivalence. members = filter( lambda member: member != "location_axis", ConnectivityMetadata._members, ) result = all( [getattr(self, field) == getattr(other, field) for field in members] ) if result: # Perform lenient comparison of the other parent members. result = super()._compare_lenient(other) return result def _difference_lenient(self, other): """Perform lenient difference of metadata members for connectivities. Parameters ---------- other : ConnectivityMetadata The other connectivity metadata participating in the lenient difference. Returns ------- A list of difference metadata member values. """ # Perform "strict" difference for "cf_role", "start_index", "location_axis". def func(field): left = getattr(self, field) right = getattr(other, field) return None if left == right else (left, right) # Note that, we use "_members" not "_fields". values = [func(field) for field in ConnectivityMetadata._members] # Perform lenient difference of the other parent members. result = super()._difference_lenient(other) result.extend(values) return result
[docs] @wraps(BaseMetadata.combine, assigned=("__doc__",), updated=()) @lenient_service def combine(self, other, lenient=None): return super().combine(other, lenient=lenient)
[docs] @wraps(BaseMetadata.difference, assigned=("__doc__",), updated=()) @lenient_service def difference(self, other, lenient=None): return super().difference(other, lenient=lenient)
[docs] @wraps(BaseMetadata.equal, assigned=("__doc__",), updated=()) @lenient_service def equal(self, other, lenient=None): return super().equal(other, lenient=lenient)
[docs]class MeshMetadata(BaseMetadata): """Metadata container for a :class:`~iris.experimental.ugrid.mesh.Mesh`.""" # The node_dimension", "edge_dimension" and "face_dimension" members are # stateful only; they not participate in lenient/strict equivalence. _members = ( "topology_dimension", "node_dimension", "edge_dimension", "face_dimension", ) __slots__ = () @wraps(BaseMetadata.__eq__, assigned=("__doc__",), updated=()) @lenient_service def __eq__(self, other): return super().__eq__(other) def _combine_lenient(self, other): """Perform lenient combination of metadata members for meshes. Parameters ---------- other : MeshMetadata The other mesh metadata participating in the lenient combination. Returns ------- A list of combined metadata member values. """ # Perform "strict" combination for "topology_dimension", # "node_dimension", "edge_dimension" and "face_dimension". def func(field): left = getattr(self, field) right = getattr(other, field) return left if left == right else None # Note that, we use "_members" not "_fields". values = [func(field) for field in MeshMetadata._members] # Perform lenient combination of the other parent members. result = super()._combine_lenient(other) result.extend(values) return result def _compare_lenient(self, other): """Perform lenient equality of metadata members for meshes. Parameters ---------- other : MeshMetadata The other mesh metadata participating in the lenient comparison. Returns ------- bool """ # Perform "strict" comparison for "topology_dimension". # "node_dimension", "edge_dimension" and "face_dimension" are not part # of lenient equivalence at all. result = self.topology_dimension == other.topology_dimension if result: # Perform lenient comparison of the other parent members. result = super()._compare_lenient(other) return result def _difference_lenient(self, other): """Perform lenient difference of metadata members for meshes. Parameters ---------- other : MeshMetadata The other mesh metadata participating in the lenient difference. Returns ------- A list of difference metadata member values. """ # Perform "strict" difference for "topology_dimension", # "node_dimension", "edge_dimension" and "face_dimension". def func(field): left = getattr(self, field) right = getattr(other, field) return None if left == right else (left, right) # Note that, we use "_members" not "_fields". values = [func(field) for field in MeshMetadata._members] # Perform lenient difference of the other parent members. result = super()._difference_lenient(other) result.extend(values) return result
[docs] @wraps(BaseMetadata.combine, assigned=("__doc__",), updated=()) @lenient_service def combine(self, other, lenient=None): return super().combine(other, lenient=lenient)
[docs] @wraps(BaseMetadata.difference, assigned=("__doc__",), updated=()) @lenient_service def difference(self, other, lenient=None): return super().difference(other, lenient=lenient)
[docs] @wraps(BaseMetadata.equal, assigned=("__doc__",), updated=()) @lenient_service def equal(self, other, lenient=None): return super().equal(other, lenient=lenient)
[docs]class MeshCoordMetadata(BaseMetadata): """Metadata container for a :class:`~iris.coords.MeshCoord`.""" _members = ("location", "axis") # NOTE: in future, we may add 'mesh' as part of this metadata, # as the Mesh seems part of the 'identity' of a MeshCoord. # For now we omit it, particularly as we don't yet implement Mesh.__eq__. # # Thus, for now, the MeshCoord class will need to handle 'mesh' explicitly # in identity / comparison, but in future that may be simplified. __slots__ = () @wraps(BaseMetadata.__eq__, assigned=("__doc__",), updated=()) @lenient_service def __eq__(self, other): return super().__eq__(other) def _combine_lenient(self, other): """Perform lenient combination of metadata members for MeshCoord. Parameters ---------- other : MeshCoordMetadata The other metadata participating in the lenient combination. Returns ------- A list of combined metadata member values. """ # It is actually "strict" : return None except where members are equal. def func(field): left = getattr(self, field) right = getattr(other, field) return left if left == right else None # Note that, we use "_members" not "_fields". values = [func(field) for field in self._members] # Perform lenient combination of the other parent members. result = super()._combine_lenient(other) result.extend(values) return result def _compare_lenient(self, other): """Perform lenient equality of metadata members for MeshCoord. Parameters ---------- other : MeshCoordMetadata The other metadata participating in the lenient comparison. Returns ------- bool """ # Perform "strict" comparison for the MeshCoord specific members # 'location', 'axis' : for equality, they must all match. result = all( [getattr(self, field) == getattr(other, field) for field in self._members] ) if result: # Perform lenient comparison of the other parent members. result = super()._compare_lenient(other) return result def _difference_lenient(self, other): """Perform lenient difference of metadata members for MeshCoord. Parameters ---------- other : MeshCoordMetadata The other MeshCoord metadata participating in the lenient difference. Returns ------- A list of different metadata member values. """ # Perform "strict" difference for location / axis. def func(field): left = getattr(self, field) right = getattr(other, field) return None if left == right else (left, right) # Note that, we use "_members" not "_fields". values = [func(field) for field in self._members] # Perform lenient difference of the other parent members. result = super()._difference_lenient(other) result.extend(values) return result
[docs] @wraps(BaseMetadata.combine, assigned=("__doc__",), updated=()) @lenient_service def combine(self, other, lenient=None): return super().combine(other, lenient=lenient)
[docs] @wraps(BaseMetadata.difference, assigned=("__doc__",), updated=()) @lenient_service def difference(self, other, lenient=None): return super().difference(other, lenient=lenient)
[docs] @wraps(BaseMetadata.equal, assigned=("__doc__",), updated=()) @lenient_service def equal(self, other, lenient=None): return super().equal(other, lenient=lenient)
# Add our new optional metadata operations into the 'convenience collections' # of lenient metadata services. # TODO: when included in 'iris.common.metadata', install each one directly ? _op_names_and_service_collections = [ ("combine", SERVICES_COMBINE), ("difference", SERVICES_DIFFERENCE), ("__eq__", SERVICES_EQUAL), ("equal", SERVICES_EQUAL), ] _metadata_classes = [ConnectivityMetadata, MeshMetadata, MeshCoordMetadata] for _cls in _metadata_classes: for _name, _service_collection in _op_names_and_service_collections: _method = getattr(_cls, _name) _service_collection.append(_method) SERVICES.append(_method) del ( _op_names_and_service_collections, _metadata_classes, _cls, _name, _service_collection, _method, )