Exemple #1
0
    def test_multiple_radii_no_lead_times(self):
        """Test that when multiple radii are provided with no lead times an
        exception is raised."""

        radii = ["10000", "20000"]
        lead_times = None

        msg = "Multiple radii have been supplied but no associated lead times."
        with self.assertRaisesRegex(ValueError, msg):
            radius_by_lead_time(radii, lead_times)
Exemple #2
0
    def test_unmatched_input_length(self):
        """Test that when multiple radii are provided with an unmatched number
        of lead times an exception is raised."""

        radii = ["10000", "20000"]
        lead_times = ["0", "10", "20"]

        msg = "If leadtimes are supplied, it must be a list of equal length"
        with self.assertRaisesRegex(ValueError, msg):
            radius_by_lead_time(radii, lead_times)
Exemple #3
0
    def test_single_radius(self):
        """Test that when a single radius is provided with no lead times the
        returned objects are a float equal to the input radius and a NoneType
        representing the lead times."""

        radii = ["10000"]
        lead_times = None
        radii_out, lead_times_out = radius_by_lead_time(radii, lead_times)

        self.assertEqual(radii_out, float(radii[0]))
        self.assertEqual(lead_times_out, None)
        self.assertIsInstance(radii_out, float)
Exemple #4
0
    def test_multiple_radii(self):
        """Test that when multiple radii are provided with lead times the
        returned objects are two lists, one of radii as floats and one of lead
        times as ints."""

        radii = ["10000", "20000"]
        lead_times = ["0", "10"]
        radii_out, lead_times_out = radius_by_lead_time(radii, lead_times)

        self.assertEqual(radii_out, list(map(float, radii)))
        self.assertEqual(lead_times_out, list(map(int, lead_times)))
        self.assertIsInstance(radii_out[0], float)
        self.assertIsInstance(lead_times_out[0], int)
Exemple #5
0
def process(cube: cli.inputcube,
            mask: cli.inputcube,
            weights: cli.inputcube = None,
            *,
            coord_for_masking,
            radii: cli.comma_separated_list,
            lead_times: cli.comma_separated_list = None,
            area_sum=False,
            remask=False,
            collapse_dimension=False):
    """Runs neighbourhooding processing iterating over a coordinate by mask.

    Apply the requested neighbourhood method via the
    ApplyNeighbourhoodProcessingWithMask plugin to a file with one diagnostic
    dataset in combination with a cube containing one or more masks.
    The mask dataset may have an extra dimension compared to the input
    diagnostic. In this case, the user specifies the name of the extra
    coordinate and this coordinate is iterated over so each mask is applied
    to separate slices over the input cube. These intermediate masked datasets
    are then concatenated, resulting in a dataset that has been processed
    using multiple masks and has gained an extra dimension from the masking.
    There is also an option to re-mask the output dataset, so that after
    neighbourhood processing non-zero values are only present for unmasked
    grid points.
    There is an alternative option of collapsing the dimension that we gain
    using this processing using a weighted average.

    Args:
        cube (iris.cube.Cube):
            Cube to be processed.
        mask (iris.cube.Cube):
            Cube to act as a mask.
        weights (iris.cube.Cube, Optional):
            Cube containing the weights which are used for collapsing the
            dimension gained through masking. (Optional).
        coord_for_masking (str):
            String matching the name of the coordinate that will be used
            for masking.
        radii (list of float):
            The radius or a list of radii in metres of the neighbourhood to
            apply.
            If it is a list, it must be the same length as lead_times, which
            defines at which lead time to use which nbhood radius. The radius
            will be interpolated for intermediate lead times.
        lead_times (list of int):
            The lead times in hours that correspond to the radii to be used.
            If lead_times are set, radii must be a list the same length as
            lead_times. Lead times must be given as integer values.
        area_sum (bool):
            Return sum rather than fraction over the neighbourhood area.
        remask (bool):
            Include this option to apply the original un-neighbourhood
            processed mask to the neighbourhood processed cube.
            Otherwise the original un-neighbourhood processed mask
            is not applied. Therefore, the neighbourhood processing may result
            in values being present in area that were originally masked.
        collapse_dimension (bool):
            Include this option to collapse the dimension from the mask, by
            doing a weighted mean using the weights provided. This is only
            suitable when the result is left unmasked, so there is data to
            weight between the points in the coordinate we are collapsing.

    Returns:
        iris.cube.Cube:
            A cube after being fully processed.
    """
    from improver.nbhood import radius_by_lead_time
    from improver.nbhood.use_nbhood import (
        ApplyNeighbourhoodProcessingWithAMask,
        CollapseMaskedNeighbourhoodCoordinate,
    )

    sum_or_fraction = 'sum' if area_sum else 'fraction'

    radius_or_radii, lead_times = radius_by_lead_time(radii, lead_times)

    result = ApplyNeighbourhoodProcessingWithAMask(
        coord_for_masking,
        radius_or_radii,
        lead_times=lead_times,
        sum_or_fraction=sum_or_fraction,
        re_mask=remask)(cube, mask)

    # Collapse with the masking dimension.
    if collapse_dimension:
        result = CollapseMaskedNeighbourhoodCoordinate(coord_for_masking,
                                                       weights)(result)
    return result
