Пример #1
0
 def test_check_expected_values_add(self):
     """Test the expected values are returned when cubes are added.
     First check."""
     expected = np.array([[[0.0, 1.0, 2.0], [1.0, 2.0, 7.0],
                           [0.0, 3.0, 4.0]]])
     plugin = ApplyOrographicEnhancement("add")
     result = plugin._apply_orographic_enhancement(self.precip_cube,
                                                   self.sliced_oe_cube)
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertEqual(result.metadata, self.precip_cube.metadata)
     result.convert_units("mm/hr")
     self.assertArrayAlmostEqual(result.data, expected)
Пример #2
0
 def test_check_expected_values_subtract(self):
     """Test the expected values are returned when one cube is subtracted
     from another."""
     expected = np.array([[[0.0, 1.0, 2.0], [1.0, 2.0, -1.0],
                           [0.0, 1.0, 0.0]]])
     plugin = ApplyOrographicEnhancement("subtract")
     result = plugin._apply_orographic_enhancement(self.precip_cube,
                                                   self.sliced_oe_cube)
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertEqual(result.metadata, self.precip_cube.metadata)
     result.convert_units("mm/hr")
     self.assertArrayAlmostEqual(result.data, expected)
Пример #3
0
 def test_alternative_time_quarter_past(self):
     """Test extracting a time coordinate from the orographic enhancement
     cube at quarter past an hour."""
     plugin = ApplyOrographicEnhancement("add")
     self.precip_cube.coord("time").points = (
         self.precip_cube.coord("time").points + 15 * 60)  # add 15 mins
     result = plugin._select_orographic_enhancement_cube(
         self.precip_cube, self.oe_cube)
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertEqual(result.metadata, self.first_slice.metadata)
     self.assertEqual(result, self.first_slice)
     self.assertEqual(result.coord("time"), self.first_slice.coord("time"))
Пример #4
0
 def test_one_input_cube(self):
     """Test the addition of precipitation rate and orographic enhancement,
     where a single precipitation rate cube is provided."""
     expected = np.array([[[0., 1., 2.], [1., 2., 7.], [0., 3., 4.]]])
     plugin = ApplyOrographicEnhancement("add")
     result = plugin.process(self.precip_cubes[0], self.oe_cube)
     self.assertIsInstance(result, iris.cube.CubeList)
     for aresult, precip_cube in zip(result, self.precip_cubes):
         self.assertEqual(aresult.metadata, precip_cube.metadata)
     for cube in result:
         cube.convert_units("mm/hr")
     self.assertArrayAlmostEqual(result[0].data, expected)
 def test_alternative_time_half_past(self):
     """Test extracting a time coordinate from the orographic enhancement
     cube at half past an hour. Note that the time point will round down
     at the midpoint between 412227. and 412228."""
     plugin = ApplyOrographicEnhancement("add")
     self.precip_cubes[0].coord("time").points = 412227.5
     result = plugin._select_orographic_enhancement_cube(
         self.precip_cubes[0], self.oe_cube)
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertEqual(result.metadata, self.first_slice.metadata)
     self.assertEqual(result, self.first_slice)
     self.assertEqual(result.coord("time"), self.first_slice.coord("time"))
Пример #6
0
 def test_check_expected_values_for_different_units(self):
     """Test the expected values are returned when cubes are combined when
     the orographic enhancement cube is in different units to the
     precipitation rate cube."""
     expected = np.array([[[0.0, 1.0, 2.0], [1.0, 2.0, 7.0], [0.0, 3.0, 4.0]]])
     oe_cube = self.sliced_oe_cube
     oe_cube.convert_units("m/hr")
     plugin = ApplyOrographicEnhancement("add")
     result = plugin._apply_orographic_enhancement(self.precip_cube, oe_cube)
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertEqual(result.metadata, self.precip_cube.metadata)
     result.convert_units("mm/hr")
     self.assertArrayAlmostEqual(result.data, expected)
Пример #7
0
 def test_basic_add(self):
     """Test a minimum precipitation rate is applied, when the orographic
     enhancement causes the precipitation rate to become negative"""
     expected = np.array([[[0., 1., 2.], [1., 2., 7.], [0., 3., 4.]]])
     precip_cube = self.precip_cube.copy()
     added_cube = self.added_cube.copy()
     plugin = ApplyOrographicEnhancement("add")
     result = plugin._apply_minimum_precip_rate(precip_cube, added_cube)
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertEqual(result.units, Unit("m/s"))
     self.assertEqual(result.metadata, added_cube.metadata)
     result.convert_units("mm/hr")
     self.assertArrayAlmostEqual(result.data, expected)
