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 add_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"])
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 add_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"])
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)) add_history_attribute(cube, "Nowcast", append=True) self.assertTrue("history" in cube.attributes) self.assertTrue("Nowcast" in cube.attributes["history"])
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)) add_history_attribute(cube, "Nowcast") self.assertTrue("history" in cube.attributes) self.assertTrue("Nowcast" in cube.attributes["history"])
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: advected_cube (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" add_history_attribute(advected_cube, "Nowcast") advected_cube = amend_metadata(advected_cube, **self.metadata_dict) return advected_cube