Exemplo n.º 1
0
    def _create_template_slice(self, cube_to_collapse):
        """
        Create a template cube from a slice of the cube we are collapsing.
        The slice will be over blend_coord, y and x and will remove any other
        dimensions. This means that the resulting spatial weights won't vary in
        any other dimension other than the blend_coord. If the mask does
        vary in another dimension an error is raised.

        Args:
            cube_to_collapse (iris.cube.Cube):
                The cube that will be collapsed along the blend_coord
                using the spatial weights generated using this plugin. Must
                be masked where there is invalid data.

        Returns:
            iris.cube.Cube:
                A cube with dimensions blend_coord, y, x, on which to shape the
                output weights cube.

        Raises:
            ValueError: if the blend coordinate is associated with more than
                        one dimension on the cube to collapse, or no dimension
            ValueError: if the mask on cube_to_collapse varies along a
                        dimension other than the dimension associated with
                        blend_coord.
        """
        self.blend_coord = find_blend_dim_coord(cube_to_collapse,
                                                self.blend_coord)

        # Find original dim coords in input cube
        original_dim_coords = get_dim_coord_names(cube_to_collapse)

        # Slice over required coords
        x_coord = cube_to_collapse.coord(axis="x").name()
        y_coord = cube_to_collapse.coord(axis="y").name()
        coords_to_slice_over = [self.blend_coord, y_coord, x_coord]

        if original_dim_coords == coords_to_slice_over:
            return cube_to_collapse

        # Check mask does not vary over additional dimensions
        slices = cube_to_collapse.slices(coords_to_slice_over)
        first_slice = next(slices)
        if np.ma.is_masked(first_slice.data):
            first_mask = first_slice.data.mask
            for cube_slice in slices:
                if not np.all(cube_slice.data.mask == first_mask):
                    message = (
                        "The mask on the input cube can only vary along the "
                        "blend_coord, differences in the mask were found "
                        "along another dimension")
                    raise ValueError(message)
        # Remove non-spatial non-blend dimensions, returning a 3D template cube without
        # additional scalar coordinates (eg realization)
        for coord in original_dim_coords:
            if coord not in coords_to_slice_over:
                first_slice.remove_coord(coord)
        # Return slice template
        return first_slice
Exemplo n.º 2
0
    def process(self, cube, weights=None):
        """Calculate weighted blend across the chosen coord, for either
           probabilistic or percentile data. If there is a percentile
           coordinate on the cube, it will blend using the
           PercentileBlendingAggregator but the percentile coordinate must
           have at least two points.

        Args:
            cube (iris.cube.Cube):
                Cube to blend across the coord.
            weights (iris.cube.Cube):
                Cube of blending weights. This will have 1 or 3 dimensions,
                corresponding either to blend dimension on the input cube with or
                without and additional 2 spatial dimensions. If None, the input cube
                is blended with equal weights across the blending dimension.

        Returns:
            iris.cube.Cube:
                Containing the weighted blend across the chosen coordinate (typically
                forecast reference time or model).

        Raises:
            TypeError : If the first argument not a cube.
            CoordinateNotFoundError : If coordinate to be collapsed not found
                                      in cube.
            CoordinateNotFoundError : If coordinate to be collapsed not found
                                      in provided weights cube.
            ValueError : If coordinate to be collapsed is not a dimension.
        """
        if not isinstance(cube, iris.cube.Cube):
            msg = ("The first argument must be an instance of iris.cube.Cube "
                   "but is {}.".format(type(cube)))
            raise TypeError(msg)

        if not cube.coords(self.blend_coord):
            msg = "Coordinate to be collapsed not found in cube."
            raise CoordinateNotFoundError(msg)

        output_dims = get_dim_coord_names(
            next(cube.slices_over(self.blend_coord)))
        self.blend_coord = find_blend_dim_coord(cube, self.blend_coord)

        # Ensure input cube and weights cube are ordered equivalently along
        # blending coordinate.
        cube = sort_coord_in_cube(cube, self.blend_coord)
        if weights is not None:
            if not weights.coords(self.blend_coord):
                msg = "Coordinate to be collapsed not found in weights cube."
                raise CoordinateNotFoundError(msg)
            weights = sort_coord_in_cube(weights, self.blend_coord)

        # Check that the time coordinate is single valued if required.
        self.check_compatible_time_points(cube)

        # Do blending and update metadata
        if self.check_percentile_coord(cube):
            enforce_coordinate_ordering(cube, [self.blend_coord, "percentile"])
            result = self.percentile_weighted_mean(cube, weights)
        else:
            enforce_coordinate_ordering(cube, [self.blend_coord])
            result = self.weighted_mean(cube, weights)

        # Reorder resulting dimensions to match input
        enforce_coordinate_ordering(result, output_dims)

        return result
Exemplo n.º 3
0
def test_find_blend_dim_coord_noop(cycle_cube, input_coord_name):
    """Test no impact and returns correctly if called on dimension"""
    result = find_blend_dim_coord(cycle_cube, input_coord_name)
    assert result == "forecast_reference_time"
Exemplo n.º 4
0
def test_find_blend_dim_coord_error_no_dim(cycle_cube):
    """Test error if blend coordinate has no dimension"""
    cube = next(cycle_cube.slices_over("forecast_reference_time"))
    with pytest.raises(ValueError, match="no associated dimension"):
        find_blend_dim_coord(cube, "forecast_reference_time")