diff --git a/agg_regrid/__init__.py b/agg_regrid/__init__.py index d14713c..824fb94 100644 --- a/agg_regrid/__init__.py +++ b/agg_regrid/__init__.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2015 - 2018, Met Office +# (C) British Crown Copyright 2015 - 2019, Met Office # # This file is part of agg-regrid. # @@ -210,8 +210,14 @@ def __call__(self, src_cube): sx_dim = src_cube.coord_dims(sx)[0] sy_dim = src_cube.coord_dims(sy)[0] + # Manipulating masked arrays can be of the order 4-5 times slower, + # therefore use the underlying numpy array if there are no masked data + data = src_cube.data + if ma.isMA(data) and not ma.is_masked(data): + data = data.data + # Perform the regrid. - result = agg(src_cube.data, sx.points, self._sx_bounds, + result = agg(data, sx.points, self._sx_bounds, sy.points, self._sy_bounds, sx_dim, sy_dim, self._gx_bounds, self._gy_bounds, self.buffer_depth) @@ -319,24 +325,22 @@ def agg(data, sx_points, sx_bounds, sy_points, sy_bounds, raise ValueError(emsg.format(data.ndim)) # Account for negative indexing ... - dim = sx_dim if sx_dim < 0: - sx_dim = ndim + sx_dim + sx_dim += ndim if sx_dim not in dims: emsg = 'Invalid src x-coordinate dimension, got {} expected ' \ 'within range {}-{}.' - raise ValueError(emsg.format(dim, dims[0], dims[-1])) + raise ValueError(emsg.format(sx_dim, dims[0], dims[-1])) # Account for negative indexing ... - dim = sy_dim if sy_dim < 0: - sy_dim = ndim + sy_dim + sy_dim += ndim if sy_dim not in dims: emsg = 'Invalid src y-coordinate dimension, got {} expected ' \ 'within range {}-{}.' - raise ValueError(emsg.format(dim, dims[0], dims[-1])) + raise ValueError(emsg.format(sy_dim, dims[0], dims[-1])) # Determine the source data shape ... shape = data.shape diff --git a/agg_regrid/tests/test_AreaWeighted.py b/agg_regrid/tests/test_AreaWeighted.py index c178c56..afb04a1 100644 --- a/agg_regrid/tests/test_AreaWeighted.py +++ b/agg_regrid/tests/test_AreaWeighted.py @@ -46,6 +46,17 @@ def test_regridder(self): expected = [mock.call(self.src, self.tgt, buffer_depth=self.depth)] self.assertEqual(mocker.mock_calls, expected) + def test_regridder_with_buffer_depth(self): + depth = mock.sentinel.buffer_depth + regridder = 'agg_regrid._AreaWeightedRegridder' + with mock.patch(regridder, autospec=True, + return_value=self.regridder) as mocker: + scheme = AreaWeighted(buffer_depth=depth) + result = scheme.regridder(self.src, self.tgt) + self.assertEqual(result, self.regridder) + expected = [mock.call(self.src, self.tgt, buffer_depth=depth)] + self.assertEqual(mocker.mock_calls, expected) + if __name__ == '__main__': unittest.main() diff --git a/agg_regrid/tests/test__AreadWeightedRegridder.py b/agg_regrid/tests/test__AreadWeightedRegridder.py index e2b1607..78b6f55 100644 --- a/agg_regrid/tests/test__AreadWeightedRegridder.py +++ b/agg_regrid/tests/test__AreadWeightedRegridder.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2015 - 2018, Met Office +# (C) British Crown Copyright 2015 - 2019, Met Office # # This file is part of agg-regrid. # @@ -27,6 +27,7 @@ from unittest import mock except ImportError: import mock +import numpy.ma as ma from agg_regrid import (_AreaWeightedRegridder as Regridder, DEFAULT_BUFFER_DEPTH) @@ -117,14 +118,12 @@ def setUp(self): self.tgt_cube = mock.Mock(spec=iris.cube.Cube) self.sx_dim = mock.sentinel.sx_dim self.sy_dim = mock.sentinel.sy_dim - coord_dims = mock.Mock(side_effect=([self.sx_dim], [self.sy_dim])) + coord_dims = mock.Mock(side_effect=([self.sx_dim], [self.sy_dim], + [self.sx_dim], [self.sy_dim])) self.metadata = dict(standard_name='air_pressure', long_name='long_name', var_name='var_name', units='Pa', attributes={}, cell_methods=()) self.data = mock.sentinel.data - self.cube = mock.Mock(spec=iris.cube.Cube, coord_dims=coord_dims, - metadata=self.metadata, dim_coords=(), - aux_coords=(), data=self.data) scrs = mock.sentinel.scrs self.sxp = mock.sentinel.sx_points self.sxb = mock.sentinel.sx_contiguous_bounds @@ -140,11 +139,19 @@ def setUp(self): gx = mock.Mock(coord_system=tcrs) gy = mock.Mock(coord_system=tcrs) self.src_grid = (self.sx, self.sy) - tgt_grid = (gx, gy) - self.side_effect = (self.src_grid, tgt_grid) + self.tgt_grid = (gx, gy) + dim_coords = [self.sx, self.sy] + self.cube = mock.Mock(spec=iris.cube.Cube, coord_dims=coord_dims, + metadata=self.metadata, dim_coords=dim_coords, + aux_coords=(), data=self.data) + self.side_effect = (self.src_grid, self.tgt_grid) + self.gmesh = (mock.sentinel.gxx, mock.sentinel.gyy) self.snapshot_grid = 'agg_regrid.snapshot_grid' self.get_xy_dim_coords = 'agg_regrid.get_xy_dim_coords' - self.depth = DEFAULT_BUFFER_DEPTH + self.meshgrid = 'numpy.meshgrid' + self.agg = 'agg_regrid.agg' + self.add_dim_coord = 'iris.cube.Cube.add_dim_coord' + self.depth = mock.sentinel.buffer_depth def test_bad_src_cube(self): with mock.patch(self.snapshot_grid, side_effect=self.side_effect): @@ -176,14 +183,16 @@ def test_same_crs(self): with mock.patch(self.snapshot_grid, side_effect=side_effect): with mock.patch(self.get_xy_dim_coords, return_value=self.src_grid): - gxx, gyy = (mock.sentinel.gxx, mock.sentinel.gyy) - with mock.patch('numpy.meshgrid', return_value=(gxx, gyy)): + with mock.patch(self.meshgrid, return_value=self.gmesh): data = 1 - with mock.patch('agg_regrid.agg', - return_value=data) as magg: - regridder = Regridder(self.src_cube, self.tgt_cube) - result = regridder(self.cube) - + with mock.patch(self.agg, return_value=data) as magg: + with mock.patch(self.add_dim_coord) as madd: + regridder = Regridder(self.src_cube, + self.tgt_cube, + buffer_depth=self.depth) + result = regridder(self.cube) + + gxx, gyy = self.gmesh self.assertEqual(regridder._sx_bounds, self.sxb) self.assertEqual(regridder._sy_bounds, self.syb) self.assertEqual(regridder._gx_bounds, gxx) @@ -192,9 +201,32 @@ def test_same_crs(self): self.syb, self.sx_dim, self.sy_dim, gxx, gyy, self.depth)] self.assertEqual(magg.call_args_list, expected) + expected = [mock.call(self.sx.copy(), [self.sx_dim]), + mock.call(self.sy.copy(), [self.sy_dim])] + self.assertEqual(madd.call_args_list, expected) cube = iris.cube.Cube(data) cube.metadata = self.metadata self.assertEqual(result, cube) + self.assertEqual(regridder.buffer_depth, self.depth) + + def test_masked_with_no_masked_points(self): + data = ma.arange(1) + self.cube.data = data + side_effect = (self.src_grid, self.src_grid) + with mock.patch(self.snapshot_grid, side_effect=side_effect): + with mock.patch(self.get_xy_dim_coords, + return_value=self.src_grid): + with mock.patch(self.meshgrid, return_value=self.gmesh): + with mock.patch(self.agg, return_value=1) as magg: + with mock.patch(self.add_dim_coord): + regridder = Regridder(self.src_cube, self.tgt_cube) + regridder(self.cube) + + gxx, gyy = self.gmesh + expected = [mock.call(data.data, self.sxp, self.sxb, self.syp, + self.syb, self.sx_dim, self.sy_dim, gxx, gyy, + DEFAULT_BUFFER_DEPTH)] + self.assertEqual(magg.call_args_list, expected) if __name__ == '__main__':