Example #1
0
    def _create_output_cube(self, cube, advected_data, timestep):
        """
        Create a cube and appropriate metadata to contain the advected forecast

        Args:
            cube (iris.cube.Cube):
                Source cube (before advection)
            advected_data (numpy.ndarray):
                Advected data
            timestep (datetime.timedelta):
                Time difference between the advected output and the source

        Returns:
            iris.cube.Cube
        """
        attributes = generate_mandatory_attributes([cube])
        if "institution" in cube.attributes.keys():
            attributes["source"] = "{} Nowcast".format(attributes["institution"])
        else:
            attributes["source"] = "Nowcast"
        advected_cube = create_new_diagnostic_cube(
            cube.name(), cube.units, cube, attributes, data=advected_data
        )
        amend_attributes(advected_cube, self.attributes_dict)
        set_history_attribute(advected_cube, "Nowcast")

        self._update_time(cube.coord("time").copy(), advected_cube, timestep)
        self._add_forecast_reference_time(cube.coord("time").copy(), advected_cube)
        self._add_forecast_period(advected_cube, timestep)

        return advected_cube
Example #2
0
    def _reformat_analysis_cube(self, attribute_changes):
        """
        Add forecast reference time and forecast period coordinates (if they do
        not already exist) and nowcast attributes to analysis cube
        """
        coords = [coord.name() for coord in self.analysis_cube.coords()]
        if "forecast_reference_time" not in coords:
            frt_coord = self.analysis_cube.coord("time").copy()
            frt_coord.rename("forecast_reference_time")
            self.analysis_cube.add_aux_coord(frt_coord)
        if "forecast_period" not in coords:
            self.analysis_cube.add_aux_coord(
                AuxCoord(np.array([0], dtype=np.int32), "forecast_period", "seconds")
            )

        self.analysis_cube.attributes = generate_mandatory_attributes(
            [self.analysis_cube]
        )
        self.analysis_cube.attributes["source"] = "MONOW"
        self.analysis_cube.attributes[
            "title"
        ] = "MONOW Extrapolation Nowcast on UK 2 km Standard Grid"
        set_history_attribute(self.analysis_cube, "Nowcast")
        if attribute_changes is not None:
            amend_attributes(self.analysis_cube, attribute_changes)
Example #3
0
 def test_history_append(self):
     """Test that the history attribute can be updated."""
     cube = set_up_variable_cube(np.ones((3, 3), dtype=np.float32))
     old_history = "2018-09-13T11:28:29: StaGE"
     cube.attributes["history"] = old_history
     set_history_attribute(cube, "Nowcast", append=True)
     self.assertTrue("history" in cube.attributes)
     self.assertTrue("Nowcast" in cube.attributes["history"])
     self.assertTrue(old_history in cube.attributes["history"])
Example #4
0
 def test_history_already_exists(self):
     """Test that the history attribute is overwritten, if it
     already exists."""
     cube = set_up_variable_cube(np.ones((3, 3), dtype=np.float32))
     old_history = "2018-09-13T11:28:29: StaGE"
     cube.attributes["history"] = old_history
     set_history_attribute(cube, "Nowcast")
     self.assertTrue("history" in cube.attributes)
     self.assertTrue("Nowcast" in cube.attributes["history"])
     self.assertFalse(old_history in cube.attributes["history"])
Example #5
0
 def test_history_append_no_existing(self):
     """Test the "append" option doesn't crash when no history exists."""
     cube = set_up_variable_cube(np.ones((3, 3), dtype=np.float32))
     set_history_attribute(cube, "Nowcast", append=True)
     self.assertTrue("history" in cube.attributes)
     self.assertTrue("Nowcast" in cube.attributes["history"])
Example #6
0
 def test_add_history(self):
     """Test that a history attribute has been added."""
     cube = set_up_variable_cube(np.ones((3, 3), dtype=np.float32))
     set_history_attribute(cube, "Nowcast")
     self.assertTrue("history" in cube.attributes)
     self.assertTrue("Nowcast" in cube.attributes["history"])
