コード例 #1
0
 def test_exception_raised(self):
     """Test that a CoordinateNotFoundError exception is raised if the
     forecast_period, or the time and forecast_reference_time,
     are not present.
     """
     cube = set_up_cube()
     msg = "The forecast period coordinate is not available"
     with self.assertRaisesRegexp(CoordinateNotFoundError, msg):
         forecast_period_coord(cube)
コード例 #2
0
    def setUp(self):
        """Set up a UK deterministic cube for testing."""
        self.cycletime = datetime.datetime(2017, 1, 10, 6, 0)
        cube_uk_det = set_up_variable_cube(np.full((4, 4),
                                                   273.15,
                                                   dtype=np.float32),
                                           time=self.cycletime,
                                           frt=datetime.datetime(
                                               2017, 1, 10, 3, 0))

        cube_uk_det.remove_coord("forecast_period")
        # set up forecast periods of 6, 8 and 10 hours
        time_points = [1484038800, 1484046000, 1484053200]
        cube_uk_det = add_coordinate(
            cube_uk_det,
            time_points,
            "time",
            dtype=np.int64,
            coord_units="seconds since 1970-01-01 00:00:00")
        fp_coord = forecast_period_coord(cube_uk_det)
        cube_uk_det.add_aux_coord(fp_coord, data_dims=0)

        self.cube_uk_det = add_coordinate(cube_uk_det, [1000], "model_id")
        self.cube_uk_det.add_aux_coord(
            iris.coords.AuxCoord(["uk_det"], long_name="model_configuration"))
コード例 #3
0
 def test_negative_forecast_periods_warning(self, warning_list=None):
     """Test that a warning is raised if the point within the
     time coordinate is prior to the point within the
     forecast_reference_time, and therefore the forecast_period values that
     have been generated are negative.
     """
     cube = add_forecast_reference_time_and_forecast_period(set_up_cube())
     cube.remove_coord("forecast_period")
     cube.coord("forecast_reference_time").points = 402295.0
     cube.coord("time").points = 402192.5
     msg = "The values for the time"
     forecast_period_coord(cube)
     self.assertTrue(len(warning_list) == 1)
     self.assertTrue(
         any(item.category == UserWarning for item in warning_list))
     self.assertTrue(msg in str(warning_list[0]))
コード例 #4
0
 def test_negative_forecast_periods_warning(self, warning_list=None):
     """Test that a warning is raised if the point within the
     time coordinate is prior to the point within the
     forecast_reference_time, and therefore the forecast_period values that
     have been generated are negative.
     """
     cube = set_up_variable_cube(np.ones((3, 3), dtype=np.float32))
     cube.remove_coord("forecast_period")
     # default cube has a 4 hour forecast period, so add 5 hours to frt
     cube.coord("forecast_reference_time").points = (
         cube.coord("forecast_reference_time").points + 5 * 3600)
     warning_msg = "The values for the time"
     forecast_period_coord(cube)
     self.assertTrue(
         any(item.category == UserWarning for item in warning_list))
     self.assertTrue(any(warning_msg in str(item) for item in warning_list))
コード例 #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
コード例 #6
0
 def test_check_coordinate_without_forecast_period(self):
     """Test that the data within the coord is as expected with the
     expected units, when the input cube has a time coordinate and a
     forecast_reference_time coordinate.
     """
     fp_coord = self.cube.coord("forecast_period").copy()
     expected_result = fp_coord
     self.cube.remove_coord("forecast_period")
     result = forecast_period_coord(self.cube)
     self.assertEqual(result, expected_result)
