diff --git a/lib/iris/_concatenate.py b/lib/iris/_concatenate.py index 01a1bb689b..10a31eafc1 100644 --- a/lib/iris/_concatenate.py +++ b/lib/iris/_concatenate.py @@ -101,19 +101,23 @@ def __new__(mcs, coord, dims): """ defn = coord.metadata - points_dtype = coord.points.dtype - bounds_dtype = coord.bounds.dtype if coord.bounds is not None else None + points_dtype = coord.core_points().dtype + bounds_dtype = ( + coord.core_bounds().dtype + if coord.core_bounds() is not None + else None + ) kwargs = {} # Add scalar flag metadata. - kwargs["scalar"] = coord.points.size == 1 + kwargs["scalar"] = coord.core_points().size == 1 # Add circular flag metadata for dimensional coordinates. if hasattr(coord, "circular"): kwargs["circular"] = coord.circular if isinstance(coord, iris.coords.DimCoord): # Mix the monotonic ordering into the metadata. - if coord.points[0] == coord.points[-1]: + if coord.core_points()[0] == coord.core_points()[-1]: order = _CONSTANT - elif coord.points[-1] > coord.points[0]: + elif coord.core_points()[-1] > coord.core_points()[0]: order = _INCREASING else: order = _DECREASING @@ -700,18 +704,27 @@ def _cmp(coord, other): """ # A candidate axis must have non-identical coordinate points. - candidate_axis = not array_equal(coord.points, other.points) + candidate_axis = not array_equal( + coord.core_points(), other.core_points() + ) if candidate_axis: # Ensure both have equal availability of bounds. - result = (coord.bounds is None) == (other.bounds is None) + result = (coord.core_bounds() is None) == ( + other.core_bounds() is None + ) else: - if coord.bounds is not None and other.bounds is not None: + if ( + coord.core_bounds() is not None + and other.core_bounds() is not None + ): # Ensure equality of bounds. - result = array_equal(coord.bounds, other.bounds) + result = array_equal(coord.core_bounds(), other.core_bounds()) else: # Ensure both have equal availability of bounds. - result = coord.bounds is None and other.bounds is None + result = ( + coord.core_bounds() is None and other.core_bounds() is None + ) return result, candidate_axis @@ -762,21 +775,37 @@ def _calculate_extents(self): self.dim_extents = [] for coord, order in zip(self.dim_coords, self.dim_order): if order == _CONSTANT or order == _INCREASING: - points = _Extent(coord.points[0], coord.points[-1]) - if coord.bounds is not None: + points = _Extent( + coord.core_points()[0], coord.core_points()[-1] + ) + if coord.core_bounds() is not None: bounds = ( - _Extent(coord.bounds[0, 0], coord.bounds[-1, 0]), - _Extent(coord.bounds[0, 1], coord.bounds[-1, 1]), + _Extent( + coord.core_bounds()[0, 0], + coord.core_bounds()[-1, 0], + ), + _Extent( + coord.core_bounds()[0, 1], + coord.core_bounds()[-1, 1], + ), ) else: bounds = None else: # The order must be decreasing ... - points = _Extent(coord.points[-1], coord.points[0]) - if coord.bounds is not None: + points = _Extent( + coord.core_points()[-1], coord.core_points()[0] + ) + if coord.core_bounds() is not None: bounds = ( - _Extent(coord.bounds[-1, 0], coord.bounds[0, 0]), - _Extent(coord.bounds[-1, 1], coord.bounds[0, 1]), + _Extent( + coord.core_bounds()[-1, 0], + coord.core_bounds()[0, 0], + ), + _Extent( + coord.core_bounds()[-1, 1], + coord.core_bounds()[0, 1], + ), ) else: bounds = None @@ -1095,7 +1124,7 @@ def _build_aux_coordinates(self): # Concatenate the points together. dim = dims.index(self.axis) points = [ - skton.signature.aux_coords_and_dims[i].coord.points + skton.signature.aux_coords_and_dims[i].coord.core_points() for skton in skeletons ] points = np.concatenate(tuple(points), axis=dim) @@ -1104,7 +1133,9 @@ def _build_aux_coordinates(self): bnds = None if coord.has_bounds(): bnds = [ - skton.signature.aux_coords_and_dims[i].coord.bounds + skton.signature.aux_coords_and_dims[ + i + ].coord.core_bounds() for skton in skeletons ] bnds = np.concatenate(tuple(bnds), axis=dim) @@ -1307,7 +1338,7 @@ def _build_dim_coordinates(self): # Concatenate the points together for the nominated dimension. points = [ - skeleton.signature.dim_coords[dim_ind].points + skeleton.signature.dim_coords[dim_ind].core_points() for skeleton in skeletons ] points = np.concatenate(tuple(points)) @@ -1316,7 +1347,7 @@ def _build_dim_coordinates(self): bounds = None if self._cube_signature.dim_coords[dim_ind].has_bounds(): bounds = [ - skeleton.signature.dim_coords[dim_ind].bounds + skeleton.signature.dim_coords[dim_ind].core_bounds() for skeleton in skeletons ] bounds = np.concatenate(tuple(bounds)) diff --git a/lib/iris/tests/test_concatenate.py b/lib/iris/tests/test_concatenate.py index e4c22f49b0..7cb11189d6 100644 --- a/lib/iris/tests/test_concatenate.py +++ b/lib/iris/tests/test_concatenate.py @@ -12,6 +12,7 @@ # before importing anything else. import iris.tests as tests # isort:skip +import dask.array as da import numpy as np import numpy.ma as ma @@ -800,6 +801,30 @@ def test_concat_2y2d_derived_x_y_xy(self): self.assertEqual(result[0].shape, (6, 2)) self.assertEqual(result[0], com) + def test_concat_lazy_aux_coords(self): + cubes = [] + y = (0, 2) + cube = _make_cube((2, 4), y, 2, aux="xy") + cubes.append(cube) + cubes.append(_make_cube((0, 2), y, 1, aux="xy")) + for cube in cubes: + cube.data = cube.lazy_data() + cube.coord("xy-aux").points = cube.coord("xy-aux").lazy_points() + bounds = da.arange( + 4 * cube.coord("xy-aux").core_points().size + ).reshape(cube.shape + (4,)) + cube.coord("xy-aux").bounds = bounds + result = concatenate(cubes) + + self.assertTrue(cubes[0].coord("xy-aux").has_lazy_points()) + self.assertTrue(cubes[0].coord("xy-aux").has_lazy_bounds()) + + self.assertTrue(cubes[1].coord("xy-aux").has_lazy_points()) + self.assertTrue(cubes[1].coord("xy-aux").has_lazy_bounds()) + + self.assertTrue(result[0].coord("xy-aux").has_lazy_points()) + self.assertTrue(result[0].coord("xy-aux").has_lazy_bounds()) + class TestMulti2D(tests.IrisTest): def test_concat_4x2d_aux_xy(self): diff --git a/lib/iris/tests/unit/concatenate/test_concatenate.py b/lib/iris/tests/unit/concatenate/test_concatenate.py index a4243dfbbc..bb3770cf0f 100644 --- a/lib/iris/tests/unit/concatenate/test_concatenate.py +++ b/lib/iris/tests/unit/concatenate/test_concatenate.py @@ -355,7 +355,7 @@ def test_desc_bounds_all_singleton(self): class TestConcatenate__dask(tests.IrisTest): - def build_lazy_cube(self, points, bounds=None, nx=4): + def build_lazy_cube(self, points, bounds=None, nx=4, aux_coords=False): data = np.arange(len(points) * nx).reshape(len(points), nx) data = as_lazy_data(data) cube = iris.cube.Cube(data, standard_name="air_temperature", units="K") @@ -363,6 +363,15 @@ def build_lazy_cube(self, points, bounds=None, nx=4): lon = iris.coords.DimCoord(np.arange(nx), "longitude") cube.add_dim_coord(lat, 0) cube.add_dim_coord(lon, 1) + if aux_coords: + bounds = np.arange(len(points) * nx * 4).reshape( + len(points), nx, 4 + ) + bounds = as_lazy_data(bounds) + aux_coord = iris.coords.AuxCoord( + data, var_name="aux_coord", bounds=bounds + ) + cube.add_aux_coord(aux_coord, (0, 1)) return cube def test_lazy_concatenate(self): @@ -372,6 +381,20 @@ def test_lazy_concatenate(self): self.assertTrue(cube.has_lazy_data()) self.assertFalse(ma.isMaskedArray(cube.data)) + def test_lazy_concatenate_aux_coords(self): + c1 = self.build_lazy_cube([1, 2], aux_coords=True) + c2 = self.build_lazy_cube([3, 4, 5], aux_coords=True) + (result,) = concatenate([c1, c2]) + + self.assertTrue(c1.coord("aux_coord").has_lazy_points()) + self.assertTrue(c1.coord("aux_coord").has_lazy_bounds()) + + self.assertTrue(c2.coord("aux_coord").has_lazy_points()) + self.assertTrue(c2.coord("aux_coord").has_lazy_bounds()) + + self.assertTrue(result.coord("aux_coord").has_lazy_points()) + self.assertTrue(result.coord("aux_coord").has_lazy_bounds()) + def test_lazy_concatenate_masked_array_mixed_deferred(self): c1 = self.build_lazy_cube([1, 2]) c2 = self.build_lazy_cube([3, 4, 5])