Exemple #6
0
def process(
    cube: cli.inputcube,
    mask: cli.inputcube = None,
    *,
    neighbourhood_output,
    neighbourhood_shape="square",
    radii: cli.comma_separated_list,
    lead_times: cli.comma_separated_list = None,
    degrees_as_complex=False,
    weighted_mode=False,
    area_sum=False,
    percentiles: cli.comma_separated_list = DEFAULT_PERCENTILES,
    halo_radius: float = None,
):
    """Runs neighbourhood processing.

    Apply the requested neighbourhood method via the
    NeighbourhoodProcessing plugin to a Cube.

    Args:
        cube (iris.cube.Cube):
            The Cube to be processed.
        mask (iris.cube.Cube):
            A cube to mask the input cube. The data should contain 1 for
            usable points and 0 for discarded points.
            Can't be used with "percentiles" as neighbourhood_output (Optional)
        neighbourhood_output (str):
            The form of the results generated using neighbourhood processing.
            If "probabilities" is selected, the mean probability with a
            neighbourhood is calculated. If "percentiles" is selected, then
            the percentiles are calculated with a neighbourhood. Calculating
            percentiles from a neighbourhood is only supported for a circular
            neighbourhood.
            Options: "probabilities", "percentiles".
        neighbourhood_shape (str):
            Name of the neighbourhood method to use. Only a "circular"
            neighbourhood shape is applicable for calculating "percentiles"
            output.
            Options: "circular", "square".
            Default: "square".
        radii (list of float):
            The radius or a list of radii in metres of the neighbourhood to
            apply.
            If it is a list, it must be the same length as lead_times, which
            defines at which lead time to use which nbhood radius. The radius
            will be interpolated for intermediate lead times.
        lead_times (list of int):
            The lead times in hours that correspond to the radii to be used.
            If lead_times are set, radii must be a list the same length as
            lead_times.
        degrees_as_complex (bool):
            Include this option to process angles as complex numbers.
            Not compatible with circular kernel or percentiles.
        weighted_mode (bool):
            Include this option to set the weighting to decrease with radius.
            Otherwise a constant weighting is assumed.
            weighted_mode is only applicable for calculating "probability"
            neighbourhood output using the circular kernel.
        area_sum (bool):
            Return sum rather than fraction over the neighbourhood area.
        percentiles (float):
            Calculates value at the specified percentiles from the
            neighbourhood surrounding each grid point. This argument has no
            effect if the output is probabilities.
        halo_radius (float):
            Set this radius in metres to define the excess halo to clip. Used
            where a larger grid was defined than the standard grid and we want
            to clip the grid back to the standard grid. Otherwise no clipping
            is applied.

    Returns:
        iris.cube.Cube:
            A processed Cube.

    Raises:
        RuntimeError:
            If weighted_mode is used with the wrong neighbourhood_output.
        RuntimeError:
            If degree_as_complex is used with
            neighbourhood_output='percentiles'.
        RuntimeError:
            If degree_as_complex is used with neighbourhood_shape='circular'.
    """
    from improver.nbhood import radius_by_lead_time
    from improver.nbhood.nbhood import (
        GeneratePercentilesFromANeighbourhood,
        NeighbourhoodProcessing,
    )
    from improver.utilities.pad_spatial import remove_cube_halo
    from improver.wind_calculations.wind_direction import WindDirection

    if neighbourhood_output == "percentiles":
        if weighted_mode:
            raise RuntimeError("weighted_mode cannot be used with"
                               'neighbourhood_output="percentiles"')
        if degrees_as_complex:
            raise RuntimeError("Cannot generate percentiles from complex "
                               "numbers")

    if neighbourhood_shape == "circular":
        if degrees_as_complex:
            raise RuntimeError(
                "Cannot process complex numbers with circular neighbourhoods")

    if degrees_as_complex:
        # convert cube data into complex numbers
        cube.data = WindDirection.deg_to_complex(cube.data)

    radius_or_radii, lead_times = radius_by_lead_time(radii, lead_times)

    if neighbourhood_output == "probabilities":
        result = NeighbourhoodProcessing(
            neighbourhood_shape,
            radius_or_radii,
            lead_times=lead_times,
            weighted_mode=weighted_mode,
            sum_only=area_sum,
            re_mask=True,
        )(cube, mask_cube=mask)
    elif neighbourhood_output == "percentiles":
        result = GeneratePercentilesFromANeighbourhood(
            radius_or_radii,
            lead_times=lead_times,
            percentiles=percentiles,
        )(cube)

    if degrees_as_complex:
        # convert neighbourhooded cube back to degrees
        result.data = WindDirection.complex_to_deg(result.data)
    if halo_radius is not None:
        result = remove_cube_halo(result, halo_radius)
    return result