コード例 #7
0
 def test_check_coordinate(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()
     expected_points = fp_coord.points
     expected_units = str(fp_coord.units)
     result = forecast_period_coord(self.cube)
     self.assertArrayEqual(result.points, expected_points)
     self.assertEqual(str(result.units), expected_units)
コード例 #8
0
 def test_check_time_unit_conversion(self):
     """Test that the data within the coord is as expected with the
     expected units, when the input cube has a time coordinate with units
     other than the usual units of seconds since 1970-01-01 00:00:00.
     """
     expected_result = self.cube.coord("forecast_period")
     self.cube.coord("time").convert_units(
         "hours since 1970-01-01 00:00:00")
     result = forecast_period_coord(self.cube,
                                    force_lead_time_calculation=True)
     self.assertEqual(result, expected_result)
コード例 #9
0
 def test_check_coordinate_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.
     """
     cube = add_forecast_reference_time_and_forecast_period(set_up_cube())
     fp_coord = cube.coord("forecast_period").copy()
     fp_coord.convert_units("seconds")
     expected_points = fp_coord.points
     expected_units = str(fp_coord.units)
     result = forecast_period_coord(cube, force_lead_time_calculation=True)
     self.assertArrayAlmostEqual(result.points, expected_points)
     self.assertEqual(result.units, expected_units)
コード例 #10
0
 def test_check_coordinate_without_forecast_period(self):
     """Test that the data within the coord is as expected with the
     expected units, when the input cube has a time coordinate and a
     forecast_reference_time coordinate.
     """
     cube = add_forecast_reference_time_and_forecast_period(set_up_cube())
     fp_coord = cube.coord("forecast_period").copy()
     fp_coord.convert_units("seconds")
     expected_result = fp_coord
     cube.remove_coord("forecast_period")
     result = forecast_period_coord(cube)
     self.assertEqual(result, expected_result)
コード例 #11
0
 def test_check_time_unit_conversion(self):
     """Test that the data within the coord is as expected with the
     expected units, when the input cube has a time coordinate with units
     other than the usual units of hours since 1970-01-01 00:00:00.
     """
     cube = add_forecast_reference_time_and_forecast_period(set_up_cube())
     fp_coord = cube.coord("forecast_period").copy()
     fp_coord.convert_units("seconds")
     expected_result = fp_coord
     cube.coord("time").convert_units("seconds since 1970-01-01 00:00:00")
     result = forecast_period_coord(cube, force_lead_time_calculation=True)
     self.assertEqual(result, expected_result)
コード例 #12
0
 def test_check_coordinate_in_hours_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()
     fp_coord.convert_units("hours")
     expected_points = fp_coord.points
     expected_units = str(fp_coord.units)
     result = forecast_period_coord(self.cube,
                                    force_lead_time_calculation=True,
                                    result_units=fp_coord.units)
     self.assertArrayEqual(result.points, expected_points)
     self.assertEqual(result.units, expected_units)
コード例 #13
0
 def test_check_time_unit_has_bounds(self):
     """Test that the forecast_period coord has bounds if time has bounds.
     """
     cube = set_up_variable_cube(
         np.ones((3, 3), dtype=np.float32),
         time=datetime.datetime(2018, 3, 12, 20, 0),
         frt=datetime.datetime(2018, 3, 12, 15, 0),
         time_bounds=[datetime.datetime(2018, 3, 12, 19, 0),
                      datetime.datetime(2018, 3, 12, 20, 0)])
     expected_result = cube.coord("forecast_period").copy()
     expected_result.bounds = [[14400, 18000]]
     result = forecast_period_coord(cube, force_lead_time_calculation=True)
     self.assertEqual(result, expected_result)
コード例 #14
0
    def test_cubelist_input(self):
        """Test when supplying a cubelist as input containing cubes
        representing UK deterministic and UK ensemble model configuration
        and unifying the forecast_reference_time, so that both model
        configurations have a common forecast_reference_time."""
        cube_uk_ens = set_up_variable_cube(np.full((3, 4, 4),
                                                   273.15,
                                                   dtype=np.float32),
                                           time=self.cycletime,
                                           frt=datetime.datetime(
                                               2017, 1, 10, 4, 0))

        cube_uk_ens.remove_coord("forecast_period")
        # set up forecast periods of 5, 7 and 9 hours
        time_points = [1484031600, 1484038800, 1484046000]
        cube_uk_ens = add_coordinate(
            cube_uk_ens,
            time_points,
            "time",
            dtype=np.int64,
            coord_units="seconds since 1970-01-01 00:00:00")
        fp_coord = forecast_period_coord(cube_uk_ens)
        cube_uk_ens.add_aux_coord(fp_coord, data_dims=0)

        expected_uk_det = self.cube_uk_det.copy()
        frt_units = expected_uk_det.coord('forecast_reference_time').units
        frt_points = [
            np.round(frt_units.date2num(self.cycletime)).astype(np.int64)
        ]
        expected_uk_det.coord("forecast_reference_time").points = frt_points
        expected_uk_det.coord("forecast_period").points = (
            np.array([3, 5, 7]) * 3600)
        expected_uk_ens = cube_uk_ens.copy()
        expected_uk_ens.coord("forecast_reference_time").points = frt_points
        expected_uk_ens.coord("forecast_period").points = (
            np.array([1, 3, 5]) * 3600)
        expected = iris.cube.CubeList([expected_uk_det, expected_uk_ens])

        cubes = iris.cube.CubeList([self.cube_uk_det, cube_uk_ens])
        result = unify_forecast_reference_time(cubes, self.cycletime)

        self.assertIsInstance(result, iris.cube.CubeList)
        self.assertEqual(result, expected)
コード例 #15
0
 def test_basic(self):
     """Test that an iris.coord.DimCoord is returned."""
     result = forecast_period_coord(self.cube)
     self.assertIsInstance(result, iris.coords.DimCoord)
コード例 #16
0
 def test_basic_AuxCoord(self):
     """Test that an iris.coord.AuxCoord is returned."""
     self.cube.remove_coord('forecast_period')
     result = forecast_period_coord(self.cube,
                                    force_lead_time_calculation=True)
     self.assertIsInstance(result, iris.coords.AuxCoord)
コード例 #17
0
def conform_metadata(cube,
                     cube_orig,
                     coord,
                     cycletime=None,
                     coords_for_bounds_removal=None):
    """Ensure that the metadata conforms after blending together across
    the chosen coordinate.

    The metadata adjustments are:
        - Forecast reference time: If a cycletime is not present, the
          most recent available forecast_reference_time is used.
          If a cycletime is present, the cycletime is used as the
          forecast_reference_time instead.
        - Forecast period: If a forecast_period coordinate is present,
          and cycletime is not present, the lowest forecast_period is
          used. If a forecast_period coordinate is present, and the
          cycletime is present, forecast_periods are forceably calculated
          from the time and forecast_reference_time coordinate. This is
          because, if the cycletime is present, then the
          forecast_reference_time will also have been just re-calculated, so
          the forecast_period coordinate needs to be reset to match the
          newly calculated forecast_reference_time.
        - Forecast reference time and time: If forecast_reference_time and
          time coordinates are present, then a forecast_period coordinate is
          calculated and added to the cube.
        - Model_id, model_realization and realization coordinates are removed.
        - Remove bounds from the scalar coordinates, if the coordinates
          are specified within the coords_for_bounds_removal argument.

    Args:
        cube (iris.cube.Cube):
            Cube containing the metadata to be adjusted.
        cube_orig (iris.cube.Cube):
            Cube containing metadata that may be useful for adjusting
            metadata on the `cube` variable.
        coord (str):
            Coordinate that has been blended. This allows specific metadata
            changes to be limited to whichever coordinate is being blended.

    Keyword Args:
        cycletime (str):
            The cycletime in a YYYYMMDDTHHMMZ format e.g. 20171122T0100Z.
        coords_for_bounds_removal (None or list):
            List of coordinates that are scalar and should have their bounds
            removed.

    Returns:
        cube (iris.cube.Cube):
            Cube containing the adjusted metadata.

    """
    if coord in ["forecast_reference_time", "model"]:
        if cube.coords("forecast_reference_time"):
            if cycletime is None:
                new_cycletime = (np.max(
                    cube_orig.coord("forecast_reference_time").points))
            else:
                cycletime_units = (
                    cube_orig.coord("forecast_reference_time").units.origin)
                cycletime_calendar = (
                    cube.coord("forecast_reference_time").units.calendar)
                new_cycletime = cycletime_to_number(
                    cycletime,
                    time_unit=cycletime_units,
                    calendar=cycletime_calendar)
                # Preserve the data type to avoid converting ints to floats.
                fr_type = cube.coord("forecast_reference_time").dtype
                new_cycletime = np.round(new_cycletime).astype(fr_type)
            cube.coord("forecast_reference_time").points = new_cycletime
            cube.coord("forecast_reference_time").bounds = None

        if cube.coords("forecast_period"):
            forecast_period = (forecast_period_coord(
                cube, force_lead_time_calculation=True))
            forecast_period.bounds = None
            forecast_period.convert_units(cube.coord("forecast_period").units)
            forecast_period.var_name = cube.coord("forecast_period").var_name
            cube.replace_coord(forecast_period)
        elif cube.coords("forecast_reference_time") and cube.coords("time"):
            forecast_period = (forecast_period_coord(cube))
            ndim = cube.coord_dims("time")
            cube.add_aux_coord(forecast_period, data_dims=ndim)

    for coord in ["model_id", "model_realization", "realization"]:
        if cube.coords(coord) and cube.coord(coord).shape == (1, ):
            cube.remove_coord(coord)

    if coords_for_bounds_removal is None:
        coords_for_bounds_removal = []

    for coord in cube.coords():
        if coord.name() in coords_for_bounds_removal:
            if coord.shape == (1, ) and coord.has_bounds():
                coord.bounds = None
    return cube
コード例 #18
0
 def test_basic_AuxCoord(self):
     """Test that an iris.coord.AuxCoord is returned."""
     cube = add_forecast_reference_time_and_forecast_period(set_up_cube())
     cube.remove_coord('forecast_period')
     result = forecast_period_coord(cube, force_lead_time_calculation=True)
     self.assertIsInstance(result, iris.coords.AuxCoord)
コード例 #19
0
 def test_basic(self):
     """Test that an iris.coord.DimCoord is returned."""
     cube = add_forecast_reference_time_and_forecast_period(set_up_cube())
     result = forecast_period_coord(cube)
     self.assertIsInstance(result, iris.coords.DimCoord)
コード例 #20
0
    def process(self, cube, mask_cube=None):
        """
        Supply neighbourhood processing method, in order to smooth the
        input cube.

        Args:
            cube (Iris.cube.Cube):
                Cube to apply a neighbourhood processing method to, in order to
                generate a smoother field.

        Keyword Args:
            mask_cube (Iris.cube.Cube):
                Cube containing the array to be used as a mask.

        Returns:
            cube (Iris.cube.Cube):
                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 'source_realizations' in cube.attributes:
                msg = ("Realizations and attribute source_realizations "
                       "should not both be set in input cube")
                raise ValueError(msg)

        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)
                if len(cubes_time) > 1:
                    cube_new = concatenate_cubes(cubes_time,
                                                 coords_to_slice_over=["time"])
                else:
                    cube_new = cubes_time[0]
            cubes_real.append(cube_new)
        if len(cubes_real) > 1:
            combined_cube = concatenate_cubes(
                cubes_real, coords_to_slice_over=["realization"])
        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
