diff --git a/lib/iris/aux_factory.py b/lib/iris/aux_factory.py index 729817adf9..efd6607fc6 100644 --- a/lib/iris/aux_factory.py +++ b/lib/iris/aux_factory.py @@ -170,7 +170,8 @@ def _dependency_dims(self, coord_dims_func): dependency_dims[key] = coord_dims_func(coord) return dependency_dims - def _nd_bounds(self, coord, dims, ndim): + @staticmethod + def _nd_bounds(coord, dims, ndim): """ Returns the coord's bounds in Cube-orientation and broadcastable to N dimensions. @@ -187,7 +188,7 @@ def _nd_bounds(self, coord, dims, ndim): # Transpose to be consistent with the Cube. sorted_pairs = sorted(enumerate(dims), key=lambda pair: pair[1]) transpose_order = [pair[0] for pair in sorted_pairs] + [len(dims)] - bounds = coord.bounds + bounds = coord.core_bounds() if dims: bounds = bounds.transpose(transpose_order) @@ -296,7 +297,6 @@ def _remap_with_bounds(self, dependency_dims, derived_dims): # extra dimension to make the shape compatible, so # we just add an extra 1. shape.append(1) - nd_values = np.array(nd_values) nd_values = nd_values.reshape(shape) else: # If no coord, treat value as zero. diff --git a/lib/iris/tests/unit/aux_factory/test_AuxCoordFactory.py b/lib/iris/tests/unit/aux_factory/test_AuxCoordFactory.py index df9190de45..4b24f3887b 100644 --- a/lib/iris/tests/unit/aux_factory/test_AuxCoordFactory.py +++ b/lib/iris/tests/unit/aux_factory/test_AuxCoordFactory.py @@ -26,39 +26,48 @@ # importing anything else. import iris.tests as tests -from iris._lazy_data import as_lazy_data, is_lazy_data import numpy as np -import iris.coords +import iris +from iris._lazy_data import as_lazy_data, is_lazy_data from iris.aux_factory import AuxCoordFactory +from iris.coords import AuxCoord class Test__nd_points(tests.IrisTest): - def test_numpy_scalar_cooord(self): - points = np.arange(1) - coord = iris.coords.AuxCoord(points) + def test_numpy_scalar_coord__zero_ndim(self): + points = np.array(1) + coord = AuxCoord(points) + result = AuxCoordFactory._nd_points(coord, (), 0) + expected = np.array([1]) + self.assertArrayEqual(result, expected) + + def test_numpy_scalar_coord(self): + value = 1 + points = np.array(value) + coord = AuxCoord(points) result = AuxCoordFactory._nd_points(coord, (), 2) - expected = points[np.newaxis] + expected = np.array(value).reshape(1, 1) self.assertArrayEqual(result, expected) def test_numpy_simple(self): points = np.arange(12).reshape(4, 3) - coord = iris.coords.AuxCoord(points) + coord = AuxCoord(points) result = AuxCoordFactory._nd_points(coord, (0, 1), 2) expected = points self.assertArrayEqual(result, expected) def test_numpy_complex(self): points = np.arange(12).reshape(4, 3) - coord = iris.coords.AuxCoord(points) + coord = AuxCoord(points) result = AuxCoordFactory._nd_points(coord, (3, 2), 5) expected = points.T[np.newaxis, np.newaxis, ..., np.newaxis] self.assertArrayEqual(result, expected) def test_lazy_simple(self): raw_points = np.arange(12).reshape(4, 3) - points = as_lazy_data(raw_points, 1) - coord = iris.coords.AuxCoord(points) + points = as_lazy_data(raw_points, raw_points.shape) + coord = AuxCoord(points) self.assertTrue(is_lazy_data(coord.core_points())) result = AuxCoordFactory._nd_points(coord, (0, 1), 2) # Check we haven't triggered the loading of the coordinate values. @@ -69,8 +78,8 @@ def test_lazy_simple(self): def test_lazy_complex(self): raw_points = np.arange(12).reshape(4, 3) - points = as_lazy_data(raw_points, 1) - coord = iris.coords.AuxCoord(points) + points = as_lazy_data(raw_points, raw_points.shape) + coord = AuxCoord(points) self.assertTrue(is_lazy_data(coord.core_points())) result = AuxCoordFactory._nd_points(coord, (3, 2), 5) # Check we haven't triggered the loading of the coordinate values. @@ -80,5 +89,91 @@ def test_lazy_complex(self): self.assertArrayEqual(result, expected) +class Test__nd_bounds(tests.IrisTest): + def test_numpy_scalar_coord__zero_ndim(self): + points = np.array(0.5) + bounds = np.arange(2) + coord = AuxCoord(points, bounds=bounds) + result = AuxCoordFactory._nd_bounds(coord, (), 0) + expected = bounds + self.assertArrayEqual(result, expected) + + def test_numpy_scalar_coord(self): + points = np.array(0.5) + bounds = np.arange(2).reshape(1, 2) + coord = AuxCoord(points, bounds=bounds) + result = AuxCoordFactory._nd_bounds(coord, (), 2) + expected = bounds[np.newaxis] + self.assertArrayEqual(result, expected) + + def test_numpy_simple(self): + points = np.arange(12).reshape(4, 3) + bounds = np.arange(24).reshape(4, 3, 2) + coord = AuxCoord(points, bounds=bounds) + result = AuxCoordFactory._nd_bounds(coord, (0, 1), 2) + expected = bounds + self.assertArrayEqual(result, expected) + + def test_numpy_complex(self): + points = np.arange(12).reshape(4, 3) + bounds = np.arange(24).reshape(4, 3, 2) + coord = AuxCoord(points, bounds=bounds) + result = AuxCoordFactory._nd_bounds(coord, (3, 2), 5) + expected = bounds.transpose((1, 0, 2)).reshape(1, 1, 3, 4, 1, 2) + self.assertArrayEqual(result, expected) + + def test_lazy_simple(self): + raw_points = np.arange(12).reshape(4, 3) + points = as_lazy_data(raw_points, raw_points.shape) + raw_bounds = np.arange(24).reshape(4, 3, 2) + bounds = as_lazy_data(raw_bounds, raw_bounds.shape) + coord = AuxCoord(points, bounds=bounds) + self.assertTrue(is_lazy_data(coord.core_bounds())) + result = AuxCoordFactory._nd_bounds(coord, (0, 1), 2) + # Check we haven't triggered the loading of the coordinate values. + self.assertTrue(is_lazy_data(coord.core_bounds())) + self.assertTrue(is_lazy_data(result)) + expected = raw_bounds + self.assertArrayEqual(result, expected) + + def test_lazy_complex(self): + raw_points = np.arange(12).reshape(4, 3) + points = as_lazy_data(raw_points, raw_points.shape) + raw_bounds = np.arange(24).reshape(4, 3, 2) + bounds = as_lazy_data(raw_bounds, raw_bounds.shape) + coord = AuxCoord(points, bounds=bounds) + self.assertTrue(is_lazy_data(coord.core_bounds())) + result = AuxCoordFactory._nd_bounds(coord, (3, 2), 5) + # Check we haven't triggered the loading of the coordinate values. + self.assertTrue(is_lazy_data(coord.core_bounds())) + self.assertTrue(is_lazy_data(result)) + expected = raw_bounds.transpose((1, 0, 2)).reshape(1, 1, 3, 4, 1, 2) + self.assertArrayEqual(result, expected) + + +@tests.skip_data +class Test_lazy_aux_coords(tests.IrisTest): + def setUp(self): + self.cube = iris.load_cube(tests.get_data_path + (['NetCDF', 'testing', + 'small_theta_colpex.nc'])) + self.coords = self.cube.aux_coords + self.cube.derived_coords + + def _check_lazy(self): + for coord in self.coords: + self.assertTrue(coord.has_lazy_points()) + if coord.has_bounds(): + self.assertTrue(coord.has_lazy_bounds()) + + def test_lazy_coord_loading(self): + # Test that points and bounds arrays stay lazy upon cube loading. + self._check_lazy() + + def test_lazy_coord_printing(self): + # Test that points and bounds arrays stay lazy after cube printing. + _ = str(self.cube) + self._check_lazy() + + if __name__ == '__main__': tests.main()