Beispiel #1
0
 def test_exception_insufficient_data(self):
     """Test that a CoordinateNotFoundError exception is raised if forecast
     period cannot be calculated from the available coordinates
     """
     self.cube.remove_coord("forecast_reference_time")
     self.cube.remove_coord("forecast_period")
     msg = "The forecast period coordinate is not available"
     with self.assertRaisesRegex(CoordinateNotFoundError, msg):
         forecast_period_coord(self.cube)
Beispiel #2
0
def _set_blended_time_coords(blended_cube: Cube,
                             cycletime: Optional[str]) -> None:
    """
    For cycle and model blending:
    - Add a "blend_time" coordinate equal to the current cycletime
    - Update the forecast reference time and forecast period coordinate points
    to reflect the current cycle time (behaviour is DEPRECATED)
    - Remove any bounds from the forecast reference time (behaviour is DEPRECATED)
    - Mark the forecast reference time and forecast period as DEPRECATED

    Modifies cube in place.

    Args:
        blended_cube
        cycletime:
            Current cycletime in YYYYMMDDTHHmmZ format
    """
    try:
        cycletime_point = _get_cycletime_point(blended_cube, cycletime)
    except TypeError:
        raise ValueError(
            "Current cycle time is required for cycle and model blending")

    add_blend_time(blended_cube, cycletime)
    blended_cube.coord("forecast_reference_time").points = [cycletime_point]
    blended_cube.coord("forecast_reference_time").bounds = None
    if blended_cube.coords("forecast_period"):
        blended_cube.remove_coord("forecast_period")
    new_forecast_period = forecast_period_coord(blended_cube)
    time_dim = blended_cube.coord_dims("time")
    blended_cube.add_aux_coord(new_forecast_period, data_dims=time_dim)
    for coord in ["forecast_period", "forecast_reference_time"]:
        msg = f"{coord} will be removed in future and should not be used"
        blended_cube.coord(coord).attributes.update(
            {"deprecation_message": msg})
Beispiel #3
0
 def test_no_forecast_period(self):
     """Test that an iris.coords.AuxCoord is returned from a cube with no
     forecast period"""
     self.cube.remove_coord("forecast_period")
     result = forecast_period_coord(self.cube,
                                    force_lead_time_calculation=True)
     self.assertIsInstance(result, iris.coords.AuxCoord)
Beispiel #4
0
    def process(self, cube: Cube) -> Cube:
        """
        Supply a cube with a forecast period coordinate in order to set the
        correct radius for use in neighbourhood processing.

        Also checkes there are no unmasked NaNs in the input cube.

        Args:
            cube:
                Cube to apply a neighbourhood processing method.

        Returns:
            cube:
                The unaltered input cube.
        """

        if np.isnan(cube.data).any():
            raise ValueError("Error: NaN detected in input cube data")

        if self.lead_times:
            # Interpolate to find the radius at each required lead time.
            fp_coord = forecast_period_coord(cube)
            fp_coord.convert_units("hours")
            self.radius = self._find_radii(cube_lead_times=fp_coord.points)
        return cube
Beispiel #5
0
def update_time_and_forecast_period(cube, increment):
    """Updates time and forecast period points on an existing cube by a given
    increment (in units of time)"""
    cube.coord("time").points = cube.coord("time").points + increment
    forecast_period = forecast_period_coord(cube, force_lead_time_calculation=True)
    cube.replace_coord(forecast_period)
    return cube
Beispiel #6
0
 def test_values(self):
     """Test that the data within the coord is as expected with the
     expected units, when the input cube has a forecast_period coordinate.
     """
     fp_coord = self.cube.coord("forecast_period").copy()
     result = forecast_period_coord(self.cube)
     self.assertArrayEqual(result.points, fp_coord.points)
     self.assertEqual(result.units, fp_coord.units)
     self.assertEqual(result.dtype, fp_coord.dtype)
Beispiel #7
0
 def test_values_force_lead_time_calculation(self):
     """Test that the data within the coord is as expected with the
     expected units, when the input cube has a forecast_period coordinate.
     """
     fp_coord = self.cube.coord("forecast_period").copy()
     # put incorrect data into the existing coordinate so we can test it is
     # correctly recalculated
     self.cube.coord("forecast_period").points = [-3600]
     result = forecast_period_coord(
         self.cube, force_lead_time_calculation=True)
     self.assertArrayEqual(result.points, fp_coord.points)
     self.assertEqual(result.units, fp_coord.units)
     self.assertEqual(result.dtype, fp_coord.dtype)
    def _set_forecast_reference_time_and_period(self, blended_cube):
        """
        For cycle and model blending, update the forecast reference time and
        forecast period coordinate points to the single most recent value,
        rather than the blended average, and remove any bounds from the
        forecast reference time. Modifies cube in place.

        Args:
            blended_cube (iris.cube.Cube)
        """
        blended_cube.coord("forecast_reference_time").points = [
            self.cycletime_point
        ]
        blended_cube.coord("forecast_reference_time").bounds = None
        if blended_cube.coords("forecast_period"):
            blended_cube.remove_coord("forecast_period")
        new_forecast_period = forecast_period_coord(blended_cube)
        time_dim = blended_cube.coord_dims("time")
        blended_cube.add_aux_coord(new_forecast_period, data_dims=time_dim)