Пример #8
0
 def test_basic_add(self):
     """Test the addition of a precipitation rate cubelist and an
     orographic enhancement cube with multiple times."""
     expected0 = np.array([[[0., 1., 2.], [1., 2., 7.], [0., 3., 4.]]])
     expected1 = np.array([[[9., 9., 6.], [6., 5., 1.], [6., 5., 1.]]])
     plugin = ApplyOrographicEnhancement("add")
     result = plugin.process(self.precip_cubes, self.oe_cube)
     self.assertIsInstance(result, iris.cube.CubeList)
     for aresult, precip_cube in zip(result, self.precip_cubes):
         self.assertEqual(aresult.metadata, precip_cube.metadata)
     for cube in result:
         cube.convert_units("mm/hr")
     self.assertArrayAlmostEqual(result[0].data, expected0)
     self.assertArrayAlmostEqual(result[1].data, expected1)
Пример #9
0
    def __init__(self,
                 input_cube,
                 vel_x,
                 vel_y,
                 orographic_enhancement_cube=None,
                 metadata_dict=None):
        """
        Initialises the object.
        This includes checking if orographic enhancement is provided and
        removing the orographic enhancement from the input file ready for
        extrapolation.
        An error is raised if the input cube is precipitation rate but no
        orographic enhancement cube is provided.

        Args:
            input_cube (iris.cube.Cube):
                A 2D cube containing data to be advected.
            vel_x (iris.cube.Cube):
                Cube containing a 2D array of velocities along the x
                coordinate axis
            vel_y (iris.cube.Cube):
                Cube containing a 2D array of velocities along the y
                coordinate axis

        Keyword Args:
            orographic_enhancement_cube (iris.cube.Cube):
                Cube containing the orographic enhancement fields. May have
                data for multiple times in the cube. The orographic enhancement
                is removed from the input_cube before advecting, and added
                back on after advection.
            metadata_dict (dict):
                Dictionary containing information for amending the metadata
                of the output cube. Please see the
                :func:`improver.utilities.cube_metadata.amend_metadata`
                for information regarding the allowed contents of the metadata
                dictionary.
        """
        self.orographic_enhancement_cube = orographic_enhancement_cube
        if self.orographic_enhancement_cube:
            input_cube, = ApplyOrographicEnhancement("subtract").process(
                input_cube, self.orographic_enhancement_cube)
        elif "precipitation_rate" in input_cube.name():
            msg = ("For precipitation fields, orographic enhancement "
                   "cube must be supplied.")
            raise ValueError(msg)
        self.input_cube = input_cube
        self.advection_plugin = AdvectField(vel_x,
                                            vel_y,
                                            metadata_dict=metadata_dict)
Пример #10
0
    def __init__(self,
                 input_cube,
                 vel_x,
                 vel_y,
                 orographic_enhancement_cube=None,
                 attributes_dict=None):
        """
        Initialises the object.
        This includes checking if orographic enhancement is provided and
        removing the orographic enhancement from the input file ready for
        extrapolation.
        An error is raised if the input cube is precipitation rate but no
        orographic enhancement cube is provided.

        Args:
            input_cube (iris.cube.Cube):
                A 2D cube containing data to be advected.
            vel_x (iris.cube.Cube):
                Cube containing a 2D array of velocities along the x
                coordinate axis
            vel_y (iris.cube.Cube):
                Cube containing a 2D array of velocities along the y
                coordinate axis
            orographic_enhancement_cube (iris.cube.Cube):
                Cube containing the orographic enhancement fields. May have
                data for multiple times in the cube. The orographic enhancement
                is removed from the input_cube before advecting, and added
                back on after advection.
            attributes_dict (dict):
                Dictionary containing information for amending the attributes
                of the output cube.
        """
        if not (vel_x and vel_y):
            raise TypeError("Neither x velocity or y velocity can be None")

        self.orographic_enhancement_cube = orographic_enhancement_cube
        if self.orographic_enhancement_cube:
            input_cube, = ApplyOrographicEnhancement("subtract")(
                input_cube, self.orographic_enhancement_cube)
        elif ("precipitation_rate" in input_cube.name()
              or "rainfall_rate" in input_cube.name()):
            msg = ("For precipitation or rainfall fields, orographic "
                   "enhancement cube must be supplied.")
            raise ValueError(msg)
        self.input_cube = input_cube
        self.advection_plugin = AdvectField(vel_x,
                                            vel_y,
                                            attributes_dict=attributes_dict)
