def setUp(self): """Set up a cube.""" self.padded_cube = set_up_cube(zero_point_indices=((0, 0, 3, 3), ), num_time_points=1, num_grid_points=7) self.cube = set_up_cube(zero_point_indices=((0, 0, 1, 1), ), num_time_points=1, num_grid_points=3)
def test_coord_promotion(self): """Test that scalar coordinates in new_cube are promoted to dimension coordinates to match the parent cube.""" cube = set_up_cube() new_cube = iris.util.squeeze(cube) result = check_cube_coordinates(cube, new_cube) self.assertEqual(result.dim_coords, cube.dim_coords)
def test_single_point_on_edge(self): """Test behaviour for a non-zero grid cell on the edge. Note that this behaviour is 'wrong' and is a result of scipy.ndimage.correlate 'nearest' mode. We need to fix this in the future. """ cube = set_up_cube( zero_point_indices=[(0, 0, 7, 0)]) # On the (y) edge. expected = np.ones_like(cube.data) expected_centroid = np.array([ [0.92, 0.96, 0.992], [0.848, 0.912, 0.968], [0.824, 0.896, 0.96], [0.848, 0.912, 0.968], [0.92, 0.96, 0.992], ]) for index, slice_ in enumerate(expected_centroid): expected[0][0][5 + index][0:3] = slice_ ranges = (3, 3) result = ( CircularNeighbourhood( unweighted_mode=False).apply_circular_kernel(cube, ranges)) self.assertArrayAlmostEqual(result.data, expected)
def setUp(self): """Set up the test inputs.""" # Create a template cube. This cube has a forecast_reference_time of # 20151123T0400Z, a forecast period of T+4 and a # validity time of 2015-11-23 07:00:00 input_cube = iris.util.squeeze( add_forecast_reference_time_and_forecast_period(set_up_cube())) # Create an input cube with 3 realizations realizations = iris.cube.CubeList() for i in range(3): realization = input_cube.copy() realization.coord("realization").points = np.array(i) realizations.append(realization) self.input_cube = realizations.merge_cube() # Create a second cube from a later forecast with a different set of # realizations. self.input_cube2 = self.input_cube.copy() self.input_cube2.coord("forecast_reference_time").points = np.array( self.input_cube2.coord("forecast_reference_time").points[0] + 1) self.input_cube2.coord("forecast_period").points = np.array( self.input_cube2.coord("forecast_period").points[0] - 1) self.input_cube2.coord("realization").points = np.array([3, 4, 5]) # Put the two cubes in a cubelist ready to use in the plugin. self.input_cubelist = iris.cube.CubeList( [self.input_cube, self.input_cube2])
def test_multiple_times_with_mask(self): """Test that the run method produces a cube with correct data when a cube containing masked data at multiple time steps is passed in.""" cube = set_up_cube(zero_point_indices=((0, 0, 2, 2), ), num_time_points=2, num_grid_points=5) 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) cube.data = masked_data expected_array = np.array( [[[np.nan, np.nan, 0.57142857, 0.5, np.nan], [np.nan, 0.75, 0.57142857, 0.42857143, np.nan], [np.nan, np.nan, 0.71428571, 0.57142857, 0.2], [np.nan, np.nan, 0.66666667, 0.57142857, np.nan], [np.nan, np.nan, 0.66666667, 0.66666667, np.nan]], [[np.nan, np.nan, 0.57142857, 0.5, np.nan], [np.nan, 0.6, 0.5, 0.42857143, np.nan], [np.nan, 0.75, 0.42857143, 0.33333333, 0.], [np.nan, np.nan, np.nan, 0.33333333, np.nan], [np.nan, np.nan, 0.4, 0.4, np.nan]]]) result = SquareNeighbourhood().run(cube, self.RADIUS) self.assertArrayAlmostEqual(result.data.filled(), expected_array)
def test_for_multiple_times(self): """ Test that the y-dimension and x-dimension accumulation produces the intended result when the input cube has multiple times. The input cube has an extra time dimension to ensure that a 3d cube is correctly handled. """ 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.]], [[1., 2., 3., 4., 5.], [2., 4., 6., 8., 10.], [3., 6., 9., 12., 15.], [4., 8., 12., 15., 19.], [5., 10., 15., 19., 24.]], [[0., 1., 2., 3., 4.], [1., 3., 5., 7., 9.], [2., 5., 8., 11., 14.], [3., 7., 11., 15., 19.], [4., 9., 14., 19., 24.]]]) cube = set_up_cube(zero_point_indices=((0, 0, 2, 2), (0, 1, 3, 3), (0, 2, 0, 0)), num_time_points=3, num_grid_points=5) nan_mask = np.zeros(cube[0, 0, :, :].data.shape, dtype=int).flatten() result = SquareNeighbourhood().cumulate_array(cube) self.assertIsInstance(result[0], Cube) self.assertArrayAlmostEqual(result[0].data, data) self.assertArrayAlmostEqual(result[1][0].data, nan_mask)
def test_masked_array_with_nans_re_mask_false(self): """Test that the run method produces a cube with correct data when a cube containing masked nans is passed in.""" cube = set_up_cube( zero_point_indices=((0, 0, 2, 2),), num_time_points=1, num_grid_points=5) cube.data = np.array([[[[np.nan, 1, 0, 1, 1], [1, 1, 1, 0, 0], [1, 0, 1, 0, 0], [0, 0, 1, 1, 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]]]]) expected_array = np.array( [[[[np.nan, 0.666667, 0.600000, 0.500000, 0.50], [1.0000, 0.750000, 0.571429, 0.428571, 0.25], [1.0000, 1.000000, 0.714286, 0.571429, 0.25], [np.nan, 1.000000, 0.666667, 0.571429, 0.25], [np.nan, 1.000000, 0.750000, 0.750000, 0.50]]]]) cube.data = np.ma.masked_where(mask == 0, cube.data) result = SquareNeighbourhood(re_mask=False).run(cube, self.RADIUS) self.assertArrayAlmostEqual(result.data, expected_array)
def test_complex(self): """Test that a cube containing complex numbers is sensibly processed""" cube = set_up_cube(zero_point_indices=((0, 0, 2, 2), ), num_time_points=1, num_grid_points=5) cube.data = cube.data.astype(complex) cube.data[0, 0, 1, 3] = 0.5 + 0.5j cube.data[0, 0, 4, 3] = 0.4 + 0.6j expected_data = np.array([[ [[ 1.0 + 0.0j, 1.0 + 0.0j, 0.91666667 + 0.083333333j, 0.91666667 + 0.083333333j, 0.875 + 0.125j ], [ 1.0 + 0.0j, 0.88888889 + 0.0j, 0.83333333 + 0.055555556j, 0.83333333 + 0.055555556j, 0.91666667 + 0.083333333j ], [ 1.0 + 0.0j, 0.88888889 + 0.0j, 0.83333333 + 0.055555556j, 0.83333333 + 0.055555556j, 0.91666667 + 0.083333333j ], [ 1.0 + 0.0j, 0.88888889 + 0.0j, 0.82222222 + 0.066666667j, 0.82222222 + 0.066666667j, 0.9 + 0.1j ], [1.0 + 0.0j, 1.0 + 0.0j, 0.9 + 0.1j, 0.9 + 0.1j, 0.85 + 0.15j]] ]]) result = SquareNeighbourhood().run(cube, self.RADIUS) self.assertArrayAlmostEqual(result.data, expected_data)
def test_coord_promotion_and_reordering(self): """Test case in which a scalar coordinate are promoted but the order must be corrected to match the progenitor cube.""" cube = set_up_cube() new_cube = iris.util.squeeze(cube) cube.transpose(new_order=[1, 0, 2, 3]) result = check_cube_coordinates(cube, new_cube) self.assertEqual(result.dim_coords, cube.dim_coords)
def test_exception_raised(self): """Test that a CoordinateNotFoundError exception is raised if the forecast_period, or the time and forecast_reference_time, are not present. """ cube = set_up_cube() msg = "The forecast period coordinate is not available" with self.assertRaisesRegexp(CoordinateNotFoundError, msg): forecast_period_coord(cube)
def setUp(self): """Set up a cube.""" self.padded_cube = set_up_cube( zero_point_indices=((0, 0, 3, 3),), num_time_points=1, num_grid_points=7) self.padded_cube = iris.util.squeeze(self.padded_cube) self.cube = set_up_cube( zero_point_indices=((0, 0, 1, 1),), num_time_points=1, num_grid_points=3) self.cube = iris.util.squeeze(self.cube) self.mask_cube = self.cube.copy() masked_array = np.ones(self.mask_cube.data.shape) masked_array[1, 2] = 0 masked_array[0, 1] = 0 self.mask_cube.data = masked_array self.mask_cube.rename('mask_data') self.no_mask = self.mask_cube.copy() self.no_mask.data = np.ones(self.mask_cube.data.shape)
def test_check_coordinate(self): """ Test that the data within the numpy array is as expected, when the input cube has a forecast_period coordinate. """ cube = add_forecast_reference_time_and_forecast_period(set_up_cube()) expected_result = cube.coord("forecast_period").points result = find_required_lead_times(cube) self.assertArrayAlmostEqual(result, expected_result)
def test_coord_promotion_only_dim_coords_in_parent(self): """Test that only dimension coordinates in the parent cube are matched when promoting the scalar coordinates in new_cube. Here realization is made into a scalar coordinate on the parent, and so should remain a scalar in new_cube as well.""" cube = set_up_cube() new_cube = iris.util.squeeze(cube) cube = cube[0] result = check_cube_coordinates(cube, new_cube) self.assertEqual(result.dim_coords, cube.dim_coords)
def test_single_point_range_1(self): """Test behaviour with a non-zero point with unit range.""" cube = set_up_cube() expected = np.ones_like(cube.data) expected[0][0][7][7] = 0.0 ranges = (1, 1) result = ( CircularNeighbourhood( unweighted_mode=False).apply_circular_kernel(cube, ranges)) self.assertArrayAlmostEqual(result.data, expected)
def test_basic(self): """Test that the plugin returns an iris.cube.Cube.""" cube = set_up_cube( zero_point_indices=((0, 0, 2, 2),), num_time_points=1, num_grid_points=5) ranges = (2, 2) result = ( CircularNeighbourhood( unweighted_mode=True).apply_circular_kernel(cube, ranges)) self.assertIsInstance(result, Cube)
def test_no_permitted_exception_coordinates(self): """Test that if the new_cube has additional coordinates compared with the original cube, if no coordinates are listed as exception coordinates, then an exception will be raised.""" cube = set_up_cube() new_cube = cube[0].copy() cube = iris.util.squeeze(cube) msg = 'is not within the permitted exceptions' with self.assertRaisesRegexp(iris.exceptions.InvalidCubeError, msg): check_cube_coordinates(cube, new_cube)
def test_coord_promotion_missing_scalar(self): """Test case in which a scalar coordinate has been lost from new_cube, meaning the cube undergoing checking ends up with different dimension coordinates to the progenitor cube. This raises an error.""" cube = set_up_cube() new_cube = iris.util.squeeze(cube) new_cube.remove_coord('realization') msg = 'The number of dimension coordinates within the new cube' with self.assertRaisesRegexp(iris.exceptions.CoordinateNotFoundError, msg): check_cube_coordinates(cube, new_cube)
def setUp(self): """Set up a cube.""" self.cube = set_up_cube(zero_point_indices=((0, 0, 1, 1), ), num_time_points=1, num_grid_points=3) for sliced_cube in self.cube.slices( ["projection_y_coordinate", "projection_x_coordinate"]): break sliced_cube.remove_coord("realization") sliced_cube.remove_coord("time") self.cube = sliced_cube
def test_single_point(self): """Test behaviour for a single non-zero grid cell.""" cube = set_up_cube() expected = np.ones_like(cube.data) for index, slice_ in enumerate(SINGLE_POINT_RANGE_3_CENTROID): expected[0][0][5 + index][5:10] = slice_ ranges = (3, 3) result = ( CircularNeighbourhood( unweighted_mode=False).apply_circular_kernel(cube, ranges)) self.assertArrayAlmostEqual(result.data, expected)
def test_check_time_unit_conversion(self): """ Test that the data within the numpy array is as expected, when the input cube has a time coordinate with units other than the desired units of hours since 1970-01-01 00:00:00. """ cube = add_forecast_reference_time_and_forecast_period(set_up_cube()) expected_result = cube.coord("forecast_period").points.copy() cube.coord("time").convert_units("seconds since 1970-01-01 00:00:00") result = find_required_lead_times(cube) self.assertArrayAlmostEqual(result, expected_result)
def test_two_way_mismatch(self): """Test when finding a two-way mismatch, when the first and second cube contain different coordinates.""" cube = set_up_cube() first_cube = cube.copy() first_cube.remove_coord("time") second_cube = cube.copy() second_cube.remove_coord("realization") result = find_dimension_coordinate_mismatch(first_cube, second_cube) self.assertIsInstance(result, list) self.assertListEqual(result, ["time", "realization"])
def test_mismatch_in_first_cube(self): """Test when finding a one-way mismatch, so that the second cube has a missing coordinate. This returns an empty list.""" cube = set_up_cube() first_cube = cube.copy() second_cube = cube.copy() second_cube.remove_coord("time") result = find_dimension_coordinate_mismatch( first_cube, second_cube, two_way_mismatch=False) self.assertIsInstance(result, list) self.assertFalse(result)
def test_check_forecast_period_unit_conversion_exception(self): """ Test that an exception is raised, when the input cube has a forecast_period coordinate with units that can not be converted into hours. """ cube = add_forecast_reference_time_and_forecast_period(set_up_cube()) cube.coord("forecast_period").units = Unit("Celsius") msg = "For forecast_period" with self.assertRaisesRegexp(ValueError, msg): find_required_lead_times(cube)
def test_check_time_unit_conversion(self): """Test that the data within the coord is as expected with the expected units, when the input cube has a time coordinate with units other than the usual units of hours since 1970-01-01 00:00:00. """ cube = add_forecast_reference_time_and_forecast_period(set_up_cube()) fp_coord = cube.coord("forecast_period").copy() fp_coord.convert_units("seconds") expected_result = fp_coord cube.coord("time").convert_units("seconds since 1970-01-01 00:00:00") result = forecast_period_coord(cube, force_lead_time_calculation=True) self.assertEqual(result, expected_result)
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_check_coordinate_without_forecast_period(self): """Test that the data within the coord is as expected with the expected units, when the input cube has a time coordinate and a forecast_reference_time coordinate. """ cube = add_forecast_reference_time_and_forecast_period(set_up_cube()) fp_coord = cube.coord("forecast_period").copy() fp_coord.convert_units("seconds") expected_result = fp_coord cube.remove_coord("forecast_period") result = forecast_period_coord(cube) self.assertEqual(result, expected_result)
def test_check_coordinate_force_lead_time_calculation(self): """Test that the data within the coord is as expected with the expected units, when the input cube has a forecast_period coordinate. """ cube = add_forecast_reference_time_and_forecast_period(set_up_cube()) fp_coord = cube.coord("forecast_period").copy() fp_coord.convert_units("seconds") expected_points = fp_coord.points expected_units = str(fp_coord.units) result = forecast_period_coord(cube, force_lead_time_calculation=True) self.assertArrayAlmostEqual(result.points, expected_points) self.assertEqual(result.units, expected_units)
def test_permitted_exception_coordinates(self): """Test that if the new_cube is known to have additional coordinates compared with the original cube, these coordinates are listed are exception_coordinates and handled correctly.""" cube = set_up_cube() new_cube = cube[0].copy() cube = iris.util.squeeze(cube) exception_coordinates = ["time"] result = check_cube_coordinates( cube, new_cube, exception_coordinates=exception_coordinates) dim_coords = tuple(new_cube.coord("time")) + cube.dim_coords self.assertEqual(result.dim_coords, dim_coords)
def test_mismatch_in_second_cube(self): """Test when finding a one-way mismatch, so that the first cube has a missing coordinate. This returns a list with the missing coordinate name.l""" cube = set_up_cube() first_cube = cube.copy() first_cube.remove_coord("time") second_cube = cube.copy() result = find_dimension_coordinate_mismatch( first_cube, second_cube, two_way_mismatch=False) self.assertIsInstance(result, list) self.assertListEqual(result, ["time"])
def test_single_point_almost_corner(self): """Test behaviour for a non-zero grid cell quite near a corner.""" cube = set_up_cube( zero_point_indices=[(0, 0, 2, 2)]) # Just within corner range. expected = np.ones_like(cube.data) for index, slice_ in enumerate(SINGLE_POINT_RANGE_3_CENTROID): expected[0][0][index][0:5] = slice_ ranges = (3, 3) result = ( CircularNeighbourhood( unweighted_mode=False).apply_circular_kernel(cube, ranges)) self.assertArrayAlmostEqual(result.data, expected)