class Test_process(IrisTest): """Tests the process method""" def setUp(self): """Create dummy cubes for tests""" self.plugin = ResolveWindComponents() wind_speed_data = np.array( [[6, 5, 4, 3], [8, 6, 4, 4], [12, 8, 6, 5]], dtype=np.float32 ) self.wind_speed_cube = set_up_cube(wind_speed_data, "wind_speed", "knots") wind_direction_data = np.array( [[138, 142, 141, 141], [141, 143, 140, 142], [142, 146, 141, 142]], dtype=np.float32, ) self.wind_direction_cube = set_up_cube( wind_direction_data, "wind_to_direction", "degrees" ) self.expected_u = np.array( [ [3.804214, 2.917800, 2.410297, 1.822455], [4.711193, 3.395639, 2.454748, 2.365005], [6.844465, 4.144803, 3.580219, 2.943424], ], dtype=np.float32, ) self.expected_v = np.array( [ [-4.639823, -4.060351, -3.1922507, -2.382994], [-6.465651, -4.946681, -3.1581972, -3.225949], [-9.856638, -6.842559, -4.8147717, -4.041813], ], dtype=np.float32, ) def test_basic(self): """Test plugin creates two output cubes with the correct metadata""" ucube, vcube = self.plugin.process( self.wind_speed_cube, self.wind_direction_cube ) for cube in ucube, vcube: self.assertIsInstance(cube, iris.cube.Cube) self.assertEqual(cube.units, self.wind_speed_cube.units) self.assertEqual(ucube.name(), "grid_eastward_wind") self.assertEqual(vcube.name(), "grid_northward_wind") def test_values(self): """Test plugin generates expected wind values""" ucube, vcube = self.plugin.process( self.wind_speed_cube, self.wind_direction_cube ) self.assertArrayAlmostEqual(ucube.data, self.expected_u, decimal=5) self.assertArrayAlmostEqual(vcube.data, self.expected_v, decimal=5) def test_coordinate_value_mismatch(self): """Test an error is raised if coordinate values are different for wind speed and direction cubes""" self.wind_direction_cube.coord(axis="y").convert_units("km") msg = "Wind speed and direction cubes have unmatched coordinates" with self.assertRaisesRegex(ValueError, msg): _, _ = self.plugin.process(self.wind_speed_cube, self.wind_direction_cube) def test_projection_mismatch(self): """Test an error is raised if coordinate names are different for wind speed and direction cubes""" self.wind_speed_cube.coord(axis="x").rename("longitude") self.wind_speed_cube.coord(axis="y").rename("latitude") msg = "Wind speed and direction cubes have unmatched coordinates" with self.assertRaisesRegex(ValueError, msg): _, _ = self.plugin.process(self.wind_speed_cube, self.wind_direction_cube) def test_height_levels(self): """Test a cube on more than one height level is correctly processed""" wind_speed_3d = add_new_dimension(self.wind_speed_cube, 3, "height", "km") wind_direction_3d = add_new_dimension( self.wind_direction_cube, 3, "height", "km" ) ucube, vcube = self.plugin.process(wind_speed_3d, wind_direction_3d) self.assertSequenceEqual(ucube.shape, (3, 3, 4)) self.assertArrayAlmostEqual(ucube[1].data, self.expected_u, decimal=5) self.assertArrayAlmostEqual(vcube[2].data, self.expected_v, decimal=5) def test_wind_from_direction(self): """Test correct behaviour when wind direction is 'from' not 'to'. We do not get perfect direction inversion to the 7th decimal place here because we ignore imprecision in the iris rotate_winds calcuation near the corners of the domain, and regrid the available data linearly to fill the gap. The output wind speeds (in m s-1) compare equal to the 5th decimal place. """ expected_u = -1.0 * self.expected_u expected_v = -1.0 * self.expected_v self.wind_direction_cube.rename("wind_from_direction") ucube, vcube = self.plugin.process( self.wind_speed_cube, self.wind_direction_cube ) self.assertArrayAllClose(ucube.data, expected_u, atol=1e-5) self.assertArrayAllClose(vcube.data, expected_v, atol=1e-5)
class Test_process(IrisTest): """Tests the process method""" def setUp(self): """Create dummy cubes for tests""" self.plugin = ResolveWindComponents() wind_speed_data = np.array([[6, 5, 4, 3], [8, 6, 4, 4], [12, 8, 6, 5]], dtype=np.float32) self.wind_speed_cube = set_up_cube(wind_speed_data, "wind_speed", "knots") wind_direction_data = np.array( [[138, 142, 141, 141], [141, 143, 140, 142], [142, 146, 141, 142]], dtype=np.float32) self.wind_direction_cube = set_up_cube(wind_direction_data, "wind_to_direction", "degrees") self.expected_u = np.array([[3.774919, 2.902824, 2.406847, 1.826062], [4.683339, 3.386423, 2.456537, 2.374629], [6.829466, 4.149148, 3.593584, 2.963243]], dtype=np.float32) self.expected_v = np.array( [[-4.663688, -4.071071, -3.194854, -2.380230], [-6.485857, -4.952993, -3.156806, -3.218872], [-9.867035, -6.839924, -4.804805, -4.027306]], dtype=np.float32) def test_basic(self): """Test plugin creates two output cubes with the correct metadata""" ucube, vcube = self.plugin.process(self.wind_speed_cube, self.wind_direction_cube) for cube in ucube, vcube: self.assertIsInstance(cube, iris.cube.Cube) self.assertEqual(cube.units, self.wind_speed_cube.units) self.assertEqual(ucube.name(), "grid_eastward_wind") self.assertEqual(vcube.name(), "grid_northward_wind") def test_values(self): """Test plugin generates expected wind values""" ucube, vcube = self.plugin.process(self.wind_speed_cube, self.wind_direction_cube) self.assertArrayAlmostEqual(ucube.data, self.expected_u) self.assertArrayAlmostEqual(vcube.data, self.expected_v) def test_coordinate_value_mismatch(self): """Test an error is raised if coordinate values are different for wind speed and direction cubes""" self.wind_direction_cube.coord(axis='y').convert_units("km") msg = 'Wind speed and direction cubes have unmatched coordinates' with self.assertRaisesRegex(ValueError, msg): _, _ = self.plugin.process(self.wind_speed_cube, self.wind_direction_cube) def test_projection_mismatch(self): """Test an error is raised if coordinate names are different for wind speed and direction cubes""" self.wind_speed_cube.coord(axis='x').rename('longitude') self.wind_speed_cube.coord(axis='y').rename('latitude') msg = 'Wind speed and direction cubes have unmatched coordinates' with self.assertRaisesRegex(ValueError, msg): _, _ = self.plugin.process(self.wind_speed_cube, self.wind_direction_cube) def test_height_levels(self): """Test a cube on more than one height level is correctly processed""" wind_speed_3d = add_new_dimension(self.wind_speed_cube, 3, "height", "km") wind_direction_3d = add_new_dimension(self.wind_direction_cube, 3, "height", "km") ucube, vcube = self.plugin.process(wind_speed_3d, wind_direction_3d) self.assertSequenceEqual(ucube.shape, (3, 3, 4)) self.assertArrayAlmostEqual(ucube[1].data, self.expected_u) self.assertArrayAlmostEqual(vcube[2].data, self.expected_v) def test_wind_from_direction(self): """Test correct behaviour when wind direction is 'from' not 'to'. We do not get perfect direction inversion to the 7th decimal place here because we ignore imprecision in the iris rotate_winds calcuation near the corners of the domain, and regrid the available data linearly to fill the gap. The output wind speeds (in m s-1) compare equal to the 5th decimal place. """ expected_u = -1. * self.expected_u expected_v = -1. * self.expected_v self.wind_direction_cube.rename("wind_from_direction") ucube, vcube = self.plugin.process(self.wind_speed_cube, self.wind_direction_cube) self.assertTrue(np.allclose(ucube.data, expected_u, atol=1e-6)) self.assertTrue(np.allclose(vcube.data, expected_v, atol=1e-6))