Пример #11
0
    def _generate_forecast_cubes(
            self, all_forecasts: ndarray,
            attributes_dict: Optional[Dict]) -> List[Cube]:
        """
        Convert forecast arrays into IMPROVER output cubes with re-added
        orographic enhancement

        Args:
            all_forecasts:
                Array of 2D forecast fields returned by extrapolation function
            attributes_dict:
                Dictionary containing information for amending the attributes
                of the output cube.

        Returns:
            List of iris.cube.Cube instances containing forecasts at all
            required lead times, and conforming to the IMPROVER metadata
            standard.
        """
        # re-mask forecast data
        all_forecasts = np.ma.masked_invalid(all_forecasts)

        # put forecast data arrays into cubes
        self._reformat_analysis_cube(attributes_dict)
        timestamped_cubes = self._set_up_output_cubes(all_forecasts)

        # re-convert cubes to original units and add orographic enhancement
        forecast_cubes = []
        for cube in timestamped_cubes:
            cube.convert_units(self.required_units)
            if self.orogenh:
                (cube, ) = ApplyOrographicEnhancement("add").process(
                    cube, self.orogenh)
            forecast_cubes.append(cube)
        return forecast_cubes
Пример #12
0
    def extrapolate(self, leadtime_minutes):
        """
        Produce a new forecast cube for the supplied lead time. Creates a new
        advected forecast and then reapplies the orographic enhancement if it
        is supplied.

        Args:
            leadtime_minutes (float):
                The forecast leadtime we want to generate a forecast for
                in minutes.

        Returns:
            iris.cube.Cube:
                New cube with updated time and extrapolated data.  New data
                are filled with np.nan and masked where source data were
                out of bounds (ie where data could not be advected from outside
                the cube domain).

        Raises:
            ValueError: If no leadtime_minutes are provided.
        """
        # cast to float as datetime.timedelta cannot accept np.int
        timestep = datetime.timedelta(minutes=float(leadtime_minutes))
        forecast_cube = self.advection_plugin(self.input_cube, timestep)
        if self.orographic_enhancement_cube:
            # Add orographic enhancement.
            (forecast_cube,) = ApplyOrographicEnhancement("add")(
                forecast_cube, self.orographic_enhancement_cube
            )

        return forecast_cube
Пример #13
0
 def test_no_unit_conversion(self):
     """Test that the minimum precipitation rate is applied correctly,
     when the units of the input cube do not require conversion to mm/hr."""
     expected = np.array([[[0., 1., 2.], [1., 2., MIN_PRECIP_RATE_MMH],
                           [0., 1., MIN_PRECIP_RATE_MMH]]])
     precip_cube = self.precip_cube.copy()
     subtracted_cube = self.subtracted_cube.copy()
     precip_cube.convert_units("mm/hr")
     subtracted_cube.convert_units("mm/hr")
     plugin = ApplyOrographicEnhancement("subtract")
     result = (plugin._apply_minimum_precip_rate(precip_cube,
                                                 subtracted_cube))
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertEqual(result.units, Unit("mm/hr"))
     self.assertEqual(result.metadata, subtracted_cube.metadata)
     self.assertArrayAlmostEqual(result.data, expected)