Example #7
0
    def process(self, cube, timestep):
        """
        Extrapolates input cube data and updates validity time.  The input
        cube should have precisely two non-scalar dimension coordinates
        (spatial x/y), and is expected to be in a projection such that grid
        spacing is the same (or very close) at all points within the spatial
        domain.  The input cube should also have a "time" coordinate.

        Args:
            cube (iris.cube.Cube):
                The 2D cube containing data to be advected
            timestep (datetime.timedelta):
                Advection time step

        Returns:
            iris.cube.Cube:
                New cube with updated time and extrapolated data.  New data
                are filled with np.nan and masked where source data were
                out of bounds (ie where data could not be advected from outside
                the cube domain).

        """
        # check that the input cube has precisely two non-scalar dimension
        # coordinates (spatial x/y) and a scalar time coordinate
        check_input_coords(cube, require_time=True)

        # check spatial coordinates match those of plugin velocities
        if (cube.coord(axis="x") != self.x_coord
                or cube.coord(axis="y") != self.y_coord):
            raise InvalidCubeError("Input data grid does not match advection "
                                   "velocities")

        # derive velocities in "grid squares per second"
        def grid_spacing(coord):
            """Calculate grid spacing along a given spatial axis"""
            new_coord = coord.copy()
            new_coord.convert_units('m')
            return np.float32(np.diff((new_coord).points)[0])

        grid_vel_x = self.vel_x.data / grid_spacing(cube.coord(axis="x"))
        grid_vel_y = self.vel_y.data / grid_spacing(cube.coord(axis="y"))

        # raise a warning if data contains unmasked NaNs
        nan_count = np.count_nonzero(~np.isfinite(cube.data))
        if nan_count > 0:
            warnings.warn("input data contains unmasked NaNs")

        # perform advection and create output cube
        advected_data = self._advect_field(cube.data, grid_vel_x, grid_vel_y,
                                           timestep.total_seconds())
        advected_cube = cube.copy(data=advected_data)

        # increment output cube time and add a "forecast_period" coordinate
        original_datetime, = \
            (cube.coord("time").units).num2date(cube.coord("time").points)
        new_datetime = original_datetime + timestep

        new_time = (cube.coord("time").units).date2num(new_datetime)

        advected_cube.coord("time").points = new_time
        advected_cube.coord("time").convert_units(
            "seconds since 1970-01-01 00:00:00")
        advected_cube.coord("time").points = (np.around(
            advected_cube.coord("time").points).astype(np.int64))

        try:
            advected_cube.coord("forecast_reference_time").convert_units(
                "seconds since 1970-01-01 00:00:00")
        except CoordinateNotFoundError:
            frt_coord = cube.coord("time").copy()
            frt_coord.rename("forecast_reference_time")
            advected_cube.add_aux_coord(frt_coord)
            advected_cube.coord("forecast_reference_time").convert_units(
                "seconds since 1970-01-01 00:00:00")

        frt_points = np.around(
            advected_cube.coord("forecast_reference_time").points).astype(
                np.int64)
        advected_cube.coord("forecast_reference_time").points = frt_points

        forecast_period_seconds = np.int32(timestep.total_seconds())
        forecast_period_coord = AuxCoord(forecast_period_seconds,
                                         standard_name="forecast_period",
                                         units="s")
        try:
            advected_cube.remove_coord("forecast_period")
        except CoordinateNotFoundError:
            pass
        advected_cube.add_aux_coord(forecast_period_coord)

        # Modify the source attribute to describe the advected field as a
        # Nowcast
        if "institution" in advected_cube.attributes.keys():
            advected_cube.attributes["source"] = ("{} Nowcast".format(
                advected_cube.attributes["institution"]))
        else:
            advected_cube.attributes["source"] = "Nowcast"
        amend_attributes(advected_cube, self.attributes_dict)
        set_history_attribute(advected_cube, "Nowcast")
        return advected_cube