def test_multiple_realizations_with_mask(self): """Test that the run method produces a cube with correct data when a cube containing masked data at multiple realizations is passed in. Re-masking is disabled.""" data = np.array([ [ [1, 1, 0, 1, 1], [1, 1, 1, 0, 0], [1, 0, 1, 0, 0], [0, 0, 1, 1, 0], [0, 1, 1, 0, 1], ], [ [1, 1, 0, 1, 1], [1, 1, 1, 0, 0], [1, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 1, 1, 0, 1], ], ]) mask = np.array([ [ [0, 0, 1, 1, 0], [0, 1, 1, 1, 0], [0, 0, 1, 1, 1], [0, 0, 1, 1, 0], [0, 0, 1, 1, 0], ], [ [0, 0, 1, 1, 0], [0, 1, 1, 1, 0], [0, 1, 1, 1, 1], [0, 0, 0, 1, 0], [0, 0, 1, 1, 0], ], ]) masked_data = np.ma.masked_where(mask == 0, data) self.multi_realization_cube.data = masked_data expected_array = np.array([ [ [1.0000, 0.666667, 0.600000, 0.500000, 0.500000], [1.0000, 0.750000, 0.571429, 0.428571, 0.250000], [1.0000, 1.000000, 0.714286, 0.571429, 0.250000], [np.nan, 1.000000, 0.666667, 0.571429, 0.250000], [np.nan, 1.000000, 0.750000, 0.750000, 0.500000], ], [ [1.0000, 0.666667, 0.600000, 0.500000, 0.500000], [0.5000, 0.600000, 0.500000, 0.428571, 0.250000], [0.5000, 0.750000, 0.428571, 0.333333, 0.000000], [0.0000, 0.666667, 0.333333, 0.333333, 0.000000], [np.nan, 1.000000, 0.333333, 0.333333, 0.000000], ], ]) result = SquareNeighbourhood(re_mask=False).run( self.multi_realization_cube, self.RADIUS) self.assertArrayAlmostEqual(result.data, expected_array)
def test_with_masked_data_and_no_remasking(self): """Test that removing halo works correctly with remask=False""" expected = np.array([[1., 1., 1.], [1., 0., 1.], [1., 1., 1.]]) grid_cells_x = grid_cells_y = 1 nbcube = (SquareNeighbourhood(re_mask=False)._remove_padding_and_mask( self.padded_cube, self.cube, self.mask_cube, grid_cells_x, grid_cells_y)) self.assertIsInstance(nbcube, Cube) self.assertArrayAlmostEqual(nbcube.data, expected)
def test_basic_padded(self): """Test cube with correct data is produced when mean over neighbourhood is calculated.""" nan_masks = [np.zeros(self.padded_cube.data.shape, dtype=int)] result = SquareNeighbourhood().mean_over_neighbourhood( self.padded_cube, self.width, self.width, nan_masks) self.assertIsInstance(result, Cube) self.assertArrayAlmostEqual(result.data[2:-2, 2:-2], self.padded_result)
def test_complex(self): """Test neighbourhooding with an array of complex wind directions""" # set up cube with complex wind directions 30-60 degrees a = 0.54066949 + 0.82535591j b = 0.56100423 + 0.80502117j c = 0.5 + 0.8660254j d = 0.59150635 + 0.77451905j expected_data_complex = np.array( [[a, b, b, a, b, c, a], [a, 0.55228934 + 0.81373606j, d, b, d, c, b], [b, 0.54575318 + 0.82027223j, d, b, d, c, b], [b, 0.62200847 + 0.74401694j, b, a, b, c, a], [a, 0.55228934 + 0.81373606j, d, b, d, c, b], [b, 0.57320508 + 0.79282032j, c, c, c, c, c], [c, c, b, a, b, c, a]]) expected_data_deg = np.array( [[ 56.77222443, 55.12808228, 55.12808228, 56.77222443, 55.12808228, 60.00000381, 56.77222443 ], [ 56.77222443, 55.83494186, 52.63074112, 55.12808228, 52.63074112, 60.00000381, 55.12808228 ], [ 55.12808228, 56.36291885, 52.63074112, 55.12808228, 52.63074112, 60.00000381, 55.12808228 ], [ 55.12808228, 50.10391235, 55.12808228, 56.77222443, 55.12808228, 60.00000381, 56.77222443 ], [ 56.77222443, 55.83494186, 52.63074112, 55.12808228, 52.63074112, 60.00000381, 55.12808228 ], [ 55.12808228, 54.13326263, 60.00000381, 60.00000381, 60.00000381, 60.00000381, 60.00000381 ], [ 60.00000381, 60.00000381, 55.12808228, 56.77222443, 55.12808228, 60.00000381, 56.77222443 ]], dtype=np.float32) self.cube.data = WindDirection.deg_to_complex(30. * self.cube.data + 30.) nbcube = (SquareNeighbourhood()._pad_and_calculate_neighbourhood( self.cube, self.mask, 1, 1)) self.assertTrue(np.any(np.iscomplex(nbcube.data))) self.assertArrayAlmostEqual(nbcube.data, expected_data_complex) self.assertArrayAlmostEqual(WindDirection.complex_to_deg(nbcube.data), expected_data_deg)
def test_return_type(self): """Test that the run_recursion method returns an iris.cube.Cube.""" edge_width = 1 alphas_x = RecursiveFilter().set_alphas(self.cube, self.alpha_x, None) alphas_y = RecursiveFilter().set_alphas(self.cube, self.alpha_y, None) padded_cube = SquareNeighbourhood().pad_cube_with_halo( self.cube, edge_width, edge_width) result = RecursiveFilter().run_recursion(padded_cube, alphas_x, alphas_y, self.iterations) self.assertIsInstance(result, Cube)
def test_without_masked_data(self): """Test that removing a halo of points from the data on a cube has worked as intended when the input data is not masked.""" expected = np.array([[1., 1., 1.], [1., 0., 1.], [1., 1., 1.]]) grid_cells_x = grid_cells_y = 1 nbcube = (SquareNeighbourhood()._remove_padding_and_mask( self.padded_cube, self.cube, self.no_mask, grid_cells_x, grid_cells_y)) self.assertIsInstance(nbcube, Cube) self.assertArrayAlmostEqual(nbcube.data, expected)
def test_expected_result(self): """Test that the run_recursion method returns the expected value.""" edge_width = 1 alphas_x = RecursiveFilter().set_alphas(self.cube, self.alpha_x, None) alphas_y = RecursiveFilter().set_alphas(self.cube, self.alpha_y, None) padded_cube = SquareNeighbourhood().pad_cube_with_halo( self.cube, edge_width, edge_width) result = RecursiveFilter().run_recursion(padded_cube, alphas_x, alphas_y, self.iterations) expected_result = 0.13382206 self.assertAlmostEqual(result.data[4][4], expected_result)
def test_basic_re_mask_false(self): """Test that a cube with correct data is produced by the run method.""" expected_array = np.array([ [1.0, 1.0, 1.0, 1.0, 1.0], [1.0, 0.88888889, 0.88888889, 0.88888889, 1.0], [1.0, 0.88888889, 0.88888889, 0.88888889, 1.0], [1.0, 0.88888889, 0.88888889, 0.88888889, 1.0], [1.0, 1.0, 1.0, 1.0, 1.0], ]) result = SquareNeighbourhood(re_mask=False).run(self.cube, self.RADIUS) self.assertArrayAlmostEqual(result.data, expected_array)
def test_realization(self): """Test that a cube is created with the expected order for coordinates within the output cube, if the input cube has dimensions of realization, projection_y_coordinate and projection_x_coordinate.""" for sliced_cube in self.cube.slices([ "realization", "projection_y_coordinate", "projection_x_coordinate" ]): break data = sliced_cube.data coord_x = sliced_cube.coord("projection_x_coordinate") coord_y = sliced_cube.coord("projection_y_coordinate") new_cube = SquareNeighbourhood()._create_cube_with_new_data( sliced_cube, data, coord_x, coord_y) self.assertIsInstance(new_cube, Cube) self.assertArrayAlmostEqual(new_cube.data, data) self.assertEqual(new_cube.coord_dims("realization")[0], 0) self.assertEqual(new_cube.coord_dims("projection_y_coordinate")[0], 1) self.assertEqual(new_cube.coord_dims("projection_x_coordinate")[0], 2)
def test_clipping(self): """Test that clipping is working""" expected = np.array([[1., 1., 1.], [1., 0., 1.], [1., 1., 1.]]) grid_cells_x = grid_cells_y = 1 self.padded_cube.data[2, 2] = 1.1 self.padded_cube.data[3, 3] = -0.1 nbcube = (SquareNeighbourhood(re_mask=False)._remove_padding_and_mask( self.padded_cube, self.cube, self.mask_cube, grid_cells_x, grid_cells_y)) self.assertIsInstance(nbcube, Cube) self.assertArrayAlmostEqual(nbcube.data, expected)
def test_nan_array_re_mask_false(self): """Test that an array containing nans is handled correctly.""" expected_array = np.array([ [np.nan, 1.0, 1.0, 1.0, 1.0], [1.0, 0.8750000, 0.88888889, 0.88888889, 1.0], [1.0, 0.88888889, 0.88888889, 0.88888889, 1.0], [1.0, 0.88888889, 0.88888889, 0.88888889, 1.0], [1.0, 1.0, 1.0, 1.0, 1.0], ]) self.cube.data[0, 0] = np.nan result = SquareNeighbourhood(re_mask=False).run(self.cube, self.RADIUS) self.assertArrayAlmostEqual(result.data, expected_array)
def test_without_masked_data(self): """Test setting up cubes to be neighbourhooded when the input cube does not contain masked arrays.""" expected_mask = np.ones((5, 5)) expected_nans = expected_mask.astype(bool)*False cube, mask, nan_array = ( SquareNeighbourhood.set_up_cubes_to_be_neighbourhooded(self.cube)) self.assertIsInstance(cube, Cube) self.assertIsInstance(mask, Cube) self.assertEqual(cube, self.cube) self.assertArrayEqual(nan_array, expected_nans) self.assertArrayEqual(mask.data, expected_mask)
def test_metadata(self): """Test that a cube with correct metadata is produced by the run method.""" cube = set_up_cube(zero_point_indices=((0, 0, 2, 2), ), num_time_points=1, num_grid_points=5) cube.attributes = {"Conventions": "CF-1.5"} cube.add_cell_method(CellMethod("mean", coords="time")) result = SquareNeighbourhood().run(cube, self.RADIUS) self.assertIsInstance(cube, Cube) self.assertTupleEqual(result.cell_methods, cube.cell_methods) self.assertDictEqual(result.attributes, cube.attributes)
def test_basic(self): """Test that removing a halo of points from the data on a cube has worked as intended.""" for sliced_cube in self.cube.slices( ["projection_y_coordinate", "projection_x_coordinate"]): break expected = np.array([[0.]]) width_x = width_y = 1 padded_cube = SquareNeighbourhood().remove_halo_from_cube( sliced_cube, width_x, width_y) self.assertIsInstance(padded_cube, Cube) self.assertArrayAlmostEqual(padded_cube.data, expected)
def test_forecast_period(self): """Test that a cube is created with the expected order for coordinates within the output cube, if the input cube has dimensions of projection_y_coordinate and projection_x_coordinate, and where the input cube also has a forecast_period coordinate.""" for sliced_cube in self.cube.slices( ["projection_y_coordinate", "projection_x_coordinate"]): break fp_coord = DimCoord(np.array([10]), standard_name="forecast_period") sliced_cube.add_aux_coord(fp_coord) data = sliced_cube.data coord_x = sliced_cube.coord("projection_x_coordinate") coord_y = sliced_cube.coord("projection_y_coordinate") new_cube = SquareNeighbourhood()._create_cube_with_new_data( sliced_cube, data, coord_x, coord_y) self.assertIsInstance(new_cube, Cube) self.assertArrayAlmostEqual(new_cube.data, data) self.assertEqual(new_cube.coord("forecast_period").points, 10) self.assertEqual(new_cube.coord_dims("projection_y_coordinate")[0], 0) self.assertEqual(new_cube.coord_dims("projection_x_coordinate")[0], 1)
def set_alphas(self, cube, alpha, alphas_cube): """ Set up the alpha parameter. Args: cube (Iris.cube.Cube): Cube containing the input data to which the recursive filter will be applied. alpha (Float): The constant used to weight the recursive filter in that direction: Defined such that 0.0 < alpha < 1.0 alphas_cube (Iris.cube.Cube or None): Cube containing array of alpha values that will be used when applying the recursive filter in a specific direction. Raises: ValueError: If both alphas_cube and alpha are provided. ValueError: If alpha and alphas_cube are both set to None ValueError: If dimension of alphas array is less than dimension of data array ValueError: If dimension of alphas array is greater than dimension of data array Returns: alphas_cube (Iris.cube.Cube): Cube containing a padded array of alpha values for the specified direction. """ if alpha is not None and alphas_cube is not None: emsg = ("A cube of alpha values and a single float value for alpha" " have both been provded. Only one of these options can be" " set.") raise ValueError(emsg) if alphas_cube is None: if alpha is None: emsg = ("A value for alpha must be set if alphas_cube is " "set to None: alpha is currently set as: {}") raise ValueError(emsg.format(alpha)) alphas_cube = cube.copy( data=np.ones(cube.data.shape) * alpha) if alphas_cube is not None: if alphas_cube.data.shape != cube.data.shape: emsg = ("Dimensions of alphas array do not match dimensions " "of data array: {} < {}") raise ValueError(emsg.format(alphas_cube.data.shape, cube.data.shape)) alphas_cube = SquareNeighbourhood().pad_cube_with_halo( alphas_cube, self.edge_width, self.edge_width) return alphas_cube
def test_remove(self): """Test the functionality to remove padding from the chosen coordinate. Includes a test that the coordinate bounds array is modified to reflect the new values.""" expected = np.array([30.]) expected_bounds = np.array([expected - 5, expected + 5]).T coord = self.cube.coord("projection_x_coordinate") width = 1 method = "remove" new_coord = SquareNeighbourhood.pad_coord(coord, width, method) self.assertIsInstance(new_coord, DimCoord) self.assertArrayAlmostEqual(new_coord.points, expected) self.assertArrayEqual(new_coord.bounds, expected_bounds)
def test_nan_mask(self): """Test the correct result is returned when a nan must be substituted into the final array. Note: this type of data should also be masked, so the expected_data array looks strange because there is further processing to be done on it.""" cube = self.padded_cube nan_masks = [np.zeros([9, 9]).astype(bool)] nan_masks[0][2, 2] = True expected_data = self.padded_result expected_data[0, 0] = np.nan result = SquareNeighbourhood().mean_over_neighbourhood( cube, self.width, self.width, nan_masks) self.assertArrayAlmostEqual(result.data[2:-2, 2:-2], expected_data)
def test_with_masked_data(self): """Test that removing a halo of points from the data on a cube has worked as intended when the input data has an associated mask.""" expected = np.array([[1., 1., 1.], [1., 0., 1.], [1., 1., 1.]]) expected_mask = np.array([[False, True, False], [False, False, True], [False, False, False]]) grid_cells_x = grid_cells_y = 1 nbcube = (SquareNeighbourhood()._remove_padding_and_mask( self.padded_cube, self.cube, self.mask_cube, grid_cells_x, grid_cells_y)) self.assertIsInstance(nbcube, Cube) self.assertArrayAlmostEqual(nbcube.data.data, expected) self.assertArrayAlmostEqual(nbcube.data.mask, expected_mask)
def test_basic_re_mask_true(self): """Test that a cube with correct data is produced by the run method when re-masking is applied.""" expected_array = np.array([ [1.0, 1.0, 1.0, 1.0, 1.0], [1.0, 0.88888889, 0.88888889, 0.88888889, 1.0], [1.0, 0.88888889, 0.88888889, 0.88888889, 1.0], [1.0, 0.88888889, 0.88888889, 0.88888889, 1.0], [1.0, 1.0, 1.0, 1.0, 1.0], ]) result = SquareNeighbourhood().run(self.cube, self.RADIUS) self.assertIsInstance(result, Cube) self.assertArrayAlmostEqual(result.data, expected_array)
def test_basic_re_mask_false(self): """Test that a cube with correct data is produced by the run method.""" data = np.array([[[[1., 1., 1., 1., 1.], [1., 0.88888889, 0.88888889, 0.88888889, 1.], [1., 0.88888889, 0.88888889, 0.88888889, 1.], [1., 0.88888889, 0.88888889, 0.88888889, 1.], [1., 1., 1., 1., 1.]]]]) cube = set_up_cube(zero_point_indices=((0, 0, 2, 2), ), num_time_points=1, num_grid_points=5) result = SquareNeighbourhood(re_mask=False).run(cube, self.RADIUS) self.assertIsInstance(cube, Cube) self.assertArrayAlmostEqual(result.data, data)
def test_add(self): """Test the functionality to add padding to the chosen coordinate. Includes a test that the coordinate bounds array is modified to reflect the new values.""" expected = np.array([-10., 0., 10., 20., 30., 40., 50., 60., 70.]) coord = self.cube.coord("projection_x_coordinate") expected_bounds = np.array([expected - 5, expected + 5]).T width = 1 method = "add" new_coord = SquareNeighbourhood.pad_coord(coord, width, method) self.assertIsInstance(new_coord, DimCoord) self.assertArrayAlmostEqual(new_coord.points, expected) self.assertArrayEqual(new_coord.bounds, expected_bounds)
def test_basic(self): """ Test that calculate neighbourhood returns correct values """ expected = np.array([[8., 5., -5., -8., -5., -3., 8.], [8., 6., -3., -5., -3., -2., 5.], [5., 7., 3., 5., 3., 2., -5.], [-5., -2., 5., 8., 5., 3., -8.], [-8., -6., 3., 5., 3., 2., -5.], [-5., -4., 2., 3., 2., 1., -3.], [-3., -6., -5., -8., -5., -3., 8.]]) result = (SquareNeighbourhood().calculate_neighbourhood( self.cube, self.ymax_xmax_disp, self.ymin_xmax_disp, self.ymin_xmin_disp, self.ymax_xmin_disp, self.n_rows, self.n_columns)) self.assertArrayEqual(result, expected)
def test_nan_array_re_mask_false(self): """Test that an array containing nans is handled correctly.""" data = np.array([[[[np.nan, 1., 1., 1., 1.], [1., 0.8750000, 0.88888889, 0.88888889, 1.], [1., 0.88888889, 0.88888889, 0.88888889, 1.], [1., 0.88888889, 0.88888889, 0.88888889, 1.], [1., 1., 1., 1., 1.]]]]) cube = set_up_cube(zero_point_indices=((0, 0, 2, 2), ), num_time_points=1, num_grid_points=5) cube.data[0, 0, 0, 0] = np.nan result = SquareNeighbourhood(re_mask=False).run(cube, self.RADIUS) self.assertIsInstance(cube, Cube) self.assertArrayAlmostEqual(result.data, data)
def test_basic(self): """Test that the run_recursion method returns an iris.cube.Cube.""" edge_width = 1 alphas_x = None alphas_y = None plugin = RecursiveFilter(alpha_x=self.alpha_x, alpha_y=self.alpha_y, iterations=self.iterations) alphas_x = plugin.set_alphas(self.cube, self.alpha_x, alphas_x) alphas_y = plugin.set_alphas(self.cube, self.alpha_y, alphas_y) padded_cube = SquareNeighbourhood().pad_cube_with_halo(self.cube, edge_width, edge_width) result = plugin.run_recursion(padded_cube, alphas_x, alphas_y, self.iterations) self.assertIsInstance(result, Cube)
def test_multiple_times(self): """Test mean over neighbourhood with more than two dimensions.""" data = np.array([self.padded_data, self.padded_data]) cube = Cube(data, long_name='two times test') cube.add_dim_coord(self.padded_x_coord, 1) cube.add_dim_coord(self.padded_y_coord, 2) t_coord = DimCoord([0, 1], standard_name='time') cube.add_dim_coord(t_coord, 0) nan_masks = [np.zeros(cube.data[0].shape, dtype=int)] * 2 result = SquareNeighbourhood().mean_over_neighbourhood( cube, self.width, self.width, nan_masks) self.assertArrayAlmostEqual(result.data[0, 2:-2, 2:-2], self.padded_result) self.assertArrayAlmostEqual(result.data[1, 2:-2, 2:-2], self.padded_result)
def test_basic(self): """ Test that the y-dimension and x-dimension accumulation produces the intended result. A 2d cube is passed in. """ data = np.array([[1., 2., 3., 4., 5.], [2., 4., 6., 8., 10.], [3., 6., 8., 11., 14.], [4., 8., 11., 15., 19.], [5., 10., 14., 19., 24.]]) cube = set_up_cube(zero_point_indices=((0, 0, 2, 2), ), num_time_points=1, num_grid_points=5) cube = iris.util.squeeze(cube) result_cube = SquareNeighbourhood().cumulate_array(cube) self.assertIsInstance(result_cube, Cube) self.assertArrayAlmostEqual(result_cube.data, data)
def test_expected_result(self): """Test that the run_recursion method returns an iris.cube.Cube.""" edge_width = 1 alphas_x = None alphas_y = None plugin = RecursiveFilter(alpha_x=self.alpha_x, alpha_y=self.alpha_y, iterations=self.iterations) alphas_x = plugin.set_alphas(self.cube, self.alpha_x, alphas_x) alphas_y = plugin.set_alphas(self.cube, self.alpha_y, alphas_y) padded_cube = SquareNeighbourhood().pad_cube_with_halo(self.cube, edge_width, edge_width) result = plugin.run_recursion(padded_cube, alphas_x, alphas_y, self.iterations) expected_result = 0.13382206 self.assertAlmostEqual(result.data[4][4], expected_result)
def test_with_masked_data(self): """Test setting up cubes to be neighbourhooded when the input cube contains masked arrays.""" cube = self.cube data = cube.data cube.data[0, 0, 1, 3] = 0.5 cube.data[0, 0, 3, 3] = 0.5 cube.data = np.ma.masked_equal(data, 0.5) mask = np.logical_not(cube.data.mask.astype(int)) data = cube.data.data * mask cubes = (SquareNeighbourhood._set_up_cubes_to_be_neighbourhooded( cube.copy())) self.assertIsInstance(cubes, CubeList) self.assertEqual(len(cubes), 2) self.assertArrayAlmostEqual(cubes[0].data, data) self.assertArrayAlmostEqual(cubes[1].data, mask)
def test_basic_sum(self): """Test cube with correct data is produced when mean over neighbourhood is calculated where the sum_or_fraction option is set to "sum".""" expected = np.array([[8., 5., -5., -8., -5., -3., 8.], [8., 6., -3., -5., -3., -2., 5.], [5., 7., 3., 5., 3., 2., -5.], [-5., -2., 5., 8., 5., 3., -8.], [-8., -6., 3., 5., 3., 2., -5.], [-5., -4., 2., 3., 2., 1., -3.], [-3., -6., -5., -8., -5., -3., 8.]]) result = SquareNeighbourhood( sum_or_fraction="sum").mean_over_neighbourhood( self.cube, self.mask, self.width, self.width) self.assertIsInstance(result, Cube) self.assertArrayAlmostEqual(result.data, expected)