コード例 #1
0
ファイル: expected_value.py プロジェクト: Kat-90/improver
    def process(self, cube: Cube) -> Cube:
        """Expected value calculation and metadata updates.

        Args:
            cube:
                Probabilistic data with a realization, threshold or percentile
                representation.

        Returns:
            Expected value of probability distribution. Same shape as input cube
            but with realization/threshold/percentile coordinate removed.
        """
        if is_probability(cube):
            # TODO: replace this with direct calculation of the integral over
            # probability thresholds. The current approach works and has the
            # correct interface, but some accuracy will be lost in the conversion
            # and memory usage is very high.
            #
            # 19 percentiles corresponds to 5, 10, 15...95%.
            cube = ConvertProbabilitiesToPercentiles().process(
                cube, no_of_percentiles=19)
        if is_percentile(cube):
            cube = RebadgePercentilesAsRealizations().process(cube)
        mean_cube = collapse_realizations(cube)
        mean_cube.add_cell_method(CellMethod("mean", coords="realization"))
        return mean_cube
コード例 #2
0
def process(cube: cli.inputcube):
    """Sets night values to zero for UV index.

    Args:
        cube (iris.cube.Cube):
            Cube that will have night values set to zero.  This should contain
            either diagnostic values or probabilities of UV index above threshold.

    Returns:
        iris.cube.Cube:
            Input cube with all night values set to zero.

    Raises:
        ValueError: If input cube is suspicious, within reason.  Note that this is
            a general check: the CLI expects a cube of UV index or probability of
            UV index above thresold, and will raise an error if given a probability
            below threshold, but will not recognise a completely inappropriate cube
            (eg temperature in Kelvin).  Therefore this CLI should be used with care.
    """
    import numpy as np

    from improver.metadata.probabilistic import is_probability
    from improver.utilities.solar import DayNightMask

    if is_probability(cube):
        if "above_threshold" not in cube.name():
            raise ValueError(f"{cube.name()} unsuitable for night masking")

    mask = DayNightMask()(cube).data
    # Broadcast mask to shape of input cube to account for additional dimensions.
    mask = np.broadcast_to(mask, cube.shape)
    # setting night values to zero.
    cube.data = np.where(mask == DayNightMask().night, 0, cube.data)
    return cube
コード例 #3
0
 def test_false(self):
     """Test cube that does not contain thresholded probabilities
     evaluates as false"""
     cube = set_up_variable_cube(
         self.data, name="probability_of_rain_at_surface", units="1"
     )
     result = is_probability(cube)
     self.assertFalse(result)
コード例 #4
0
ファイル: expected_value.py プロジェクト: dmentipl/improver
    def process(self, cube: Cube) -> Cube:
        """Expected value calculation and metadata updates.

        Args:
            cube:
                Probabilistic data with a realization, threshold or percentile
                representation.

        Returns:
            Expected value of probability distribution. Same shape as input cube
            but with realization/threshold/percentile coordinate removed.
        """
        if is_probability(cube):
            ev_cube = self.integrate_over_thresholds(cube)
        elif is_percentile(cube):
            ev_cube = collapse_realizations(
                RebadgePercentilesAsRealizations().process(cube)
            )
        else:
            ev_cube = collapse_realizations(cube)
        ev_cube.add_cell_method(CellMethod("mean", coords="realization"))
        return ev_cube
コード例 #5
0
 def test_scalar_threshold_coord(self):
     """Test a probability cube with a single threshold evaluates as true"""
     cube = iris.util.squeeze(self.prob_cube[0])
     result = is_probability(cube)
     self.assertTrue(result)
コード例 #6
0
 def test_true(self):
     """Test a probability cube evaluates as true"""
     result = is_probability(self.prob_cube)
     self.assertTrue(result)
