def test_exception_mismatched_dimensions(self): """Test an error is raised if dimension coordinates do not match""" self.cube2.coord("lwe_thickness_of_precipitation_amount").rename("snow_depth") plugin = CubeCombiner("+") msg = "Cannot combine cubes with different dimensions" with self.assertRaisesRegex(ValueError, msg): plugin.process([self.cube1, self.cube2], "new_cube_name")
def test_exception_mismatched_dimensions(self): """Test an error is raised if dimension coordinates do not match""" self.cube2.coord("latitude").rename("projection_y_coordinate") plugin = CubeCombiner("+") msg = "Cannot combine cubes with different dimensions" with self.assertRaisesRegex(ValueError, msg): plugin.process([self.cube1, self.cube2], "new_cube_name")
def test_exception_for_cube_passed_in(self): """Test that the plugin raises an exception if something other than a cubelist is passed in.""" plugin = CubeCombiner('-') msg = "Expecting data to be an instance of" with self.assertRaisesRegex(TypeError, msg): plugin.process(self.cube1, 'new_cube_name')
def test_exception_for_single_entry_cubelist(self): """Test that the plugin raises an exception if a cubelist containing only one cube is passed in.""" plugin = CubeCombiner("-") msg = "Expecting 2 or more cubes in cube_list" cubelist = iris.cube.CubeList([self.cube1]) with self.assertRaisesRegex(ValueError, msg): plugin.process(cubelist, "new_cube_name")
def test_mixed_dtypes_overflow(self): """Test the plugin with a dtype combination that results in float64 data.""" plugin = CubeCombiner("add") cubelist = iris.cube.CubeList( [self.cube1, self.cube2.copy(np.ones_like(self.cube2.data, dtype=np.int32))] ) msg = "Operation .* results in float64 data" with self.assertRaisesRegex(TypeError, msg): plugin.process(cubelist, "new_cube_name")
def test_error_broadcast_coord_is_auxcoord(self): """Test that plugin throws an error if the broadcast coord already exists""" plugin = CubeCombiner("*") cube = self.cube4[:, 0, ...].copy() cube.data = np.ones_like(cube.data) cubelist = iris.cube.CubeList([self.cube4.copy(), cube]) msg = "Cannot broadcast to coord threshold as it already exists as an AuxCoord" with self.assertRaisesRegex(TypeError, msg): plugin.process(cubelist, "new_cube_name", broadcast_to_coords=["threshold"])
def test_error_broadcast_coord_not_found(self): """Test that plugin throws an error if the broadcast coord is not present anywhere""" plugin = CubeCombiner("*") cube = self.cube4[:, 0, ...].copy() cube.data = np.ones_like(cube.data) cubelist = iris.cube.CubeList([self.cube4.copy(), cube]) msg = ( "Cannot find coord kittens in " "<iris 'Cube' of probability_of_lwe_thickness_of_precipitation_amount_above_threshold / \(1\) " "\(realization: 3; lwe_thickness_of_precipitation_amount: 2; latitude: 2; longitude: 2\)> " "to broadcast to." ) with self.assertRaisesRegex(CoordinateNotFoundError, msg): plugin.process(cubelist, "new_cube_name", broadcast_to_coords=["kittens"])
def test_error_broadcast_coord_wrong_order(self): """Test that plugin throws an error if the broadcast coord is not on the first cube""" plugin = CubeCombiner("*") cube = self.cube4[:, 0, ...].copy() cube.data = np.ones_like(cube.data) cube.remove_coord("lwe_thickness_of_precipitation_amount") cubelist = iris.cube.CubeList([cube, self.cube4.copy()]) msg = ( "Cannot find coord threshold in " "<iris 'Cube' of probability_of_lwe_thickness_of_precipitation_amount_above_threshold / \(1\) " "\(realization: 3; latitude: 2; longitude: 2\)> to broadcast to" ) with self.assertRaisesRegex(CoordinateNotFoundError, msg): plugin.process(cubelist, "new_cube_name", broadcast_to_coords=["threshold"])
def test_multiply_preserves_bounds(self): """Test specific case for precipitation type, where multiplying a precipitation accumulation by a point-time probability of snow retains the bounds on the original accumulation.""" validity_time = datetime(2015, 11, 19, 0) time_bounds = [datetime(2015, 11, 18, 23), datetime(2015, 11, 19, 0)] forecast_reference_time = datetime(2015, 11, 18, 22) precip_accum = set_up_variable_cube( np.full((2, 3, 3), 1.5, dtype=np.float32), name="lwe_thickness_of_precipitation_amount", units="mm", time=validity_time, time_bounds=time_bounds, frt=forecast_reference_time, ) snow_prob = set_up_variable_cube( np.full(precip_accum.shape, 0.2, dtype=np.float32), name="probability_of_snow", units="1", time=validity_time, frt=forecast_reference_time, ) plugin = CubeCombiner("multiply") result = plugin.process([precip_accum, snow_prob], "lwe_thickness_of_snowfall_amount") self.assertArrayAlmostEqual(result.data, np.full((2, 3, 3), 0.3)) self.assertArrayEqual(result.coord("time"), precip_accum.coord("time"))
def test_mean(self): """Test that the plugin calculates the mean correctly. """ plugin = CubeCombiner("mean") cubelist = iris.cube.CubeList([self.cube1, self.cube2]) result = plugin.process(cubelist, "new_cube_name") expected_data = np.full((2, 2), 0.55, dtype=np.float32) self.assertEqual(result.name(), "new_cube_name") self.assertArrayAlmostEqual(result.data, expected_data)
def test_mean_multi_cube(self): """Test that the plugin calculates the mean for three cubes. """ plugin = CubeCombiner('mean') cubelist = iris.cube.CubeList([self.cube1, self.cube2, self.cube3]) result = plugin.process(cubelist, 'new_cube_name') expected_data = np.full((1, 2, 2), 0.4, dtype=np.float32) self.assertEqual(result.name(), 'new_cube_name') self.assertArrayAlmostEqual(result.data, expected_data)
def test_basic(self): """Test that the plugin returns a Cube. """ plugin = CubeCombiner('+') cubelist = iris.cube.CubeList([self.cube1, self.cube2]) result = plugin.process(cubelist, 'new_cube_name') self.assertIsInstance(result, Cube) self.assertEqual(result.name(), 'new_cube_name') expected_data = np.full((1, 2, 2), 1.1, dtype=np.float32) self.assertArrayAlmostEqual(result.data, expected_data)
def test_bounds_expansion_midpoint(self): """Test option to use the midpoint between the bounds as the time coordinate point, rather than the (default) maximum.""" plugin = CubeCombiner("add") cubelist = iris.cube.CubeList([self.cube1, self.cube2]) result = plugin.process(cubelist, "new_cube_name", use_midpoint=True) self.assertEqual(result.name(), "new_cube_name") self.assertEqual(result.coord("time").points[0], 1447891200) self.assertArrayEqual(result.coord("time").bounds, [[1447887600, 1447894800]])
def test_mean(self): """Test that the plugin calculates the mean correctly. """ plugin = CubeCombiner('mean') cubelist = iris.cube.CubeList([self.cube1, self.cube2]) result = plugin.process(cubelist, 'new_cube_name') expected_data = np.zeros((1, 2, 2, 2)) expected_data[:, 0, :, :] = 0.3 expected_data[:, 1, :, :] = 0.5 self.assertEqual(result.name(), 'new_cube_name') self.assertArrayAlmostEqual(result.data, expected_data)
def test_basic(self): """Test that the plugin returns a Cube and doesn't modify the inputs.""" plugin = CubeCombiner("+") cubelist = iris.cube.CubeList([self.cube1, self.cube2]) input_copy = deepcopy(cubelist) result = plugin.process(cubelist, "new_cube_name") self.assertIsInstance(result, Cube) self.assertEqual(result.name(), "new_cube_name") expected_data = np.full((2, 2), 1.1, dtype=np.float32) self.assertArrayAlmostEqual(result.data, expected_data) self.assertCubeListEqual(input_copy, cubelist)
def test_basic(self): """Test that the plugin returns a Cube. """ plugin = CubeCombiner('+') cubelist = iris.cube.CubeList([self.cube1, self.cube1]) result = plugin.process(cubelist, 'new_cube_name') self.assertIsInstance(result, Cube) self.assertEqual(result.name(), 'new_cube_name') expected_data = np.zeros((1, 2, 2, 2)) expected_data[:, 0, :, :] = 1.0 expected_data[:, 1, :, :] = 1.2 self.assertArrayAlmostEqual(result.data, expected_data)
def test_with_mask(self): """Test that the plugin preserves the mask if any of the inputs are masked""" expected_data = np.full((1, 2, 2), 1.2, dtype=np.float32) mask = [[[False, True], [False, False]]] self.cube1.data = np.ma.MaskedArray(self.cube1.data, mask=mask) plugin = CubeCombiner("add") result = plugin.process([self.cube1, self.cube2, self.cube3], "new_cube_name") self.assertIsInstance(result.data, np.ma.MaskedArray) self.assertArrayAlmostEqual(result.data.data, expected_data) self.assertArrayEqual(result.data.mask, mask)
def test_bounds_expansion(self): """Test that the plugin calculates the sum of the input cubes correctly and expands the time coordinate bounds on the resulting output.""" plugin = CubeCombiner("add") cubelist = iris.cube.CubeList([self.cube1, self.cube2]) result = plugin.process(cubelist, "new_cube_name") expected_data = np.full((1, 2, 2), 1.1, dtype=np.float32) self.assertEqual(result.name(), "new_cube_name") self.assertArrayAlmostEqual(result.data, expected_data) self.assertEqual(result.coord("time").points[0], 1447894800) self.assertArrayEqual(result.coord("time").bounds, [[1447887600, 1447894800]])
def test_revised_attributes(self): """Test that the plugin passes through the relevant dictionary to modify an attribute and that these modifications are present in the returned cube.""" plugin = CubeCombiner('mean') revised_attributes = {'attribute_to_update': 'second_value'} cubelist = iris.cube.CubeList([self.cube1, self.cube2]) result = plugin.process(cubelist, 'new_cube_name', revised_attributes=revised_attributes) self.assertEqual(result.attributes['attribute_to_update'], 'second_value')
def test_mixed_dtypes(self): """Test that the plugin calculates the sum correctly and doesn't mangle dtypes.""" plugin = CubeCombiner("add") cubelist = iris.cube.CubeList( [self.cube1, self.cube2.copy(np.ones_like(self.cube2.data, dtype=np.int8))] ) result = plugin.process(cubelist, "new_cube_name") expected_data = np.full((2, 2), 1.5, dtype=np.float32) self.assertEqual(result.name(), "new_cube_name") self.assertArrayAlmostEqual(result.data, expected_data) self.assertTrue(cubelist[0].dtype == np.float32) self.assertTrue(cubelist[1].dtype == np.int8) self.assertTrue(result.dtype == np.float32)
def test_broadcast_coord(self): """Test that plugin broadcasts to a coord and doesn't change the inputs.""" plugin = CubeCombiner("*") cube = self.cube4[:, 0, ...].copy() cube.data = np.ones_like(cube.data) cube.remove_coord("lwe_thickness_of_precipitation_amount") cubelist = iris.cube.CubeList([self.cube4.copy(), cube]) input_copy = deepcopy(cubelist) result = plugin.process(cubelist, "new_cube_name", broadcast_to_coords=["threshold"]) self.assertIsInstance(result, Cube) self.assertEqual(result.name(), "new_cube_name") self.assertArrayAlmostEqual(result.data, self.cube4.data) self.assertCubeListEqual(input_copy, cubelist)
def test_revised_coords(self): """Test that the plugin passes through the relevant dictionary to modify a coordinate and that these modifications are present in the returned cube.""" plugin = CubeCombiner('mean') revised_coords = { 'lwe_thickness_of_precipitation_amount': { 'points': [2.0] } } cubelist = iris.cube.CubeList([self.cube1, self.cube2]) result = plugin.process(cubelist, 'new_cube_name', revised_coords=revised_coords) self.assertEqual( result.coord('lwe_thickness_of_precipitation_amount').points[0], 2.0)
def test_warnings_on(self, warning_list=None): """Test that the plugin raises warnings and updates metadata. """ plugin = CubeCombiner('-', warnings_on=True) cubelist = iris.cube.CubeList([self.cube1, self.cube1]) attributes = {'relative_to_threshold': 'between'} warning_msg = "Adding or updating attribute" expected_data = np.zeros((1, 2, 2, 2)) expected_data[0, 0, :, :] = 0.0 expected_data[0, 1, :, :] = 0.0 result = plugin.process(cubelist, 'new_cube_name', revised_attributes=attributes) self.assertTrue( any(item.category == UserWarning for item in warning_list)) self.assertTrue(any(warning_msg in str(item) for item in warning_list)) self.assertEqual(result.name(), 'new_cube_name') self.assertArrayAlmostEqual(result.data, expected_data)