Пример #14
0
def process(original_cube_list,
            orographic_enhancement_cube=None,
            attributes_dict=None,
            ofc_box_size=30,
            smart_smoothing_iterations=100):
    """Calculate optical flow components from input fields.

    Args:
        original_cube_list (iris.cube.CubeList):
            Cubelist from which to calculate optical flow velocities.
            The cubes require a 'time' coordinate on which they are sorted,
            so the order of cubes does not matter.
        orographic_enhancement_cube (iris.cube.Cube):
            Cube containing the orographic enhancement fields.
            Default is None.
        attributes_dict (dict):
            Dictionary containing required changes to the attributes.
            Every output file will have the attributes_dict applied.
            Default is None.
        ofc_box_size (int):
            Size of square 'box' (in grid spaces) within which to solve
            the optical flow equations.
            Default is 30.
        smart_smoothing_iterations (int):
            Number of iterations to perform in enforcing smoothness constraint
            for optical flow velocities.
            Default is 100.

    Returns:
        iris.cube.CubeList:
            List of the umean and vmean cubes.

    Raises:
        ValueError:
            If there is no oe_cube but a cube is called 'precipitation_rate'.

    """
    # order input files by validity time
    original_cube_list.sort(key=lambda x: x.coord("time").points[0])

    # subtract orographic enhancement
    if orographic_enhancement_cube:
        cube_list = ApplyOrographicEnhancement("subtract").process(
            original_cube_list, orographic_enhancement_cube)
    else:
        cube_list = original_cube_list
        if any("precipitation_rate" in cube.name() for cube in cube_list):
            cube_names = [cube.name() for cube in cube_list]
            msg = ("For precipitation fields, orographic enhancement "
                   "filepaths must be supplied. The names of the cubes "
                   "supplied were: {}".format(cube_names))
            raise ValueError(msg)

    # calculate optical flow velocities from T-1 to T and T-2 to T-1, and
    # average to produce the velocities for use in advection
    u_mean, v_mean = generate_optical_flow_components(
        cube_list, ofc_box_size, smart_smoothing_iterations, attributes_dict)

    return CubeList([u_mean, v_mean])
Пример #15
0
 def test_basic_subtract(self):
     """Test the subtraction of a cube of orographic
     enhancement with multiple times from cubes of precipitation rate."""
     expected0 = np.array([[[0., 1., 2.], [1., 2., MIN_PRECIP_RATE_MMH],
                            [0., 1., MIN_PRECIP_RATE_MMH]]])
     expected1 = np.array(
         [[[MIN_PRECIP_RATE_MMH, MIN_PRECIP_RATE_MMH, MIN_PRECIP_RATE_MMH],
           [2., 3., 1.], [2., 3., 1.]]])
     plugin = ApplyOrographicEnhancement("subtract")
     result = plugin.process(self.precip_cubes, self.oe_cube)
     self.assertIsInstance(result, iris.cube.CubeList)
     for aresult, precip_cube in zip(result, self.precip_cubes):
         self.assertEqual(aresult.metadata, precip_cube.metadata)
     for cube in result:
         cube.convert_units("mm/hr")
     self.assertArrayAlmostEqual(result[0].data, expected0)
     self.assertArrayAlmostEqual(result[1].data, expected1)
Пример #16
0
    def test_subtract_with_mask(self):
        """Test the subtraction of cubelists containing cubes of orographic
        enhancement from cubes of precipitation rate, where a mask has been
        applied. Orographic enhancement is not added to the masked points
        (where precip rate <= 1 mm/hr)."""
        expected0 = np.array(
            [
                [
                    [0.0, 1.0, 2.0],
                    [1.0, 2.0, MIN_PRECIP_RATE_MMH],
                    [0.0, 1.0, MIN_PRECIP_RATE_MMH],
                ]
            ]
        )
        expected1 = np.array(
            [
                [
                    [MIN_PRECIP_RATE_MMH, MIN_PRECIP_RATE_MMH, 1.0],
                    [2.0, 3.0, 1.0],
                    [2.0, 3.0, 1.0],
                ]
            ]
        )

        # Mask values within the input precipitation cube that are equal to,
        # or below 1.
        precip_cubes = self.precip_cubes.copy()
        new_precip_cubes = iris.cube.CubeList([])
        for precip_cube in precip_cubes:
            precip_cube.convert_units("mm/hr")
            masked = np.ma.masked_where(precip_cube.data <= 1, precip_cube.data)
            precip_cube.data = masked
            precip_cube.convert_units("m/s")
            new_precip_cubes.append(precip_cube)

        plugin = ApplyOrographicEnhancement("subtract")
        result = plugin.process(self.precip_cubes, self.oe_cube)
        self.assertIsInstance(result, iris.cube.CubeList)
        for aresult, precip_cube in zip(result, self.precip_cubes):
            self.assertIsInstance(aresult.data, np.ma.MaskedArray)
            self.assertEqual(aresult.metadata, precip_cube.metadata)
        for cube in result:
            cube.convert_units("mm/hr")
        self.assertArrayAlmostEqual(result[0].data.data, expected0)
        self.assertArrayAlmostEqual(result[1].data.data, expected1)