コード例 #7
0
    def process(self, spot_data_cube: Cube, neighbour_cube: Cube,
                gridded_lapse_rate_cube: Cube) -> Cube:
        """
        Extract lapse rates from the appropriate grid points and apply them to
        the spot extracted temperatures.

        The calculation is::

         lapse_rate_adjusted_temperatures = temperatures + lapse_rate *
         vertical_displacement

        Args:
            spot_data_cube:
                A spot data cube of temperatures for the spot data sites,
                extracted from the gridded temperature field. These
                temperatures will have been extracted using the same
                neighbour_cube and neighbour_selection_method that are being
                used here.
            neighbour_cube:
                The neighbour_cube that contains the grid coordinates at which
                lapse rates should be extracted and the vertical displacement
                between those grid points on the model orography and the spot
                data sites actual altitudes. This cube is only updated when
                a new site is added.
            gridded_lapse_rate_cube:
                A cube of temperature lapse rates on the same grid as that from
                which the spot data temperatures were extracted.

        Returns:
            A copy of the input spot_data_cube with the data modified by
            the lapse rates to give a better representation of the site's
            temperatures.

        Raises:
            ValueError:
                If the lapse rate cube was provided but the diagnostic being
                processed is not air temperature.
            ValueError:
                If the lapse rate cube provided does not have the name
                "air_temperature_lapse_rate"
            ValueError:
                If the lapse rate cube does not contain a single valued height
                coordinate.

        Warns:
            warning:
                If a lapse rate cube was provided, but the height of the
                temperature does not match that of the data used.
        """

        if is_probability(spot_data_cube):
            msg = (
                "Input cube has a probability coordinate which cannot be lapse "
                "rate adjusted. Input data should be in percentile or "
                "deterministic space only.")
            raise ValueError(msg)

        # Check that we are dealing with temperature data.
        if spot_data_cube.name() not in [
                "air_temperature", "feels_like_temperature"
        ]:
            msg = (
                "The diagnostic being processed is not air temperature "
                "or feels like temperature and therefore cannot be adjusted.")
            raise ValueError(msg)

        if not gridded_lapse_rate_cube.name() == "air_temperature_lapse_rate":
            msg = ("A cube has been provided as a lapse rate cube but does "
                   "not have the expected name air_temperature_lapse_rate: "
                   "{}".format(gridded_lapse_rate_cube.name()))
            raise ValueError(msg)

        try:
            lapse_rate_height_coord = gridded_lapse_rate_cube.coord("height")
        except (CoordinateNotFoundError):
            msg = ("Lapse rate cube does not contain a single valued height "
                   "coordinate. This is required to ensure it is applied to "
                   "equivalent temperature data.")
            raise CoordinateNotFoundError(msg)

        # Check the height of the temperature data matches that used to
        # calculate the lapse rates. If so, adjust temperatures using the lapse
        # rate values.
        if not spot_data_cube.coord("height") == lapse_rate_height_coord:
            raise ValueError(
                "A lapse rate cube was provided, but the height of the "
                "temperature data does not match that of the data used "
                "to calculate the lapse rates. As such the temperatures "
                "were not adjusted with the lapse rates.")

        # Check the cubes are compatible.
        check_grid_match(
            [neighbour_cube, spot_data_cube, gridded_lapse_rate_cube])

        # Extract the lapse rates that correspond to the spot sites.
        spot_lapse_rate = SpotExtraction(
            neighbour_selection_method=self.neighbour_selection_method)(
                neighbour_cube, gridded_lapse_rate_cube)

        # Extract vertical displacements between the model orography and sites.
        method_constraint = iris.Constraint(
            neighbour_selection_method_name=self.neighbour_selection_method)
        data_constraint = iris.Constraint(
            grid_attributes_key="vertical_displacement")
        vertical_displacement = neighbour_cube.extract(method_constraint
                                                       & data_constraint)

        # Apply lapse rate adjustment to the temperature at each site.
        new_spot_lapse_rate = iris.util.broadcast_to_shape(
            spot_lapse_rate.data, spot_data_cube.shape, [-1])
        new_temperatures = (
            spot_data_cube.data +
            (new_spot_lapse_rate * vertical_displacement.data)).astype(
                np.float32)
        new_spot_cube = spot_data_cube.copy(data=new_temperatures)
        return new_spot_cube
