示例#1
0
 def test_unconvertable_units(self):
     """"Test plugin produces the correct weights when the parameters for
         the triangle cannot be converted to the same units as the
         coordinate"""
     width = 7200
     weights_instance = ChooseDefaultWeightsTriangular(width, units="m")
     midpoint = 3600
     message = r"Unable to convert from 'Unit\('m'\)' to 'Unit\('hours'\)'"
     with self.assertRaisesRegex(ValueError, message):
         weights_instance.process(self.cube, self.coord_name, midpoint)
示例#2
0
 def test_same_units(self):
     """Test plugin produces the correct weights when the parameters for
        the triangle are in the same units as the input cube's coordinate"""
     width = 2
     WeightsClass = ChooseDefaultWeightsTriangular(width, units=self.units)
     midpoint = 1
     weights = WeightsClass.process(self.cube, self.coord_name, midpoint)
     expected_weights = np.array([0.33333333, 0.66666667])
     self.assertArrayAlmostEqual(weights, expected_weights)
示例#3
0
 def test_different_units(self):
     """"Test plugin produces the correct weights when the parameters for
         the triangle (width and midpoint are in different units to the
         input cube's coordinate"""
     width = 2
     weights_instance = ChooseDefaultWeightsTriangular(width, units="hours")
     midpoint = 1
     weights = weights_instance.process(self.cube, self.coord_name,
                                        midpoint)
     expected_weights = np.array([0.33333333, 0.66666667])
     self.assertArrayAlmostEqual(weights.data, expected_weights)
    def process(self, cube):
        """
        Apply the weighted blend for each point in the given coordinate.

        Args:
            cube : iris.cube.Cube
                Cube to blend.

        Returns:
            cube: iris.cube.Cube
                The processed cube, with the same coordinates as the input
                cube. The points in one coordinate will be blended with the
                adjacent points based on a triangular weighting function of the
                specified width.

        """
        # We need to correct all the coordinates associated with the dimension
        # we are collapsing over, so find the relevant coordinates now.
        dimension_to_collapse = cube.coord_dims(self.coord)
        coords_to_correct = cube.coords(dimensions=dimension_to_collapse)
        coords_to_correct = [coord.name() for coord in coords_to_correct]
        # We will also need to correct the bounds on these coordinates,
        # as bounds will be added when the blending happens, so add bounds if
        # it doesn't have some already.
        for coord in coords_to_correct:
            if not cube.coord(coord).has_bounds():
                cube.coord(coord).guess_bounds()
        # Set up a plugin to calculate the triangular weights.
        WeightsPlugin = ChooseDefaultWeightsTriangular(
            self.width, units=self.parameter_units)
        # Set up the blending function, based on whether weighted blending or
        # maximum probabilities are needed.
        BlendingPlugin = WeightedBlendAcrossWholeDimension(self.coord,
                                                           self.mode)
        result = iris.cube.CubeList([])
        # Loop over each point in the coordinate we are blending over, and
        # calculate a new weighted average for it.
        for cube_slice in cube.slices_over(self.coord):
            point = cube_slice.coord(self.coord).points[0]
            weights = WeightsPlugin.process(cube, self.coord, point)
            blended_cube = BlendingPlugin.process(cube, weights)
            self.correct_collapsed_coordinates(cube_slice, blended_cube,
                                               coords_to_correct)
            result.append(blended_cube)
        result = concatenate_cubes(result)
        return result