Пример #17
0
 def test_basic_subtract(self):
     """Test a minimum precipitation rate is applied, when the orographic
     enhancement causes the precipitation rate to become negative"""
     expected = np.array([[
         [0.0, 1.0, 2.0],
         [1.0, 2.0, MIN_PRECIP_RATE_MMH],
         [0.0, 1.0, MIN_PRECIP_RATE_MMH],
     ]])
     precip_cube = self.precip_cube.copy()
     subtracted_cube = self.subtracted_cube.copy()
     plugin = ApplyOrographicEnhancement("subtract")
     result = plugin._apply_minimum_precip_rate(precip_cube,
                                                subtracted_cube)
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertEqual(result.units, Unit("m/s"))
     self.assertEqual(result.metadata, subtracted_cube.metadata)
     result.convert_units("mm/hr")
     self.assertArrayAlmostEqual(result.data, expected)
Пример #18
0
 def test_no_min_precip_rate_applied_no_input_precip(self):
     """Test no minimum precipitation rate is applied, when the input
     precipitation rate to always below the orographic enhancement."""
     expected = np.array([[[0., 0., 0.], [0., 0., 1.], [0., 1., 1.]]])
     precip_cube = self.precip_cube.copy()
     precip_cube.convert_units("mm/hr")
     precip_cube.data = np.array([[[0., 0., 0.], [0., 0., 5.], [0., 2.,
                                                                3.]]])
     precip_cube.convert_units("m/s")
     subtracted_cube = precip_cube - self.oe_cube
     plugin = ApplyOrographicEnhancement("subtract")
     result = (plugin._apply_minimum_precip_rate(precip_cube,
                                                 subtracted_cube))
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertEqual(result.units, Unit("m/s"))
     self.assertEqual(result.metadata, subtracted_cube.metadata)
     result.convert_units("mm/hr")
     self.assertArrayAlmostEqual(result.data, expected)
Пример #19
0
 def test_only_one_orographic_enhancement_cube(self):
     """Test where is an orographic enhancement cube with a single time
     point is supplied, so that multiple input precipitation fields are
     adjusted by the same orographic enhancement."""
     expected0 = np.array([[[0., 1., 2.], [1., 2., MIN_PRECIP_RATE_MMH],
                            [0., 1., MIN_PRECIP_RATE_MMH]]])
     expected1 = np.array([[[4., 4., 1.], [4., 4., MIN_PRECIP_RATE_MMH],
                            [3., 3., MIN_PRECIP_RATE_MMH]]])
     sliced_oe_cube = self.oe_cube[0]
     plugin = ApplyOrographicEnhancement("subtract")
     result = plugin.process(self.precip_cubes, sliced_oe_cube)
     self.assertIsInstance(result, iris.cube.CubeList)
     for aresult, precip_cube in zip(result, self.precip_cubes):
         self.assertEqual(aresult.metadata, precip_cube.metadata)
     for cube in result:
         cube.convert_units("mm/hr")
     self.assertArrayAlmostEqual(result[0].data, expected0)
     self.assertArrayAlmostEqual(result[1].data, expected1)
Пример #20
0
 def test_differing_units(self):
     """Test that the minimum precipitation rate is applied correctly,
     when the units of the input precipitation cube and the cube
     computed using the input precipitation cube and the orographic
     enhancement cube are handled correctly, even if the units differ."""
     expected = np.array([[[0., 1., 2.], [1., 2., MIN_PRECIP_RATE_MMH],
                           [0., 1., MIN_PRECIP_RATE_MMH]]])
     precip_cube = self.precip_cube.copy()
     subtracted_cube = self.subtracted_cube.copy()
     precip_cube.convert_units("km/hr")
     subtracted_cube.convert_units("ft/s")
     plugin = ApplyOrographicEnhancement("subtract")
     result = (plugin._apply_minimum_precip_rate(precip_cube,
                                                 subtracted_cube))
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertEqual(result.units, Unit("ft/s"))
     self.assertEqual(result.metadata, subtracted_cube.metadata)
     result.convert_units("mm/hr")
     self.assertArrayAlmostEqual(result.data, expected)