Beispiel #9
0
    def process(self, cube: Cube, mask_cube: Optional[Cube] = None) -> Cube:
        """
        Supply neighbourhood processing method, in order to smooth the
        input cube.

        Args:
            cube:
                Cube to apply a neighbourhood processing method to, in order to
                generate a smoother field.
            mask_cube:
                Cube containing the array to be used as a mask.

        Returns:
            Cube after applying a neighbourhood processing method, so that
            the resulting field is smoothed.
        """
        if not getattr(self.neighbourhood_method, "run", None) or not callable(
                self.neighbourhood_method.run):
            msg = ("{} is not valid as a neighbourhood_method. "
                   "Please choose a valid neighbourhood_method with a "
                   "run method.".format(self.neighbourhood_method))
            raise ValueError(msg)

        # Check if a dimensional realization coordinate exists. If so, the
        # cube is sliced, so that it becomes a scalar coordinate.
        try:
            cube.coord("realization", dim_coords=True)
        except iris.exceptions.CoordinateNotFoundError:
            slices_over_realization = [cube]
        else:
            slices_over_realization = cube.slices_over("realization")

        if np.isnan(cube.data).any():
            raise ValueError("Error: NaN detected in input cube data")

        cubes_real = []
        for cube_realization in slices_over_realization:
            if self.lead_times is None:
                cube_new = self.neighbourhood_method.run(cube_realization,
                                                         self.radii,
                                                         mask_cube=mask_cube)
            else:
                # Interpolate to find the radius at each required lead time.
                fp_coord = forecast_period_coord(cube_realization)
                fp_coord.convert_units("hours")
                required_radii = self._find_radii(
                    cube_lead_times=fp_coord.points)

                cubes_time = iris.cube.CubeList([])
                # Find the number of grid cells required for creating the
                # neighbourhood, and then apply the neighbourhood
                # processing method to smooth the field.
                for cube_slice, radius in zip(
                        cube_realization.slices_over("time"), required_radii):
                    cube_slice = self.neighbourhood_method.run(
                        cube_slice, radius, mask_cube=mask_cube)
                    cubes_time.append(cube_slice)
                cube_new = MergeCubes()(cubes_time)

            cubes_real.append(cube_new)

        if len(cubes_real) > 1:
            combined_cube = MergeCubes()(cubes_real,
                                         slice_over_realization=True)
        else:
            combined_cube = cubes_real[0]

        # Promote dimensional coordinates that used to be present.
        exception_coordinates = find_dimension_coordinate_mismatch(
            cube, combined_cube, two_way_mismatch=False)
        combined_cube = check_cube_coordinates(
            cube, combined_cube, exception_coordinates=exception_coordinates)

        return combined_cube
Beispiel #10
0
def add_coordinate(incube,
                   coord_points,
                   coord_name,
                   coord_units=None,
                   dtype=np.float32,
                   order=None,
                   is_datetime=False,
                   attributes=None):
    """
    Function to duplicate a sample cube with an additional coordinate to create
    a cubelist. The cubelist is merged to create a single cube, which can be
    reordered to place the new coordinate in the required position.

    Args:
        incube (iris.cube.Cube):
            Cube to be duplicated.
        coord_points (list or numpy.ndarray):
            Values for the coordinate.
        coord_name (str):
            Long name of the coordinate to be added.
        coord_units (str):
            Coordinate unit required.
        dtype (type):
            Datatype for coordinate points.
        order (list):
            Optional list of integers to reorder the dimensions on the new
            merged cube.  For example, if the new coordinate is required to
            be in position 1 on a 4D cube, use order=[1, 0, 2, 3] to swap the
            new coordinate position with that of the original leading
            coordinate.
        is_datetime (bool):
            If "true", the leading coordinate points have been given as a
            list of datetime objects and need converting.  In this case the
            "coord_units" argument is overridden and the time points provided
            in seconds.  The "dtype" argument is overridden and set to int64.
        attributes (dict):
            Optional coordinate attributes.

    Returns:
        iris.cube.Cube:
            Cube containing an additional dimension coordinate.
    """
    # if the coordinate already exists as a scalar coordinate, remove it
    cube = incube.copy()
    try:
        cube.remove_coord(coord_name)
    except CoordinateNotFoundError:
        pass

    # if new coordinate points are provided as datetimes, convert to seconds
    if is_datetime:
        coord_units = TIME_COORDS["time"].units
        dtype = TIME_COORDS["time"].dtype
        new_coord_points = [_create_time_point(val) for val in coord_points]
        coord_points = new_coord_points

    cubes = iris.cube.CubeList([])
    for val in coord_points:
        temp_cube = cube.copy()
        temp_cube.add_aux_coord(
            DimCoord(np.array([val], dtype=dtype),
                     long_name=coord_name,
                     units=coord_units,
                     attributes=attributes))

        # recalculate forecast period if time or frt have been updated
        if ("time" in coord_name and coord_units is not None
                and Unit(coord_units).is_time_reference()):
            forecast_period = forecast_period_coord(
                temp_cube, force_lead_time_calculation=True)
            try:
                temp_cube.replace_coord(forecast_period)
            except CoordinateNotFoundError:
                temp_cube.add_aux_coord(forecast_period)

        cubes.append(temp_cube)

    new_cube = cubes.merge_cube()
    if order is not None:
        new_cube.transpose(order)

    return new_cube
Beispiel #11
0
 def test_basic(self):
     """Test that an iris.coords.DimCoord is returned from a cube with an
     existing forecast period"""
     result = forecast_period_coord(self.cube)
     self.assertIsInstance(result, iris.coords.DimCoord)