class TriangularWeightedBlendAcrossAdjacentPoints(object):
    """
    Apply a Weighted blend to a coordinate, using triangular weights at each
    point in the coordinate. Returns a cube with the same coordinates as the
    input cube, with each point in the coordinate of interest having been
    blended with the adjacent points according to a triangular weighting
    function of a specified width.

    There are two modes of blending:

        1. Weighted mean across the dimension of interest.
        2. Weighted maximum across the dimension of interest, where
           probabilities are multiplied by the weights and the maximum is
           taken.
    """
    def __init__(self, coord, central_point, parameter_units, width,
                 weighting_mode):
        """Set up for a Weighted Blending plugin

        Args:
            coord (string):
                The name of a coordinate dimension in the cube that we
                will blend over.
            central_point (float or int):
                Central point at which the output from the triangular weighted
                blending will be calculated.
            parameter_units (string):
                The units of the width of the triangular weighting function
                and the units of the central_point.
                This does not need to be the same as the units of the
                coordinate we are blending over, but it should be possible to
                convert between them.
            width (float):
                The width of the triangular weighting function we will use
                to blend.
            weighting_mode (string):
                The mode of blending, either weighted_mean or
                weighted_maximum. Weighted average finds the weighted mean
                across the dimension of interest. Maximum probability
                multiplies the values across the dimension of interest by the
                given weights and returns the maximum value.

        Raises:
            ValueError : If an invalid weighting_mode is given
        """
        self.coord = coord
        self.central_point = central_point
        self.parameter_units = parameter_units
        self.width = width
        if weighting_mode not in ['weighted_maximum', 'weighted_mean']:
            msg = ("weighting_mode: {} is not recognised, must be either "
                   "weighted_maximum or weighted_mean").format(weighting_mode)
            raise ValueError(msg)
        self.mode = weighting_mode

        # Set up a plugin to calculate the triangular weights.
        self.WeightsPlugin = ChooseDefaultWeightsTriangular(
            width, units=parameter_units)

        # Set up the blending function, based on whether weighted blending or
        # maximum probabilities are needed.
        self.BlendingPlugin = (WeightedBlendAcrossWholeDimension(
            coord, weighting_mode, timeblending=True))

    def __repr__(self):
        """Represent the configured plugin instance as a string."""
        msg = ('<TriangularWeightedBlendAcrossAdjacentPoints:'
               ' coord = {0:s}, central_point = {1:.2f}, '
               'parameter_units = {2:s}, width = {3:.2f}, mode = {4:s}>')
        return msg.format(self.coord, self.central_point, self.parameter_units,
                          self.width, self.mode)

    def _find_central_point(self, cube):
        """
        Find the cube that contains the central point, otherwise, raise
        an exception.

        Args:
            cube (iris.cube.Cube):
                Cube containing input for blending.

        Returns:
            central_point_cube (iris.cube.Cube):
                Cube containing central point.

        Raises:
            ValueError: Central point is not available within the input cube.

        """
        # Convert central point into the units of the cube, so that a
        # central point can be extracted.
        central_point = (Unit(self.parameter_units).convert(
            self.central_point,
            cube.coord(self.coord).units))
        constr = iris.Constraint(coord_values={self.coord: central_point})
        central_point_cube = cube.extract(constr)
        if central_point_cube is None:
            msg = ("The central point {} in units of {} not available "
                   "within input cube coordinate points: {}.".format(
                       self.central_point, self.parameter_units,
                       cube.coord(self.coord).points))
            raise ValueError(msg)
        return central_point_cube

    def process(self, cube):
        """
        Apply the weighted blend for each point in the given coordinate.

        Args:
            cube (iris.cube.Cube):
                Cube containing input for blending.

        Returns:
            blended_cube (iris.cube.Cube):
                The processed cube, with the same coordinates as the input
                central_cube. The points in one coordinate will be blended
                with the adjacent points based on a triangular weighting
                function of the specified width.

        """
        # Extract the central point from the input cube.
        central_point_cube = self._find_central_point(cube)

        # Calculate weights and produce blended output.
        weights = self.WeightsPlugin.process(cube, self.coord,
                                             self.central_point)
        blended_cube = self.BlendingPlugin.process(cube, weights)

        # With one threshold dimension (such as for low cloud), the threshold
        # axis is demoted to a scalar co-ordinate by BlendingPlugin. This line
        # promotes threshold to match the dimensions of central_point_cube.
        blended_cube = check_cube_coordinates(central_point_cube, blended_cube)

        blended_cube = central_point_cube.copy(blended_cube.data)
        return blended_cube