Пример #21
0
 def test_no_min_precip_rate_applied_no_negative_rates(self):
     """Test no minimum precipitation rate is applied, when the cube
     calculated by subtracting the orographic enhancement from the input
     precipitation is always positive, so there are no negative values
     that require the minimum precipitation rate to be applied."""
     expected = np.array([[[1., 1., 1.], [1., 2., 3.], [3., 3., 4.]]])
     precip_cube = self.precip_cube.copy()
     subtracted_cube = self.subtracted_cube.copy()
     subtracted_cube.convert_units("mm/hr")
     subtracted_cube.data = np.array([[[1., 1., 1.], [1., 2., 3.],
                                       [3., 3., 4.]]])
     subtracted_cube.convert_units("m/s")
     plugin = ApplyOrographicEnhancement("subtract")
     result = (plugin._apply_minimum_precip_rate(precip_cube,
                                                 subtracted_cube))
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertEqual(result.units, Unit("m/s"))
     self.assertEqual(result.metadata, subtracted_cube.metadata)
     result.convert_units("mm/hr")
     self.assertArrayAlmostEqual(result.data, expected)
Пример #22
0
 def test_NaN_values(self):
     """Test that NaN values are preserved when they are contained within
     the input when applying a minimum precipitation rate."""
     expected = np.array([[[0., 1., 2.], [np.NaN, np.NaN, np.NaN],
                           [0., 1., MIN_PRECIP_RATE_MMH]]])
     precip_cube = self.precip_cube.copy()
     subtracted_cube = self.subtracted_cube.copy()
     subtracted_cube.convert_units("mm/hr")
     subtracted_cube.data = np.array([[[0., 1.,
                                        2.], [np.NaN, np.NaN, np.NaN],
                                       [0., 1., 0.]]])
     subtracted_cube.convert_units("m/s")
     plugin = ApplyOrographicEnhancement("subtract")
     result = (plugin._apply_minimum_precip_rate(precip_cube,
                                                 subtracted_cube))
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertEqual(result.units, Unit("m/s"))
     self.assertEqual(result.metadata, subtracted_cube.metadata)
     result.convert_units("mm/hr")
     self.assertArrayAlmostEqual(result.data, expected)
Пример #23
0
    def _get_advectable_precip_rate(self) -> ndarray:
        """
        From the initial cube, generate a precipitation rate array in mm h-1
        with orographic enhancement subtracted, as required for advection

        Returns:
            2D precipitation rate array in mm h-1
        """
        (self.analysis_cube,
         ) = ApplyOrographicEnhancement("subtract").process(
             self.analysis_cube, self.orogenh)
        self.analysis_cube.convert_units("mm h-1")
        return np.ma.filled(self.analysis_cube.data, np.nan)
 def test_check_scalar_time_dimensions(self):
     """Test the expected values are returned when cubes are combined when
     the dimensions of the input cubes mismatch. As the
     _select_orographic_enhancement_cube extracts a time point, the output
     from this method can result in an orographic enhancement cube
     with a scalar time coordinate. This means that that precipitation cube
     and the orographic enhancement cube do not necessarily match in terms
     of the number of dimensions, as the time dimension may be a dimension
     coordinate within the precipitation cube. This test aims to check
     that the check_cube_coordinate has worked as intended by promoting
     the scalar time coordinate on the orographic enhancement cube,
     if this is required, so that the input precipitation cube and the
     orographic enhancement cube do not have mismatching dimensions."""
     expected = np.array([[[[0., 1., 2.], [1., 2., 7.], [0., 3., 4.]]]])
     oe_cube = self.oe_cube[:, 0, :, :]
     plugin = ApplyOrographicEnhancement("add")
     result = plugin._apply_orographic_enhancement(self.precip_cubes[0],
                                                   oe_cube)
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertEqual(result.metadata, self.precip_cubes[0].metadata)
     result.convert_units("mm/hr")
     self.assertArrayAlmostEqual(result.data, expected)
