def test_time_as_number(self): # Make sure Coord.cell() normally returns the values straight # out of the Coord's points/bounds arrays. coord = self._mock_coord() cell = Coord.cell(coord, 0) self.assertIs(cell.point, mock.sentinel.time) self.assertEqual(cell.bound, (mock.sentinel.lower, mock.sentinel.upper))
def test_time_as_number(self): # Make sure Coord.cell() normally returns the values straight # out of the Coord's points/bounds arrays. coord = self._mock_coord() cell = Coord.cell(coord, 0) self.assertIs(cell.point, mock.sentinel.time) self.assertEquals(cell.bound, (mock.sentinel.lower, mock.sentinel.upper))
def _add_forecast_reference_time(input_time: Coord, advected_cube: Cube) -> None: """Add or replace a forecast reference time on the advected cube""" try: advected_cube.remove_coord("forecast_reference_time") except CoordinateNotFoundError: pass frt_coord_name = "forecast_reference_time" frt_coord_spec = TIME_COORDS[frt_coord_name] frt_coord = input_time.copy() frt_coord.rename(frt_coord_name) frt_coord.convert_units(frt_coord_spec.units) frt_coord.points = round_close(frt_coord.points, dtype=frt_coord_spec.dtype) advected_cube.add_aux_coord(frt_coord)
def pad_coord(coord: Coord, width: int, method: str) -> Coord: """ Construct a new coordinate by extending the current coordinate by the padding width. Args: coord: Original coordinate which will be used as the basis of the new extended coordinate. width: The width of padding in grid cells (the extent of the neighbourhood radius in grid cells in a given direction). method: A string determining whether the coordinate is being expanded or contracted. Options: 'remove' to remove points from coord; 'add' to add points to coord. Returns: Coordinate with expanded or contracted length, to be added to the padded or unpadded iris cube. Raises: ValueError: Raise an error if non-uniform increments exist between grid points. """ orig_points = coord.points increment = orig_points[1:] - orig_points[:-1] if np.isclose(np.sum(np.diff(increment)), 0): increment = increment[0] else: msg = "Non-uniform increments between grid points: " "{}.".format( increment) raise ValueError(msg) if method == "add": num_of_new_points = len(orig_points) + width + width new_points = np.linspace( orig_points[0] - width * increment, orig_points[-1] + width * increment, num_of_new_points, dtype=np.float32, ) elif method == "remove": end_width = -width if width != 0 else None new_points = np.float32(orig_points[width:end_width]) new_points = new_points.astype(orig_points.dtype) new_points_bounds = np.array( [new_points - 0.5 * increment, new_points + 0.5 * increment], dtype=np.float32).T return coord.copy(points=new_points, bounds=new_points_bounds)
def test_time_as_object(self): # Ensure Coord.cell() converts the point/bound values to # "datetime" objects. coord = self._mock_coord() coord.units.num2date = mock.Mock( side_effect=[mock.sentinel.datetime, (mock.sentinel.datetime_lower, mock.sentinel.datetime_upper)]) cell = Coord.cell(coord, 0) self.assertIs(cell.point, mock.sentinel.datetime) self.assertEqual(cell.bound, (mock.sentinel.datetime_lower, mock.sentinel.datetime_upper)) self.assertEqual(coord.units.num2date.call_args_list, [mock.call((mock.sentinel.time,)), mock.call((mock.sentinel.lower, mock.sentinel.upper))])
def test_time_as_object(self): # Ensure Coord.cell() converts the point/bound values to # "datetime" objects. coord = self._mock_coord() coord.units.num2date = mock.Mock(side_effect=[ mock.sentinel.datetime, (mock.sentinel.datetime_lower, mock.sentinel.datetime_upper) ]) cell = Coord.cell(coord, 0) self.assertIs(cell.point, mock.sentinel.datetime) self.assertEqual( cell.bound, (mock.sentinel.datetime_lower, mock.sentinel.datetime_upper)) self.assertEqual(coord.units.num2date.call_args_list, [ mock.call((mock.sentinel.time, )), mock.call((mock.sentinel.lower, mock.sentinel.upper)) ])
def test_time_as_object(self): # When iris.FUTURE.cell_datetime_objects is True, ensure # Coord.cell() converts the point/bound values to "datetime" # objects. coord = self._mock_coord() coord.units.num2date = mock.Mock( side_effect=[mock.sentinel.datetime, (mock.sentinel.datetime_lower, mock.sentinel.datetime_upper)]) with mock.patch('iris.FUTURE', cell_datetime_objects=True): cell = Coord.cell(coord, 0) self.assertIs(cell.point, mock.sentinel.datetime) self.assertEqual(cell.bound, (mock.sentinel.datetime_lower, mock.sentinel.datetime_upper)) self.assertEqual(coord.units.num2date.call_args_list, [mock.call((mock.sentinel.time,)), mock.call((mock.sentinel.lower, mock.sentinel.upper))])
def iris_time_to_datetime(time_coord: Coord, point_or_bound: str = "point") -> List[datetime]: """ Convert iris time to python datetime object. Working in UTC. Args: time_coord: Iris time coordinate element(s). Returns: The time element(s) recast as a python datetime object. """ coord = time_coord.copy() coord.convert_units("seconds since 1970-01-01 00:00:00") if point_or_bound == "point": datetime_list = [value.point for value in coord.cells()] elif point_or_bound == "bound": datetime_list = [value.bound for value in coord.cells()] return datetime_list
def test_time_as_object(self): # When iris.FUTURE.cell_datetime_objects is True, ensure # Coord.cell() converts the point/bound values to "datetime" # objects. coord = self._mock_coord() coord.units.num2date = mock.Mock(side_effect=[ mock.sentinel.datetime, (mock.sentinel.datetime_lower, mock.sentinel.datetime_upper) ]) with mock.patch('iris.FUTURE', cell_datetime_objects=True): cell = Coord.cell(coord, 0) self.assertIs(cell.point, mock.sentinel.datetime) self.assertEquals( cell.bound, (mock.sentinel.datetime_lower, mock.sentinel.datetime_upper)) self.assertEqual(coord.units.num2date.call_args_list, [ mock.call((mock.sentinel.time, )), mock.call((mock.sentinel.lower, mock.sentinel.upper)) ])
def _update_time(input_time: Coord, advected_cube: Cube, timestep: timedelta) -> None: """Increment validity time on the advected cube Args: input_time: Time coordinate from source cube advected_cube: Cube containing advected data (modified in place) timestep: Time difference between the advected output and the source """ original_datetime = next(input_time.cells())[0] new_datetime = original_datetime + timestep new_time = input_time.units.date2num(new_datetime) time_coord_name = "time" time_coord_spec = TIME_COORDS[time_coord_name] time_coord = advected_cube.coord(time_coord_name) time_coord.points = new_time time_coord.convert_units(time_coord_spec.units) time_coord.points = round_close(time_coord.points, dtype=time_coord_spec.dtype)
def test_coord_stash(self): token = 'stash' coord = Coord(1, attributes=dict(STASH=token)) self._check(token, coord)
def grid_spacing(coord: Coord) -> float: """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])
def test(self): emsg = ("Can't instantiate abstract class Coord with abstract" " methods __init__") with self.assertRaisesRegex(TypeError, emsg): _ = Coord(points=[0, 1])
def _calculate_forecast_period( time_coord: Coord, frt_coord: Coord, dim_coord: bool = False, coord_spec: TimeSpec = TIME_COORDS["forecast_period"], ) -> Coord: """ Calculate a forecast period from existing time and forecast reference time coordinates. Args: time_coord: Time coordinate frt_coord: Forecast reference coordinate dim_coord: If true, create an iris.coords.DimCoord instance. Default is to create an iris.coords.AuxCoord. coord_spec: Specification of units and dtype for the forecast_period coordinate. Returns: Forecast period coordinate corresponding to the input times and forecast reference times specified Warns: UserWarning: If any calculated forecast periods are negative """ # use cell() access method to get datetime.datetime instances time_points = np.array([c.point for c in time_coord.cells()]) forecast_reference_time_points = np.array([c.point for c in frt_coord.cells()]) required_lead_times = time_points - forecast_reference_time_points required_lead_times = np.array([x.total_seconds() for x in required_lead_times]) if time_coord.bounds is not None: time_bounds = np.array([c.bound for c in time_coord.cells()]) required_lead_time_bounds = time_bounds - forecast_reference_time_points required_lead_time_bounds = np.array( [[b.total_seconds() for b in x] for x in required_lead_time_bounds] ) else: required_lead_time_bounds = None coord_type = DimCoord if dim_coord else AuxCoord result_coord = coord_type( required_lead_times, standard_name="forecast_period", bounds=required_lead_time_bounds, units="seconds", ) result_coord.convert_units(coord_spec.units) if coord_spec.dtype not in FLOAT_TYPES: result_coord.points = round_close(result_coord.points) if result_coord.bounds is not None: result_coord.bounds = round_close(result_coord.bounds) result_coord.points = result_coord.points.astype(coord_spec.dtype) if result_coord.bounds is not None: result_coord.bounds = result_coord.bounds.astype(coord_spec.dtype) if np.any(result_coord.points < 0): msg = ( "The values for the time {} and " "forecast_reference_time {} coordinates from the " "input cube have produced negative values for the " "forecast_period. A forecast does not generate " "values in the past." ).format(time_coord.points, frt_coord.points) warnings.warn(msg) return result_coord
def test_coord_standard_name(self): token = 'air_temperature' coord = Coord(1, standard_name=token) self._check(token, coord)
def test_mixture_default(self): token = 'air temperature' # includes space coord = Coord(1, long_name=token) result = CellMethod(self.method, coords=[coord, token]) expected = '{}: unknown, unknown'.format(self.method, token, token) self.assertEqual(str(result), expected)
def test_mixture(self): token = 'air_temperature' coord = Coord(1, standard_name=token) result = CellMethod(self.method, coords=[coord, token]) expected = '{}: {}, {}'.format(self.method, token, token) self.assertEqual(str(result), expected)
def test_coord_stash_default(self): token = '_stash' # includes leading underscore coord = Coord(1, attributes=dict(STASH=token)) self._check(token, coord, default=True)
def test_coord_var_name_fail(self): token = 'var name' # includes space emsg = 'is not a valid NetCDF variable name' with self.assertRaisesRegexp(ValueError, emsg): Coord(1, var_name=token)
def test_coord_var_name(self): token = 'var_name' coord = Coord(1, var_name=token) self._check(token, coord)
def test_coord_long_name_default(self): token = 'long name' # includes space coord = Coord(1, long_name=token) self._check(token, coord, default=True)
def test_coord_long_name(self): token = 'long_name' coord = Coord(1, long_name=token) self._check(token, coord)