def test_exception_using_allowed_dt_difference(self): """Test that an exception is raised, if the time point is outside of the allowed difference specified in seconds.""" time_point = datetime(2017, 11, 23, 6, 0) msg = "is not available within the input cube" with self.assertRaisesRegex(ValueError, msg): extract_nearest_time_point(self.cube, time_point, allowed_dt_difference=3600)
def test_time_name_exception(self): """Test that an exception is raised, if an invalid time name is specified.""" time_point = datetime(2017, 11, 23, 6, 0) msg = "The time_name must be either " "'time' or 'forecast_reference_time'" with self.assertRaisesRegex(ValueError, msg): extract_nearest_time_point(self.cube, time_point, time_name="forecast_period")
def _select_orographic_enhancement_cube( precip_cube: Cube, oe_cube: Cube, allowed_time_diff: int = 1800) -> Cube: """Select the orographic enhancement cube with the required time coordinate. Args: precip_cube: Cube containing the input precipitation fields. oe_cube: Cube containing orographic enhancement fields at one or more times. allowed_time_diff: The maximum permitted difference, in integer seconds, between the datetime of the precipitation cube and the time points available within the orographic enhancement cube. If this limit is exceeded, then an error is raised. Returns: Cube containing the orographic enhancement field at the required time. Raises: ValueError: If required time step is not available within tolerance (in theory. In practise, the tolerance is left as the default None, which matches ANY available field regardless of time offset. So this error will never be thrown.) """ (time_point, ) = iris_time_to_datetime( precip_cube.coord("time").copy()) oe_cube_slice = extract_nearest_time_point( oe_cube, time_point, allowed_dt_difference=allowed_time_diff) return oe_cube_slice
def _select_orographic_enhancement_cube(precip_cube, oe_cube, allowed_time_diff=1800): """Select the orographic enhancement cube with the required time coordinate. Args: precip_cube (iris.cube.Cube): Cube containing the input precipitation fields. oe_cube (iris.cube.Cube): Cube containing orographic enhancement fields at one or more times. allowed_time_diff (int): The maximum permitted difference, in integer seconds, between the datetime of the precipitation cube and the time points available within the orographic enhancement cube. If this limit is exceeded, then an error is raised. Returns: iris.cube.Cube: Cube containing the orographic enhancement field at the required time. """ (time_point, ) = iris_time_to_datetime( precip_cube.coord("time").copy()) oe_cube_slice = extract_nearest_time_point( oe_cube, time_point, allowed_dt_difference=allowed_time_diff) return oe_cube_slice
def test_time_coord_upper_case(self): """Test that the nearest time point within the time coordinate is extracted, when a time of 07:31 is requested.""" expected = self.cube[:, 1, :, :] time_point = datetime.datetime(2015, 11, 23, 7, 31) result = extract_nearest_time_point(self.cube, time_point) self.assertEqual(result, expected)
def test_time_coord(self): """Test that the nearest time point within the time coordinate is extracted.""" expected = self.cube[:, 0, :, :] time_point = datetime.datetime(2015, 11, 23, 6, 0) result = extract_nearest_time_point(self.cube, time_point) self.assertEqual(result, expected)
def _select_orographic_enhancement_cube(precip_cube, oe_cubes, allowed_time_diff=1800): """Select the orographic enhancement cube with the required time coordinate. Args: precip_cube (iris.cube.Cube): Cube containing the input precipitation fields. oe_cubes (iris.cube.Cube or iris.cube.CubeList): Cube or CubeList containing the orographic enhancement fields. allowed_time_diff (int): An int in seconds to define a limit to the maximum difference between the datetime of the precipitation cube and the time points available within the orographic enhancement cube. If this limit is exceeded, then an error is raised. Returns: iris.cube.Cube: Cube containing the orographic enhancement fields at the required time. """ time_point, = iris_time_to_datetime(precip_cube.coord("time").copy()) oe_cube = extract_nearest_time_point( oe_cubes, time_point, allowed_dt_difference=allowed_time_diff) return oe_cube
def test_time_coord_lower_case(self): """Test that the nearest time point within the time coordinate is extracted, when a time of 07:30 is requested.""" expected = self.cube[:, 0, :, :] time_point = datetime(2015, 11, 23, 7, 30) result = extract_nearest_time_point(self.cube, time_point, allowed_dt_difference=1800) self.assertEqual(result, expected)
def test_time_coord(self): """Test that the nearest time point within the time coordinate is extracted.""" expected = self.cube[:, 0, :, :] time_point = datetime(2015, 11, 23, 6, 31) result = extract_nearest_time_point(self.cube, time_point, allowed_dt_difference=1800) self.assertEqual(result, expected)
def test_forecast_reference_time_coord(self): """Test that the nearest time point within the forecast_reference_time coordinate is extracted.""" expected = self.cube time_point = datetime.datetime(2015, 11, 23, 6, 0) result = extract_nearest_time_point( self.cube, time_point, time_name="forecast_reference_time") self.assertEqual(result, expected)
def test_forecast_reference_time_coord(self): """Test that the nearest time point within the forecast_reference_time coordinate is extracted.""" later_frt = self.cube.copy() later_frt.coord('forecast_reference_time').points = ( later_frt.coord('forecast_reference_time').points + 3600) cubes = iris.cube.CubeList([self.cube, later_frt]) cube = cubes.merge_cube() expected = self.cube time_point = datetime(2015, 11, 23, 3, 29) result = extract_nearest_time_point( cube, time_point, time_name="forecast_reference_time", allowed_dt_difference=1800) self.assertEqual(result, expected)
def _select_orographic_enhancement_cube(precip_cube, oe_cubes): """Select the orographic enhancement cube with the required time coordinate. Args: precip_cube (iris.cube.Cube): Cube containing the input precipitation fields. oe_cubes (iris.cube.Cube or iris.cube.CubeList): Cube or CubeList containing the orographic enhancement fields. Returns: oe_cube (iris.cube.Cube): Cube containing the orographic enhancement fields at the required time. """ time_point, = iris_time_to_datetime(precip_cube.coord("time").copy()) oe_cube = extract_nearest_time_point(oe_cubes, time_point) return oe_cube
def _modify_first_guess( self, cube: Cube, first_guess_lightning_cube: Cube, lightning_rate_cube: Cube, prob_precip_cube: Cube, prob_vii_cube: Optional[Cube] = None, ) -> Cube: """ Modify first-guess lightning probability with nowcast data. Args: cube: Provides the meta-data for the Nowcast lightning probability output cube. first_guess_lightning_cube: First-guess lightning probability. Must have same x & y dimensions as cube. Time dimension should overlap that of cube (closest slice in time is used with a maximum time mismatch of 2 hours). This is included to allow this cube to come from a different modelling system, such as the UM. lightning_rate_cube: Nowcast lightning rate. Must have same dimensions as cube. prob_precip_cube: Nowcast precipitation probability (threshold > 0.5, 7, 35). Must have same other dimensions as cube. prob_vii_cube: Radar-derived vertically integrated ice content (VII). Must have same x and y dimensions as cube. Time should be a scalar coordinate. Must have a threshold coordinate with points matching. self.vii_thresholds. Can be <No cube> or None or anything that evaluates to False. Returns: Output cube containing Nowcast lightning probability. Raises: iris.exceptions.ConstraintMismatchError: If lightning_rate_cube or first_guess_lightning_cube do not contain the expected times. """ new_cube_list = iris.cube.CubeList([]) # Loop over required forecast validity times for cube_slice in cube.slices_over("time"): this_time = iris_time_to_datetime( cube_slice.coord("time").copy())[0] lightning_rate_slice = lightning_rate_cube.extract( iris.Constraint(time=this_time)) err_string = "No matching {} cube for {}" if not isinstance(lightning_rate_slice, iris.cube.Cube): raise ConstraintMismatchError( err_string.format("lightning", this_time)) first_guess_slice = extract_nearest_time_point( first_guess_lightning_cube, this_time, allowed_dt_difference=7201) first_guess_slice = cube_slice.copy(data=first_guess_slice.data) first_guess_slice.coord("forecast_period").convert_units("minutes") fcmins = first_guess_slice.coord("forecast_period").points[0] # Increase prob(lightning) to Risk 2 (pl_dict[2]) when # lightning nearby (lrt_lev2) # (and leave unchanged when condition is not met): first_guess_slice.data = np.where( (lightning_rate_slice.data >= self.lrt_lev2) & (first_guess_slice.data < self.pl_dict[2]), self.pl_dict[2], first_guess_slice.data, ) # Increase prob(lightning) to Risk 1 (pl_dict[1]) when within # lightning storm (lrt_lev1): # (and leave unchanged when condition is not met): lratethresh = self.lrt_lev1(fcmins) first_guess_slice.data = np.where( (lightning_rate_slice.data >= lratethresh) & (first_guess_slice.data < self.pl_dict[1]), self.pl_dict[1], first_guess_slice.data, ) new_cube_list.append(first_guess_slice) new_prob_lightning_cube = new_cube_list.merge_cube() new_prob_lightning_cube = check_cube_coordinates( cube, new_prob_lightning_cube) # Apply precipitation adjustments. new_prob_lightning_cube = self.apply_precip(new_prob_lightning_cube, prob_precip_cube) # If we have VII data, increase prob(lightning) accordingly. if prob_vii_cube: new_prob_lightning_cube = self.apply_ice(new_prob_lightning_cube, prob_vii_cube) return new_prob_lightning_cube