Пример #25
0
def process(orographic_enhancement: cli.inputcube,
            *cubes: cli.inputcube,
            attributes_config: cli.inputjson = None,
            ofc_box_size: int = 30,
            smart_smoothing_iterations: int = 100):
    """Calculate optical flow components from input fields.

    Args:
        orographic_enhancement (iris.cube.Cube):
            Cube containing the orographic enhancement fields.
        cubes (iris.cube.CubeList):
            Cubes from which to calculate optical flow velocities.
            These three cubes will be sorted by their time coords.
        attributes_config (dict):
            Dictionary containing required changes to the attributes.
            Every output file will have the attributes_config applied.
        ofc_box_size (int):
            Size of square 'box' (in grid spaces) within which to solve
            the optical flow equations.
        smart_smoothing_iterations (int):
            Number of iterations to perform in enforcing smoothness constraint
            for optical flow velocities.

    Returns:
        iris.cube.CubeList:
            List of the umean and vmean cubes.

    """
    from iris.cube import CubeList

    from improver.nowcasting.optical_flow import \
        generate_optical_flow_components
    from improver.nowcasting.utilities import ApplyOrographicEnhancement

    original_cube_list = CubeList(cubes)
    # order input files by validity time
    original_cube_list.sort(key=lambda x: x.coord("time").points[0])

    # subtract orographic enhancement
    cube_list = ApplyOrographicEnhancement("subtract").process(
            original_cube_list, orographic_enhancement)

    # calculate optical flow velocities from T-1 to T and T-2 to T-1, and
    # average to produce the velocities for use in advection
    u_mean, v_mean = generate_optical_flow_components(
        cube_list, ofc_box_size, smart_smoothing_iterations, attributes_config)

    return CubeList([u_mean, v_mean])
Пример #26
0
def generate_advection_velocities_from_winds(
    cubes, background_flow, orographic_enhancement
):
    """Generate advection velocities as perturbations from a non-zero background
    flow

    Args:
        cubes (iris.cube.CubeList):
            Two rainfall observations separated by a time difference
        background_flow (iris.cube.CubeList):
            u- and v-components of a non-zero background flow field
        orographic_enhancement (iris.cube.Cube):
            Field containing orographic enhancement data valid for both
            input cube times

        Returns:
        iris.cube.CubeList:
            u- and v- advection velocities
    """
    cubes.sort(key=lambda x: x.coord("time").points[0])

    lead_time_seconds = (
        cubes[1].coord("time").cell(0).point - cubes[0].coord("time").cell(0).point
    ).total_seconds()
    lead_time_minutes = int(lead_time_seconds / 60)

    # advect earlier cube forward to match time of later cube, using steering flow
    advected_cube = PystepsExtrapolate(lead_time_minutes, lead_time_minutes)(
        cubes[0], *background_flow, orographic_enhancement
    )[-1]

    # calculate velocity perturbations required to match forecast to observation
    cube_list = ApplyOrographicEnhancement("subtract")(
        [advected_cube, cubes[1]], orographic_enhancement
    )
    perturbations = OpticalFlow(data_smoothing_radius_km=8.0, iterations=20)(
        *cube_list, boxsize=18
    )

    # sum perturbations and original flow field to get advection velocities
    total_advection = _perturb_background_flow(background_flow, perturbations)
    return total_advection
Пример #27
0
def process(
    orographic_enhancement: cli.inputcube, *cubes: cli.inputcube,
):
    """Calculate optical flow components from input fields.

    Args:
        orographic_enhancement (iris.cube.Cube):
            Cube containing the orographic enhancement fields.
        cubes (iris.cube.CubeList):
            Cubes from which to calculate optical flow velocities.
            These three cubes will be sorted by their time coords.

    Returns:
        iris.cube.CubeList:
            List of the umean and vmean cubes.

    """
    from iris.cube import CubeList

    from improver.nowcasting.optical_flow import generate_optical_flow_components
    from improver.nowcasting.utilities import ApplyOrographicEnhancement

    original_cube_list = CubeList(cubes)
    # order input files by validity time
    original_cube_list.sort(key=lambda x: x.coord("time").points[0])

    # subtract orographic enhancement
    cube_list = ApplyOrographicEnhancement("subtract")(
        original_cube_list, orographic_enhancement
    )

    # calculate optical flow velocities from T-1 to T and T-2 to T-1, and
    # average to produce the velocities for use in advection
    u_mean, v_mean = generate_optical_flow_components(
        cube_list, ofc_box_size=30, smart_smoothing_iterations=100
    )

    return CubeList([u_mean, v_mean])