コード例 #8
0
def process(
    cube: cli.inputcube,
    *,
    coordinates: cli.comma_separated_list = None,
    percentiles: cli.comma_separated_list = None,
    ignore_ecc_bounds=False,
):
    r"""Collapses cube coordinates and calculate percentiled data.

    Calculate percentiled data over a given coordinate by collapsing that
    coordinate. Typically used to convert realization data into percentiled
    data, but may calculate over any dimension coordinate. Alternatively
    calling this with a dataset containing probabilities will convert those
    to percentiles using the ensemble coupla coupling plugin. If no particular
    percentiles are given at which to calculate values and no
    'number of percentiles' to calculate are specified, the
    following defaults will be used.
    '[0, 5, 10, 20, 25, 30, 40, 50, 60, 70, 75, 80, 90, 95, 100]'

    Args:
        cube (iris.cube.Cube):
            A Cube for processing.
        coordinates (str or list):
            Coordinate or coordinates over which to collapse data and
            calculate percentiles; e.g. 'realization' or 'latitude,longitude'.
            This argument must be provided when collapsing a coordinate or
            coordinates to create percentiles, but is redundant when
            converting probabilities to percentiles and may be omitted. This
            coordinate(s) will be removed and replaced by a percentile
            coordinate.
        percentiles (list):
            Optional definition of percentiles at which to calculate data.
        ignore_ecc_bounds (bool):
            If True, where calculated percentiles are outside the ECC bounds
            range, raises a warning rather than an exception.

    Returns:
        iris.cube.Cube:
            The processed Cube.

    Raises:
        ValueError:
            If the cube name does not contain 'probability_of\_' and
            coordinates isn't used.

    Warns:
        Warning:
            If 'probability_of\_' is in the cube name and coordinates is used.

    """
    import warnings

    import numpy as np

    from improver.ensemble_copula_coupling.ensemble_copula_coupling import (
        ConvertProbabilitiesToPercentiles, )
    from improver.metadata.probabilistic import is_probability
    from improver.percentile import PercentileConverter

    if percentiles is not None:
        percentiles = [float(p) for p in percentiles]

    if is_probability(cube):
        result = ConvertProbabilitiesToPercentiles(
            ecc_bounds_warning=ignore_ecc_bounds)(cube,
                                                  percentiles=percentiles)
        if coordinates:
            warnings.warn("Converting probabilities to percentiles. The "
                          "provided COORDINATES_TO_COLLAPSE variable will "
                          "not be used.")
    else:
        if not coordinates:
            raise ValueError("To collapse a coordinate to calculate "
                             "percentiles, a coordinate or list of "
                             "coordinates must be provided.")

        # Switch back to use the slow scipy method if the cube contains masked
        # data which the numpy method cannot handle.
        fast_percentile_method = True

        if np.ma.is_masked(cube.data):
            # Check for masked points:
            fast_percentile_method = False
        elif np.ma.isMaskedArray(cube.data):
            # Check if we have a masked array with an empty mask. If so,
            # replace it with a non-masked array:
            cube.data = cube.data.data

        result = PercentileConverter(
            coordinates,
            percentiles=percentiles,
            fast_percentile_method=fast_percentile_method,
        )(cube)
    return result