Exemple #7
0
def process(
    cube: cli.inputcube,
    mask: cli.inputcube,
    weights: cli.inputcube = None,
    *,
    coord_for_masking,
    neighbourhood_shape="square",
    radii: cli.comma_separated_list,
    lead_times: cli.comma_separated_list = None,
    area_sum=False,
):
    """Runs neighbourhooding processing iterating over a coordinate by mask.

    Apply the requested neighbourhood method via the
    ApplyNeighbourhoodProcessingWithMask plugin to a file with one diagnostic
    dataset in combination with a cube containing one or more masks.
    The mask dataset may have an extra dimension compared to the input
    diagnostic. In this case, the user specifies the name of the extra
    coordinate and this coordinate is iterated over so each mask is applied
    to separate slices over the input cube. These intermediate masked datasets
    are then concatenated, resulting in a dataset that has been processed
    using multiple masks and has gained an extra dimension from the masking.
    If weights are given the masking dimension that we gain will be collapsed
    using a weighted average.

    Args:
        cube (iris.cube.Cube):
            Cube to be processed.
        mask (iris.cube.Cube):
            Cube to act as a mask.
        weights (iris.cube.Cube, Optional):
            Cube containing the weights which are used for collapsing the
            dimension gained through masking. (Optional).
        coord_for_masking (str):
            String matching the name of the coordinate that will be used
            for masking.
        neighbourhood_shape (str):
            Name of the neighbourhood method to use.
            Options: "circular", "square".
            Default: "square".
        radii (list of float):
            The radius or a list of radii in metres of the neighbourhood to
            apply.
            If it is a list, it must be the same length as lead_times, which
            defines at which lead time to use which nbhood radius. The radius
            will be interpolated for intermediate lead times.
        lead_times (list of int):
            The lead times in hours that correspond to the radii to be used.
            If lead_times are set, radii must be a list the same length as
            lead_times. Lead times must be given as integer values.
        area_sum (bool):
            Return sum rather than fraction over the neighbourhood area.


    Returns:
        iris.cube.Cube:
            A cube after being fully processed.
    """
    from improver.nbhood import radius_by_lead_time
    from improver.nbhood.use_nbhood import ApplyNeighbourhoodProcessingWithAMask

    radius_or_radii, lead_times = radius_by_lead_time(radii, lead_times)

    result = ApplyNeighbourhoodProcessingWithAMask(
        coord_for_masking,
        neighbourhood_shape,
        radius_or_radii,
        lead_times=lead_times,
        collapse_weights=weights,
        sum_only=area_sum,
    )(cube, mask)

    return result