コード例 #21
0
def conform_metadata(cube, cube_orig, coord, cycletime=None):
    """Ensure that the metadata conforms after blending together across
    the chosen coordinate.

    The metadata adjustments are:
        - Forecast reference time: If a cycletime is not present, the
          most recent available forecast_reference_time is used.
          If a cycletime is present, the cycletime is used as the
          forecast_reference_time instead.
        - Forecast period: If a forecast_period coordinate is present,
          and cycletime is not present, the lowest forecast_period is
          used. If a forecast_period coordinate is present, and the
          cycletime is present, forecast_periods are forceably re-calculated
          from the time and forecast_reference_time coordinates so that their
          values are self-consistent.
        - Forecast reference time and time: If forecast_reference_time and
          time coordinates are present, then a forecast_period coordinate is
          calculated and added to the cube.
        - Model_id, model_realization and realization coordinates are removed.
        - A title attribute is added to the cube if none is found. Otherwise
          the attributes are unchanged.

    Args:
        cube (iris.cube.Cube):
            Cube containing the metadata to be adjusted.
        cube_orig (iris.cube.Cube):
            Cube containing metadata that may be useful for adjusting
            metadata on the `cube` variable.
        coord (str):
            Coordinate that has been blended. This allows specific metadata
            changes to be limited to whichever coordinate is being blended.
        cycletime (str):
            The cycletime in a YYYYMMDDTHHMMZ format e.g. 20171122T0100Z.

    Returns:
        cube (iris.cube.Cube):
            Cube containing the adjusted metadata.

    """
    # unify time coordinates for cycle and grid (model) blends
    if coord in ["forecast_reference_time", "model_id"]:
        # if cycle blending, update forecast reference time and remove bounds
        if cube.coords("forecast_reference_time"):
            if cycletime is None:
                new_cycletime = (np.max(
                    cube_orig.coord("forecast_reference_time").points))
            else:
                cycletime_units = (
                    cube_orig.coord("forecast_reference_time").units.origin)
                cycletime_calendar = (
                    cube.coord("forecast_reference_time").units.calendar)
                new_cycletime = cycletime_to_number(
                    cycletime,
                    time_unit=cycletime_units,
                    calendar=cycletime_calendar)
                # Preserve the data type to avoid converting ints to floats.
                frt_type = cube.coord("forecast_reference_time").dtype
                new_cycletime = np.round(new_cycletime).astype(frt_type)
            cube.coord("forecast_reference_time").points = new_cycletime
            cube.coord("forecast_reference_time").bounds = None

        # recalculate forecast period coordinate
        if cube.coords("forecast_period"):
            forecast_period = forecast_period_coord(
                cube, force_lead_time_calculation=True)
            forecast_period.convert_units(cube.coord("forecast_period").units)
            forecast_period.var_name = cube.coord("forecast_period").var_name
            cube.replace_coord(forecast_period)
        elif cube.coords("forecast_reference_time") and cube.coords("time"):
            forecast_period = forecast_period_coord(cube)
            ndim = cube.coord_dims("time")
            cube.add_aux_coord(forecast_period, data_dims=ndim)

    # update blended cube attributes
    if "title" not in cube.attributes.keys():
        cube.attributes["title"] = "IMPROVER Model Forecast"

    # remove appropriate scalar coordinates
    for crd in ["model_id", "model_configuration", "realization"]:
        if cube.coords(crd) and cube.coord(crd).shape == (1, ):
            cube.remove_coord(crd)

    return cube
コード例 #22
0
ファイル: set_up_test_cubes.py プロジェクト: kinow/improver
def add_coordinate(incube,
                   coord_points,
                   coord_name,
                   coord_units=None,
                   dtype=np.float32,
                   order=None,
                   is_datetime=False):
    """
    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):
            Values for the coordinate.
        coord_name (str):
            Long name of the coordinate to be added.

    Kwargs:
        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.

    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_UNIT
        dtype = np.int64
        new_coord_points = []
        for val in coord_points:
            time_point_seconds = np.round(date2num(val, TIME_UNIT,
                                                   CALENDAR)).astype(np.int64)
            new_coord_points.append(time_point_seconds)
        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))

        # recalculate forecast period if time or frt have been updated
        if is_datetime and "time" in coord_name:
            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