コード例 #9
0
    def process(self, cube: Cube) -> Cube:
        """
        Produces the vicinity processed data. The input data is sliced to
        yield y-x slices to which the maximum_within_vicinity method is applied.
        The different vicinity radii (if multiple) are looped over and a
        coordinate recording the radius used is added to each resulting cube.
        A single cube is returned with the leading coordinates of the input cube
        preserved. If a single vicinity radius is provided, a new scalar
        radius_of_vicinity coordinate will be found on the returned cube. If
        multiple radii are provided, this coordinate will be a dimension
        coordinate following any probabilistic / realization coordinates.

        Args:
            cube:
                Thresholded cube.

        Returns:
            Cube containing the occurrences within a vicinity for each radius,
            calculated for each yx slice, which have been merged to yield a
            single cube.

        Raises:
            ValueError: Cube and land mask have differing spatial coordinates.
        """
        if self.land_mask_cube and not spatial_coords_match(
            [cube, self.land_mask_cube]):
            raise ValueError(
                "Supplied cube do not have the same spatial coordinates and land mask"
            )

        if not self.native_grid_point_radius:
            grid_point_radii = [
                distance_to_number_of_grid_cells(cube, radius)
                for radius in self.radii
            ]
        else:
            grid_point_radii = self.radii

        radii_cubes = CubeList()

        # List of non-spatial dimensions to restore as leading on the output.
        leading_dimensions = [
            crd.name() for crd in cube.coords(dim_coords=True)
            if not crd.coord_system
        ]

        for radius, grid_point_radius in zip(self.radii, grid_point_radii):
            max_cubes = CubeList([])
            for cube_slice in cube.slices(
                [cube.coord(axis="y"),
                 cube.coord(axis="x")]):
                max_cubes.append(
                    self.maximum_within_vicinity(cube_slice,
                                                 grid_point_radius))
            result_cube = max_cubes.merge_cube()

            # Put dimensions back if they were there before.
            result_cube = check_cube_coordinates(cube, result_cube)

            # Add a coordinate recording the vicinity radius applied to the data.
            self._add_vicinity_coordinate(result_cube, radius)

            radii_cubes.append(result_cube)

        # Merge cubes produced for each vicinity radius.
        result_cube = radii_cubes.merge_cube()

        # Enforce order of leading dimensions on the output to match the input.
        enforce_coordinate_ordering(result_cube, leading_dimensions)

        if is_probability(result_cube):
            result_cube.rename(in_vicinity_name_format(result_cube.name()))
        else:
            result_cube.rename(f"{result_cube.name()}_in_vicinity")

        return result_cube
コード例 #10
0
def process(
    cube: cli.inputcube,
    raw_cube: cli.inputcube = None,
    *,
    realizations_count: int = None,
    random_seed: int = None,
    ignore_ecc_bounds=False,
):
    """Converts an incoming cube into one containing realizations.

    Args:
        cube (iris.cube.Cube):
            A cube to be processed.
        raw_cube (iris.cube.Cube):
            Cube of raw (not post processed) weather data.
            If this argument is given ensemble realizations will be created
            from percentiles by reshuffling them in correspondence to the rank
            order of the raw ensemble. Otherwise, the percentiles are rebadged
            as realizations.
        realizations_count (int):
            The number of ensemble realizations in the output.
        random_seed (int):
            Option to specify a value for the random seed when reordering percentiles.
            This value is for testing purposes only, to ensure reproduceable outputs.
            It should not be used in real time operations as it may introduce a bias
            into the reordered forecasts.
        ignore_ecc_bounds (bool):
            If True where percentiles (calculated as an intermediate output
            before realization) exceed the ECC bounds range, raises a
            warning rather than an exception.

    Returns:
        iris.cube.Cube:
            The processed cube.
    """
    from improver.ensemble_copula_coupling.ensemble_copula_coupling import (
        ConvertProbabilitiesToPercentiles,
        EnsembleReordering,
        RebadgePercentilesAsRealizations,
        ResamplePercentiles,
    )
    from improver.metadata.probabilistic import is_probability

    if cube.coords("realization"):
        return cube

    if not cube.coords("percentile") and not is_probability(cube):
        raise ValueError("Unable to convert to realizations:\n" + str(cube))

    if realizations_count is None:
        try:
            realizations_count = len(raw_cube.coord("realization").points)
        except AttributeError:
            # raised if raw_cube is None, hence has no attribute "coord"
            msg = "Either realizations_count or raw_cube must be provided"
            raise ValueError(msg)

    if cube.coords("percentile"):
        percentiles = ResamplePercentiles(
            ecc_bounds_warning=ignore_ecc_bounds)(
                cube, no_of_percentiles=realizations_count)
    else:
        percentiles = ConvertProbabilitiesToPercentiles(
            ecc_bounds_warning=ignore_ecc_bounds)(
                cube, no_of_percentiles=realizations_count)

    if raw_cube:
        result = EnsembleReordering()(percentiles,
                                      raw_cube,
                                      random_seed=random_seed)
    else:
        result = RebadgePercentilesAsRealizations()(percentiles)

    return result