def test_returns_correct_type(self): """Test function returns the expected list.""" time_interval = 60 plugin = Accumulation(accumulation_period=120) result = plugin._get_period_sets(time_interval, self.cubes) self.assertIsInstance(result, list)
def test_returns_cubelist(self): """Test function returns a cubelist.""" plugin = Accumulation(accumulation_period=60, forecast_periods=self.forecast_periods) result = plugin.process(self.cubes) self.assertIsInstance(result, iris.cube.CubeList)
def test_returns_expected_values_1_minute(self): """Test function returns the expected accumulations over a 1 minute aggregation period. Check that the number of accumulation cubes returned is the expected number.""" expected_t0 = np.array( [[0.015, 0.03, 0.03, 0.03, 0.03, 0.06, 0.09, 0.09, 0.09, 0.09], [0.015, 0.03, 0.03, 0.03, 0.03, np.nan, np.nan, 0.09, 0.09, 0.09], [0., 0., 0., 0., 0., np.nan, np.nan, 0.09, 0.09, 0.09], [0., 0., 0., 0., 0., 0.045, 0.09, 0.09, 0.09, 0.09]]) expected_t7 = np.array( [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.015, 0.03, 0.03], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.015, 0.03, 0.03], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]) expected_mask_t0 = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) expected_mask_t7 = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) plugin = Accumulation(accumulation_period=60, accumulation_units='mm') result = plugin.process(self.cubes) self.assertArrayAlmostEqual(result[0].data, expected_t0) self.assertArrayAlmostEqual(result[7].data, expected_t7) self.assertArrayAlmostEqual(result[0].data.mask, expected_mask_t0) self.assertArrayAlmostEqual(result[7].data.mask, expected_mask_t7) self.assertEqual(len(result), 10)
def test_returns_total_accumulation_if_no_period_specified(self): """Test function returns a list containing a single accumulation cube that is the accumulation over the whole period specified by the rates cubes. The results are the same as the 10 minute test above as that is the total span of the input rates cubes. Check that the number of accumulation cubes returned is the expected number.""" expected_t0 = np.array( [[ 0.015, 0.045, 0.075, 0.105, 0.135, 0.195, 0.285, 0.375, 0.465, 0.555 ], [ 0.015, 0.045, 0.075, 0.105, 0.135, np.nan, np.nan, np.nan, np.nan, np.nan ], [0., 0., 0., 0., 0., np.nan, np.nan, np.nan, np.nan, np.nan], [0., 0., 0., 0., 0., 0.045, 0.135, 0.225, 0.315, 0.405]]) expected_mask_t0 = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) plugin = Accumulation(accumulation_units='mm') result = plugin.process(self.cubes) self.assertArrayAlmostEqual(result[0].data, expected_t0) self.assertArrayAlmostEqual(result[0].data.mask, expected_mask_t0) self.assertEqual(len(result), 1)
def test_returns_expected_values_10_minutes(self): """Test function returns the expected accumulations over the complete 10 minute aggregation period. These are written out long hand to make the comparison easy. Note that the test have been constructed such that only the top row is expected to show a difference by including the last 5 minutes of the accumulation, all the other results are the same as for the 5 minute test above. Check that the number of accumulation cubes returned is the expected number.""" expected_t0 = np.array( [[ 0.015, 0.045, 0.075, 0.105, 0.135, 0.195, 0.285, 0.375, 0.465, 0.555 ], [ 0.015, 0.045, 0.075, 0.105, 0.135, np.nan, np.nan, np.nan, np.nan, np.nan ], [0., 0., 0., 0., 0., np.nan, np.nan, np.nan, np.nan, np.nan], [0., 0., 0., 0., 0., 0.045, 0.135, 0.225, 0.315, 0.405]]) expected_mask_t0 = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) plugin = Accumulation(accumulation_period=600, accumulation_units='mm', forecast_periods=[600]) result = plugin.process(self.cubes) self.assertArrayAlmostEqual(result[0].data, expected_t0) self.assertArrayAlmostEqual(result[0].data.mask, expected_mask_t0) self.assertEqual(len(result), 1)
def test_raises_warning_for_unused_cubes(self, warning_list=None): """Test function raises a warning when there are insufficient cubes to complete the last period. In this test the accumulation period is 3 minutes, but the total span of rates cubes covers 10 minutes, resulting in 3 complete periods and a final incomplete period that is not returned. This test checks that a warning is raised to highlight that there is an incomplete final period that is not returned.""" time_interval = 60 warning_msg = ( "The provided cubes result in a partial period given the specified" " accumulation_period, i.e. the number of cubes is insufficient to" " give a set of complete periods. Only complete periods will be" " returned.") expected = [self.cubes[0:4], self.cubes[3:7], self.cubes[6:10]] plugin = Accumulation(accumulation_period=180) result = plugin._get_period_sets(time_interval, self.cubes) for index, sublist in enumerate(result): self.assertSequenceEqual(sublist, expected[index]) self.assertTrue( any(item.category == UserWarning for item in warning_list)) self.assertTrue(any(warning_msg in str(item) for item in warning_list))
def test_returns_all_cubes_if_period_unspecified(self): """Test function returns a list containing the original cube list if the accumulation_period is not set.""" time_interval = 60 plugin = Accumulation() result = plugin._get_period_sets(time_interval, self.cubes) self.assertSequenceEqual(result, [self.cubes])
def test_returns_expected_values_5_minutes(self): """Test function returns the expected accumulations over a 5 minute aggregation period. These are written out long hand to make the comparison easy. Check that the number of accumulation cubes returned is the expected number.""" expected_t0 = np.array([ [0.015, 0.045, 0.075, 0.105, 0.135, 0.18, 0.24, 0.3, 0.36, 0.42], [ 0.015, 0.045, 0.075, 0.105, 0.135, np.nan, np.nan, np.nan, np.nan, np.nan, ], [0.0, 0.0, 0.0, 0.0, 0.0, np.nan, np.nan, np.nan, np.nan, np.nan], [0.0, 0.0, 0.0, 0.0, 0.0, 0.045, 0.135, 0.225, 0.315, 0.405], ]) expected_t1 = np.array([ [0.0, 0.0, 0.0, 0.0, 0.0, 0.015, 0.045, 0.075, 0.105, 0.135], [0.0, 0.0, 0.0, 0.0, 0.0, 0.015, 0.045, 0.075, 0.105, 0.135], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], ]) expected_mask_t0 = np.array([ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ]) expected_mask_t1 = np.array([ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ]) plugin = Accumulation( accumulation_period=300, accumulation_units="mm", forecast_periods=[300, 600], ) result = plugin.process(self.cubes) self.assertArrayAlmostEqual(result[0].data, expected_t0) self.assertArrayAlmostEqual(result[1].data, expected_t1) self.assertArrayAlmostEqual(result[0].data.mask, expected_mask_t0) self.assertArrayAlmostEqual(result[1].data.mask, expected_mask_t1) self.assertEqual(len(result), 2)
def test_raises_exception_for_impossible_aggregation(self): """Test function raises an exception when attempting to create an accumulation_period that cannot be created from the input cubes.""" plugin = Accumulation(accumulation_period=119) msg = "The specified accumulation period " with self.assertRaisesRegex(ValueError, msg): plugin._check_inputs(self.cubes)
def test_basic(self): """Test that the subset of cubes that are within the accumulation period are correctly identified. In this case, the subset of cubes used for each accumulation period is expected to consist of 6 cubes.""" expected_cube_subset = self.cubes[:6] upper_bound_fp, = self.cubes[5].coord("forecast_period").points plugin = Accumulation(accumulation_period=5 * 60, forecast_periods=np.array([5]) * 60) result = plugin._get_cube_subsets(self.cubes, upper_bound_fp) self.assertEqual(expected_cube_subset, result)
def test_does_not_use_incomplete_period_data(self): """Test function returns only 2 accumulation periods when a 4 minute aggregation period is used with 10 minutes of input data. The trailing 2 cubes are insufficient to create another period and so are discarded. A warning is raised by the chunking function and has been tested above, so is ignored here. """ plugin = Accumulation(accumulation_period=240, forecast_periods=[240, 480]) result = plugin.process(self.cubes) self.assertEqual(len(result), 2)
def test_accumulation_length(self): """Test to check that the length of the accumulation period is consistent across all output cubes. Only complete periods are required.""" accumulation_length = 120 plugin = Accumulation(accumulation_period=accumulation_length, forecast_periods=self.forecast_periods) result = plugin.process(self.cubes) for cube in result: self.assertEqual(np.diff(cube.coord("forecast_period").bounds), accumulation_length)
def test_default_altered_output_units(self): """Test the function returns accumulations in the specified units if they are explicitly set. Here the units are set to mm.""" # Multiply the rates in mm/s by 60 to get accumulation over 1 minute expected = self.cubes[0].copy( data=(0.5 * (self.cubes[0].data + self.cubes[1].data) * 60)) plugin = Accumulation(accumulation_units='mm', accumulation_period=60) result = plugin.process(self.cubes) self.assertEqual(result[0].units, 'mm') self.assertArrayAlmostEqual(result[0].data, expected.data)
def test_raises_exception_for_unevenly_spaced_cubes(self): """Test function raises an exception if the input cubes are not spaced equally in time.""" last_time = self.cubes[-1].coord('time').points self.cubes[-1].coord('time').points = last_time + 60 msg = ("Accumulation is designed to work with rates " "cubes at regular time intervals.") plugin = Accumulation(accumulation_period=120) with self.assertRaisesRegex(ValueError, msg): plugin.process(self.cubes)
def test_raises_exception_for_small_accumulation_period(self): """Test that if the forecast period of the upper bound cube is not within the list of requested forecast periods, then the subset of cubes returned is equal to None.""" msg = ("The accumulation_period is less than the time interval " "between the rates cubes. The rates cubes provided are " "therefore insufficient for computing the accumulation period " "requested.") reduced_cubelist = iris.cube.CubeList([self.cubes[0], self.cubes[-1]]) plugin = Accumulation(accumulation_period=5 * 60, forecast_periods=np.array([5]) * 60) with self.assertRaisesRegex(ValueError, msg): plugin.process(reduced_cubelist)
def test_specify_accumulation_period(self): """Test that the expected time interval is returned when the accumulation period is specified. Also test that the returned list of cubes has the expected units.""" expected_time_interval = 60 expected_cubes = self.cubes.copy() for cube in expected_cubes: cube.convert_units("m/s") accumulation_period = 60 * 60 plugin = Accumulation(accumulation_period=accumulation_period) cubes, time_interval = plugin._check_inputs(self.cubes) self.assertEqual(cubes, expected_cubes) self.assertEqual(time_interval, expected_time_interval) self.assertEqual(plugin.accumulation_period, accumulation_period)
def test_specify_forecast_period(self): """Test that the expected time interval is returned when the forecast periods are specified. Also test that the returned list of cubes has the expected units.""" expected_time_interval = 60 expected_cubes = self.cubes.copy() for cube in expected_cubes: cube.convert_units("m/s") forecast_periods = [600] plugin = Accumulation(forecast_periods=forecast_periods) cubes, time_interval = plugin._check_inputs(self.cubes) self.assertEqual(cubes, expected_cubes) self.assertEqual(time_interval, expected_time_interval) self.assertEqual(plugin.forecast_periods, forecast_periods)
def test_default_output_units(self): """Test the function returns accumulations in the default units if no units are explicitly set, where the default is metres.""" # Multiply the rates in mm/s by 60 to get accumulation over 1 minute # and divide by 1000 to get into metres. expected = self.cubes[0].copy( data=(0.5 * (self.cubes[0].data + self.cubes[1].data) * 60 / 1000)) plugin = Accumulation(accumulation_period=60) result = plugin.process(self.cubes) self.assertEqual(result[0].units, 'm') self.assertArrayAlmostEqual(result[0].data, expected.data)
def test_returns_expected_cubes(self, warning_list=None): """Test function returns lists containing the expected cubes for each period. In this test all the cubes are used as the total time span of precipitation rates cubes is divisible by the requested accumulation period.""" time_interval = 60 expected = [self.cubes[0:6], self.cubes[5:]] plugin = Accumulation(accumulation_period=300) result = plugin._get_period_sets(time_interval, self.cubes) for index, sublist in enumerate(result): self.assertSequenceEqual(sublist, expected[index])
def test_specify_accumulation_period_and_forecast_period(self): """Test that the expected time interval is returned when the accumulation period and forecast periods are specified. Also test that the returned list of cubes has the expected units.""" expected_time_interval = 60 expected_cubes = self.cubes.copy() for cube in expected_cubes: cube.convert_units("m/s") accumulation_period = 20 * 60 forecast_periods = np.array([15]) * 60 plugin = Accumulation(accumulation_period=accumulation_period, forecast_periods=forecast_periods) cubes, time_interval = plugin._check_inputs(self.cubes) self.assertEqual(cubes, expected_cubes) self.assertEqual(time_interval, expected_time_interval)
def test_basic(self): """Test string representation""" result = str( Accumulation(accumulation_units="cm", accumulation_period=60)) expected_result = ("<Accumulation: accumulation_units=cm, " "accumulation_period=60s>") self.assertEqual(result, expected_result)
def test_basic(self): """Check the calculations of the accumulations, where an accumulation is computed by finding the mean rate between each adjacent pair of cubes within the cube_subset and multiplying this mean rate by the time_interval, in order to compute an accumulation. In this case, as the cube_subset only contains a pair of cubes, then the accumulation from this pair will be the same as the total accumulation. """ expected_t0 = np.array( [ [0.015, 0.03, 0.03, 0.03, 0.03, 0.06, 0.09, 0.09, 0.09, 0.09], [0.015, 0.03, 0.03, 0.03, 0.03, np.nan, np.nan, 0.09, 0.09, 0.09], [0.0, 0.0, 0.0, 0.0, 0.0, np.nan, np.nan, 0.09, 0.09, 0.09], [0.0, 0.0, 0.0, 0.0, 0.0, 0.045, 0.09, 0.09, 0.09, 0.09], ] ) expected_mask_t0 = np.array( [ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ] ) time_interval = 60 result = Accumulation()._calculate_accumulation(self.cubes[:2], time_interval) self.assertArrayAlmostEqual(result, expected_t0) self.assertArrayAlmostEqual(result.mask, expected_mask_t0)
def test_basic(self): """Test that the expected time_interval is returned and that the returned list of cubes has the expected units.""" expected_time_interval = 60 expected_cubes = self.cubes.copy() for cube in expected_cubes: cube.convert_units("m/s") cubes, time_interval = Accumulation()._check_inputs(self.cubes) self.assertEqual(cubes, expected_cubes) self.assertEqual(time_interval, expected_time_interval)
def test_times(self): """Test function returns the correct times for the sorted cubes.""" expected = [ 1510286400, 1510286460, 1510286520, 1510286580, 1510286640, 1510286700, 1510286760, 1510286820, 1510286880, 1510286940, 1510287000 ] _, times = Accumulation.sort_cubes_by_time(self.cubes) self.assertArrayEqual(times, expected)
def test_basic(self): """Check that the metadata is set as expected.""" expected_name = "lwe_thickness_of_precipitation_amount" expected_units = Unit("m") expected_time_point = [datetime.datetime(2017, 11, 10, 4, 10)] expected_time_bounds = [ ( datetime.datetime(2017, 11, 10, 4, 0), datetime.datetime(2017, 11, 10, 4, 10), ) ] expected_fp_point = 600 expected_fp_bounds = [[0, 600]] expected_cell_method = iris.coords.CellMethod("sum", coords="time") result = Accumulation()._set_metadata(self.cubes) self.assertEqual(result.name(), expected_name) self.assertEqual(result.units, expected_units) points = [value.point for value in result.coord("time").cells()] bounds = [value.bound for value in result.coord("time").cells()] self.assertEqual(points, expected_time_point) self.assertArrayAlmostEqual( result.coord("forecast_period").points, expected_fp_point ) self.assertEqual(bounds, expected_time_bounds) self.assertArrayAlmostEqual( result.coord("forecast_period").bounds, expected_fp_bounds ) self.assertEqual(result.cell_methods[0], expected_cell_method)
def test_reorders(self): """Test function reorders a cubelist that is not time ordered.""" expected = [cube.coord("time").points[0] for cube in self.cubes] self.cubes = self.cubes[::-1] reordered = [cube.coord("time").points[0] for cube in self.cubes] result, _ = Accumulation.sort_cubes_by_time(self.cubes) result_times = [cube.coord("time").points[0] for cube in result] self.assertIsInstance(result, iris.cube.CubeList) self.assertEqual(result_times, expected) self.assertNotEqual(result_times, reordered)
def test_returns_masked_cubes(self): """Test function returns a list of masked cubes for masked input data.""" result = Accumulation(forecast_periods=[600]).process(self.cubes) self.assertIsInstance(result[0].data, np.ma.MaskedArray)
def test_accumulation_period_set(self): """Test the accumulation_period is set when specified.""" plugin = Accumulation(accumulation_period=180) self.assertEqual(plugin.accumulation_period, 180)
def test_forecast_period_set(self): """Test the forecast_period is set when specified.""" plugin = Accumulation(forecast_periods=[60, 120]) self.assertListEqual(plugin.forecast_periods, [60, 120])
def test_returns_cubelist(self): """Test function returns a cubelist.""" result, _ = Accumulation.sort_cubes_by_time(self.cubes) self.assertIsInstance(result, iris.cube.CubeList)