Пример #28
0
def process(original_cube_list,
            orographic_enhancement_cube=None,
            metadata_dict=None,
            ofc_box_size=30,
            smart_smoothing_iterations=100,
            extrapolate=False,
            max_lead_time=360,
            lead_time_interval=15):
    """Calculates optical flow and can (optionally) extrapolate data.

    Calculates optical flow components from input fields and (optionally)
    extrapolate to required lead times.

    Args:
        original_cube_list (iris.cube.CubeList):
            Cubelist from which to calculate optical flow velocities.
            The cubes require a 'time' coordinate on which they are sorted,
            so the order of cubes does not matter.
        orographic_enhancement_cube (iris.cube.Cube):
            Cube containing the orographic enhancement fields.
            Default is None.
        metadata_dict (dict):
            Dictionary containing required changes to the metadata.
            Information describing the intended contents of the dictionary is
            available in improver.utilities.cube_metadata.amend_metadata.
            Every output cube will have the metadata_dict applied.
            Default is None.
        ofc_box_size (int):
            Size of square 'box' (in grid spaces) within which to solve
            the optical flow equations.
            Default is 30.
        smart_smoothing_iterations (int):
            Number of iterations to perform in enforcing smoothness constraint
            for optical flow velocities.
            Default is 100.
        extrapolate (bool):
            If True, advects current data forward to specified lead times.
            Default is False.
        max_lead_time (int):
            Maximum lead time required (mins). Ignored unless extrapolate is
            True.
            Default is 360.
        lead_time_interval (int):
            Interval between required lead times (mins). Ignored unless
            extrapolate is True.
            Default is 15.

    Returns:
        (tuple): tuple containing:
            **forecast_cubes** (list<Cube>):
                List of Cubes if extrapolate is True, else None.
            **u_and_v_mean** (list<Cube>):
                List of the umean and vmean cubes.

    Raises:
        ValueError:
            If there is no oe_cube but a cube is called 'precipitation_rate'.

    """
    if orographic_enhancement_cube:
        cube_list = ApplyOrographicEnhancement("subtract").process(
            original_cube_list, orographic_enhancement_cube)
    else:
        cube_list = original_cube_list
        if any("precipitation_rate" in cube.name() for cube in cube_list):
            cube_names = [cube.name() for cube in cube_list]
            msg = ("For precipitation fields, orographic enhancement "
                   "filepaths must be supplied. The names of the cubes "
                   "supplied were: {}".format(cube_names))
            raise ValueError(msg)

    # order input files by validity time
    cube_list.sort(key=lambda x: x.coord("time").points[0])
    time_coord = cube_list[-1].coord("time")
    # calculate optical flow velocities from T-1 to T and T-2 to T-1
    ofc_plugin = OpticalFlow(iterations=smart_smoothing_iterations,
                             metadata_dict=metadata_dict)
    u_cubes = iris.cube.CubeList([])
    v_cubes = iris.cube.CubeList([])
    for older_cube, newer_cube in zip(cube_list[:-1], cube_list[1:]):
        ucube, vcube = ofc_plugin.process(older_cube,
                                          newer_cube,
                                          boxsize=ofc_box_size)
        u_cubes.append(ucube)
        v_cubes.append(vcube)

    # average optical flow velocity components
    u_cube = u_cubes.merge_cube()
    u_mean = u_cube.collapsed("time", iris.analysis.MEAN)
    u_mean.coord("time").points = time_coord.points
    u_mean.coord("time").units = time_coord.units

    v_cube = v_cubes.merge_cube()
    v_mean = v_cube.collapsed("time", iris.analysis.MEAN)
    v_mean.coord("time").points = time_coord.points
    v_mean.coord("time").units = time_coord.units

    u_and_v_mean = [u_mean, v_mean]
    forecast_cubes = []
    if extrapolate:
        # generate list of lead times in minutes
        lead_times = np.arange(0, max_lead_time + 1, lead_time_interval)
        forecast_plugin = CreateExtrapolationForecast(
            original_cube_list[-1],
            u_mean,
            v_mean,
            orographic_enhancement_cube=orographic_enhancement_cube,
            metadata_dict=metadata_dict)
        # extrapolate input data to required lead times
        for lead_time in lead_times:
            forecast_cubes.append(
                forecast_plugin.extrapolate(leadtime_minutes=lead_time))

    return forecast_cubes, u_and_v_mean
Пример #29
0
 def test_basic(self):
     """Test that the __repr__ returns the expected string."""
     result = str(ApplyOrographicEnhancement("add"))
     msg = ('<ApplyOrographicEnhancement: operation: add>')
     self.assertEqual(result, msg)
Пример #30
0
 def test_basic(self):
     """Test that the plugin can be initialised as required."""
     plugin = ApplyOrographicEnhancement("add")
     self.assertEqual(plugin.operation, "add")