diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index 7c1fd33075..cec4a43bed 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -191,6 +191,10 @@ This document explains the changes made to Iris for this release :class:`benchmarks.HorizontalChunkedRegridding` shows a time decrease from >10s to 625ms. (:issue:`4280`, :pull:`4400`) +#. `@bjlittle`_ included an optimisation to :class:`~iris.cube.Cube.coord_dims` + to avoid unnecessary processing whenever a coordinate instance that already + exists within the cube is provided. (:pull:`4549`) + 🔥 Deprecations =============== diff --git a/lib/iris/_merge.py b/lib/iris/_merge.py index 6758e9f55d..bc12080523 100644 --- a/lib/iris/_merge.py +++ b/lib/iris/_merge.py @@ -1809,7 +1809,8 @@ def key_func(coord): # Order the coordinates by hints, axis, and definition. for coord in sorted(coords, key=key_func): - if not cube.coord_dims(coord) and coord.shape == (1,): + dims = tuple(cube.coord_dims(coord)) + if not dims and coord.shape == (1,): # Extract the scalar coordinate data and metadata. scalar_defns.append(coord.metadata) # Because we know there's a single Cell in the @@ -1834,11 +1835,11 @@ def key_func(coord): # Extract the vector coordinate and metadata. if id(coord) in cube_aux_coord_ids: vector_aux_coords_and_dims.append( - _CoordAndDims(coord, tuple(cube.coord_dims(coord))) + _CoordAndDims(coord, dims) ) else: vector_dim_coords_and_dims.append( - _CoordAndDims(coord, tuple(cube.coord_dims(coord))) + _CoordAndDims(coord, dims) ) factory_defns = [] diff --git a/lib/iris/cube.py b/lib/iris/cube.py index 2cd29682dd..ab6aad3cce 100644 --- a/lib/iris/cube.py +++ b/lib/iris/cube.py @@ -1451,39 +1451,53 @@ def coord_dims(self, coord): The (name of the) coord to look for. """ - - coord = self.coord(coord) - - # Search for existing coordinate (object) on the cube, faster lookup - # than equality - makes no functional difference. - matches = [ - (dim,) - for coord_, dim in self._dim_coords_and_dims - if coord_ is coord - ] - if not matches: - matches = [ - dims - for coord_, dims in self._aux_coords_and_dims - if coord_ is coord - ] - - # Search derived aux coords - if not matches: + name_provided = False + if isinstance(coord, str): + # Forced to look-up the coordinate if we only have the name. + coord = self.coord(coord) + name_provided = True + + coord_id = id(coord) + + # Dimension of dimension coordinate by object id + dims_by_id = {id(c): (d,) for c, d in self._dim_coords_and_dims} + # Check for id match - faster than equality check + match = dims_by_id.get(coord_id) + + if match is None: + # Dimension/s of auxiliary coordinate by object id + aux_dims_by_id = {id(c): d for c, d in self._aux_coords_and_dims} + # Check for id match - faster than equality + match = aux_dims_by_id.get(coord_id) + if match is None: + dims_by_id.update(aux_dims_by_id) + + if match is None and not name_provided: + # We may have an equivalent coordinate but not the actual + # cube coordinate instance - so forced to perform coordinate + # lookup to attempt to retrieve it + coord = self.coord(coord) + # Check for id match - faster than equality + match = dims_by_id.get(id(coord)) + + # Search derived aux coordinates + if match is None: target_metadata = coord.metadata - def match(factory): + def matcher(factory): return factory.metadata == target_metadata - factories = filter(match, self._aux_factories) + factories = filter(matcher, self._aux_factories) matches = [ factory.derived_dims(self.coord_dims) for factory in factories ] + if matches: + match = matches[0] - if not matches: + if match is None: raise iris.exceptions.CoordinateNotFoundError(coord.name()) - return matches[0] + return match def cell_measure_dims(self, cell_measure): """