def make_square_NxM_2d_cube_with_time(start_lat=-10, end_lat=10, lat_point_count=5, start_lon=-5, end_lon=5, lon_point_count=3, time_offset=0): """ Makes a well defined cube of shape 5x3 with data as follows arr([[[ 1. 2. 3. 4. 5. 6. 7.] [ 8. 9. 10. 11. 12. 13. 14.] [ 15. 16. 17. 18. 19. 20. 21.]] [[ 22. 23. 24. 25. 26. 27. 28.] [ 29. 30. 31. 32. 33. 34. 35.] [ 36. 37. 38. 39. 40. 41. 42.]] [[ 43. 44. 45. 46. 47. 48. 49.] [ 50. 51. 52. 53. 54. 55. 56.] [ 57. 58. 59. 60. 61. 62. 63.]] [[ 64. 65. 66. 67. 68. 69. 70.] [ 71. 72. 73. 74. 75. 76. 77.] [ 78. 79. 80. 81. 82. 83. 84.]] [[ 85. 86. 87. 88. 89. 90. 91.] [ 92. 93. 94. 95. 96. 97. 98.] [ 99. 100. 101. 102. 103. 104. 105.]]]) and coordinates in latitude: array([ -10, -5, 0, 5, 10 ]) longitude: array([ -5, 0, 5 ]) time: array([1984-08-27, 1984-08-28, 1984-08-29, 1984-08-30, 1984-08-31, 1984-09-01, 1984-09-02]) They are different lengths to make it easier to distinguish. Note the latitude increases as you step through the array in order - so downwards as it's written above """ import numpy as np from iris.cube import Cube from iris.coords import DimCoord import datetime from cis.time_util import convert_obj_to_standard_date_array t0 = datetime.datetime(1984, 8, 27) times = np.array([t0 + datetime.timedelta(days=d + time_offset) for d in xrange(7)]) time_nums = convert_obj_to_standard_date_array(times) time = DimCoord(time_nums, standard_name='time') latitude = DimCoord(np.linspace(start_lat, end_lat, lat_point_count), standard_name='latitude', units='degrees') longitude = DimCoord(np.linspace(start_lon, end_lon, lon_point_count), standard_name='longitude', units='degrees') data = np.reshape(np.arange(lat_point_count * lon_point_count * 7) + 1.0, (lat_point_count, lon_point_count, 7)) cube = Cube(data, dim_coords_and_dims=[(latitude, 0), (longitude, 1), (time, 2)], var_name='dummy') return cube
def index_data(self, coords, hyper_points, coord_map): """ Index the data that falls inside the grid cells :param coords: coordinates of grid :param hyper_points: list of HyperPoints to index :param coord_map: list of tuples relating index in HyperPoint to index in coords and in coords to be iterated over """ # create bounds in correct order hp_coords = [] coord_descreasing = [False] * len(coords) coord_lengths = [0] * len(coords) lower_bounds = [None] * len(coords) max_bounds = [None] * len(coords) for (hpi, ci, shi) in coord_map: coord = coords[ci] # Coordinates must be monotonic; determine whether increasing or decreasing. if len(coord.points) > 1: if coord.points[1] < coord.points[0]: coord_descreasing[shi] = True coord_lengths[shi] = len(coord.points) if coord_descreasing[shi]: lower_bounds[shi] = coord.bounds[::-1, 1] max_bounds[shi] = coord.bounds[0, 1] else: lower_bounds[shi] = coord.bounds[::, 0] max_bounds[shi] = coord.bounds[-1, 1] hp_coord = hyper_points.coords[hpi] if isinstance(hp_coord[0], datetime.datetime): hp_coord = convert_obj_to_standard_date_array(hp_coord) hp_coords.append(hp_coord) bounds_coords_max = zip(lower_bounds, hp_coords, max_bounds) # stack for each coordinate # where the coordinate is larger than the maximum set to -1 # otherwise search in the sorted coordinate to find all the index of the hyperpoints # The choice of 'left' or 'right' and '<' and '<=' determines which # cell is chosen when the coordinate is equal to the boundary. # -1 or M_i indicates the point is outside the grid. # Output is a list of coordinates which lists the indexes where the hyper points # should be located in the grid indices = np.vstack( np.where( ci < max_coordinate_value, np.searchsorted(bi, ci, side='right') - 1, -1) for bi, ci, max_coordinate_value in bounds_coords_max) # D-tuple giving the shape of the output grid grid_shape = tuple(len(bi_ci[0]) for bi_ci in bounds_coords_max) # shape (N,) telling which points actually fall within the grid, # i.e. have indexes that are not -1 and are not masked data points grid_mask = np.all( (indices >= 0) & (ma.getmaskarray(hyper_points.data) == False), axis=0) # if the coordinate was decreasing then correct the indices for this cell for indices_slice, decreasing, coord_length in zip(xrange(indices.shape[0]), coord_descreasing, coord_lengths): if decreasing: # indices[indices_slice] += (coord_length - 1) - indices[indices_slice] indices[indices_slice] *= -1 indices[indices_slice] += (coord_length - 1) # shape (N,) containing negative scalar cell numbers for each # input point (sequence doesn't matter so long as they are unique), or # -1 for points outside the grid. # # Possibly numpy.lexsort could be used to avoid the need for this, # although we'd have to be careful about points outside the grid. self.cell_numbers = np.where( grid_mask, np.tensordot( np.cumproduct((1,) + grid_shape[:-1]), indices, axes=1 ), -1) # Sort everything by cell number self.sort_order = np.argsort(self.cell_numbers) self.cell_numbers = self.cell_numbers[self.sort_order] self._indices = indices[:, self.sort_order] self.hp_coords = [hp_coord[self.sort_order] for hp_coord in hp_coords]
def make_regular_4d_ungridded_data(): """ Makes a well defined ungridded data object of shape 10x5 with data as follows data: [[ 1. 2. 3. 4. 5.] [ 6. 7. 8. 9. 10.] [ 11. 12. 13. 14. 15.] [ 16. 17. 18. 19. 20.] [ 21. 22. 23. 24. 25.] [ 26. 27. 28. 29. 30.] [ 31. 32. 33. 34. 35.] [ 36. 37. 38. 39. 40.] [ 41. 42. 43. 44. 45.] [ 46. 47. 48. 49. 50.]] latitude: [[-10. -5. 0. 5. 10.] [-10. -5. 0. 5. 10.] [-10. -5. 0. 5. 10.] [-10. -5. 0. 5. 10.] [-10. -5. 0. 5. 10.] [-10. -5. 0. 5. 10.] [-10. -5. 0. 5. 10.] [-10. -5. 0. 5. 10.] [-10. -5. 0. 5. 10.] [-10. -5. 0. 5. 10.]] longitude: [[-5. -2.5 0. 2.5 5. ] [-5. -2.5 0. 2.5 5. ] [-5. -2.5 0. 2.5 5. ] [-5. -2.5 0. 2.5 5. ] [-5. -2.5 0. 2.5 5. ] [-5. -2.5 0. 2.5 5. ] [-5. -2.5 0. 2.5 5. ] [-5. -2.5 0. 2.5 5. ] [-5. -2.5 0. 2.5 5. ] [-5. -2.5 0. 2.5 5. ]] altitude: [[ 0. 0. 0. 0. 0.] [ 10. 10. 10. 10. 10.] [ 20. 20. 20. 20. 20.] [ 30. 30. 30. 30. 30.] [ 40. 40. 40. 40. 40.] [ 50. 50. 50. 50. 50.] [ 60. 60. 60. 60. 60.] [ 70. 70. 70. 70. 70.] [ 80. 80. 80. 80. 80.] [ 90. 90. 90. 90. 90.]] pressure: [[ 4. 4. 4. 4. 4.] [ 16. 16. 16. 16. 16.] [ 20. 20. 20. 20. 20.] [ 30. 30. 30. 30. 30.] [ 40. 40. 40. 40. 40.] [ 50. 50. 50. 50. 50.] [ 60. 60. 60. 60. 60.] [ 70. 70. 70. 70. 70.] [ 80. 80. 80. 80. 80.] [ 90. 90. 90. 90. 90.]] time: [[1984-08-27 1984-08-28 1984-08-29 1984-08-30 1984-08-31] [1984-08-27 1984-08-28 1984-08-29 1984-08-30 1984-08-31] [1984-08-27 1984-08-28 1984-08-29 1984-08-30 1984-08-31] [1984-08-27 1984-08-28 1984-08-29 1984-08-30 1984-08-31] [1984-08-27 1984-08-28 1984-08-29 1984-08-30 1984-08-31] [1984-08-27 1984-08-28 1984-08-29 1984-08-30 1984-08-31] [1984-08-27 1984-08-28 1984-08-29 1984-08-30 1984-08-31] [1984-08-27 1984-08-28 1984-08-29 1984-08-30 1984-08-31] [1984-08-27 1984-08-28 1984-08-29 1984-08-30 1984-08-31] [1984-08-27 1984-08-28 1984-08-29 1984-08-30 1984-08-31]] They are shaped to represent a typical lidar type satelite data set. """ import numpy as np from cis.data_io.Coord import CoordList, Coord from cis.data_io.ungridded_data import UngriddedData, Metadata import datetime from cis.time_util import convert_obj_to_standard_date_array x_points = np.linspace(-10, 10, 5) y_points = np.linspace(-5, 5, 5) t0 = datetime.datetime(1984, 8, 27) times = convert_obj_to_standard_date_array(np.array([t0 + datetime.timedelta(days=d) for d in xrange(5)])) alt = np.linspace(0, 90, 10) data = np.reshape(np.arange(50) + 1.0, (10, 5)) # print np.mean(data[:,1:3]) # print np.mean(data[4:6,:]) # print np.mean(data[:,2]) # print np.std(data) # print np.mean(data) # print len(data.flat) y, a = np.meshgrid(y_points, alt) x, a = np.meshgrid(x_points, alt) t, a = np.meshgrid(times, alt) p = a p[0, :] = 4 p[1, :] = 16 a = Coord(a, Metadata(standard_name='altitude', units='meters')) x = Coord(x, Metadata(standard_name='latitude', units='degrees')) y = Coord(y, Metadata(standard_name='longitude', units='degrees')) p = Coord(p, Metadata(standard_name='air_pressure', units='Pa')) t = Coord(t, Metadata(standard_name='time', units='DateTime Object')) coords = CoordList([x, y, a, p, t]) return UngriddedData(data, Metadata(standard_name='rain', long_name="TOTAL RAINFALL RATE: LS+CONV KG/M2/S", units="kg m-2 s-1", missing_value=-999), coords)
def make_mock_cube(lat_dim_length=5, lon_dim_length=3, lon_range=None, alt_dim_length=0, pres_dim_length=0, time_dim_length=0, horizontal_offset=0, altitude_offset=0, pressure_offset=0, time_offset=0, data_offset=0, hybrid_ht_len=0, hybrid_pr_len=0, dim_order=None, mask=False): """ Makes a cube of any shape required, with coordinate offsets from the default available. If no arguments are given get a 5x3 cube of the form: array([[1,2,3], [4,5,6], [7,8,9], [10,11,12], [13,14,15]]) and coordinates in latitude: array([ -10, -5, 0, 5, 10 ]) longitude: array([ -5, 0, 5 ]) :param lat_dim_length: Latitude grid length :param lon_dim_length: Longitude grid length :param alt_dim_length: Altitude grid length :param pres_dim_length: Pressure grid length :param time_dim_length: Time grid length :param horizontal_offset: Offset from the default grid, in degrees, in lat and lon :param altitude_offset: Offset from the default grid in altitude :param pressure_offset: Offset from the default grid in pressure :param time_offset: Offset from the default grid in time :param data_offset: Offset from the default data values :param hybrid_ht_len: Hybrid height grid length :param hybrid_pr_len: Hybrid pressure grid length :param dim_order: List of 'lat', 'lon', 'alt', 'pres', 'time' in the order in which the dimensions occur :param mask: A mask to apply to the data, this should be either a scalar or the same shape as the data :return: A cube with well defined data. """ import iris from iris.aux_factory import HybridHeightFactory, HybridPressureFactory data_size = 1 DIM_NAMES = ['lat', 'lon', 'alt', 'pres', 'time', 'hybrid_ht', 'hybrid_pr'] dim_lengths = [lat_dim_length, lon_dim_length, alt_dim_length, pres_dim_length, time_dim_length, hybrid_ht_len, hybrid_pr_len] lon_range = lon_range or (-5., 5.) if dim_order is None: dim_order = list(DIM_NAMES) if any([True for d in dim_order if d not in DIM_NAMES]): raise ValueError("dim_order contains unrecognised name") for idx, dim in enumerate(DIM_NAMES): if dim_lengths[idx] == 0 and dim in dim_order: del dim_order[dim_order.index(dim)] coord_map = {} for idx, dim in enumerate(dim_order): coord_map[dim] = dim_order.index(dim) coord_list = [None] * len(coord_map) if lat_dim_length: coord_list[coord_map['lat']] = (DimCoord(np.linspace(-10., 10., lat_dim_length) + horizontal_offset, standard_name='latitude', units='degrees'), coord_map['lat']) data_size *= lat_dim_length if lon_dim_length: coord_list[coord_map['lon']] = ( DimCoord(np.linspace(lon_range[0], lon_range[1], lon_dim_length) + horizontal_offset, standard_name='longitude', units='degrees'), coord_map['lon']) data_size *= lon_dim_length if alt_dim_length: coord_list[coord_map['alt']] = (DimCoord(np.linspace(0., 7., alt_dim_length) + altitude_offset, standard_name='altitude', units='metres'), coord_map['alt']) data_size *= alt_dim_length if pres_dim_length: coord_list[coord_map['pres']] = (DimCoord(np.linspace(0., 7., pres_dim_length) + pressure_offset, standard_name='air_pressure', units='hPa'), coord_map['pres']) data_size *= pres_dim_length if time_dim_length: t0 = datetime.datetime(1984, 8, 27) times = np.array([t0 + datetime.timedelta(days=d + time_offset) for d in xrange(time_dim_length)]) time_nums = convert_obj_to_standard_date_array(times) time_bounds = None if time_dim_length == 1: time_bounds = convert_obj_to_standard_date_array(np.array([times[0] - datetime.timedelta(days=0.5), times[0] + datetime.timedelta(days=0.5)])) coord_list[coord_map['time']] = (DimCoord(time_nums, standard_name='time', units='days since 1600-01-01 00:00:00', bounds=time_bounds), coord_map['time']) data_size *= time_dim_length if hybrid_ht_len: coord_list[coord_map['hybrid_ht']] = (DimCoord(np.arange(hybrid_ht_len, dtype='i8') + 10, "model_level_number", units="1"), coord_map['hybrid_ht']) data_size *= hybrid_ht_len if hybrid_pr_len: coord_list[coord_map['hybrid_pr']] = (DimCoord(np.arange(hybrid_pr_len, dtype='i8'), "atmosphere_hybrid_sigma_pressure_coordinate", units="1"), coord_map['hybrid_pr']) data_size *= hybrid_pr_len data = np.reshape(np.arange(data_size) + data_offset + 1., tuple(len(i[0].points) for i in coord_list)) if mask: data = np.ma.asarray(data) data.mask = mask return_cube = Cube(data, dim_coords_and_dims=coord_list) if hybrid_ht_len: return_cube.add_aux_coord(iris.coords.AuxCoord(np.arange(hybrid_ht_len, dtype='i8') + 40, long_name="level_height", units="m"), coord_map['hybrid_ht']) return_cube.add_aux_coord(iris.coords.AuxCoord(np.arange(hybrid_ht_len, dtype='i8') + 50, long_name="sigma", units="1"), coord_map['hybrid_ht']) return_cube.add_aux_coord(iris.coords.AuxCoord( np.arange(lat_dim_length * lon_dim_length, dtype='i8').reshape(lat_dim_length, lon_dim_length) + 100, long_name="surface_altitude", units="m"), [coord_map['lat'], coord_map['lon']]) return_cube.add_aux_factory(HybridHeightFactory( delta=return_cube.coord("level_height"), sigma=return_cube.coord("sigma"), orography=return_cube.coord("surface_altitude"))) elif hybrid_pr_len: return_cube.add_aux_coord(iris.coords.AuxCoord(np.arange(hybrid_pr_len, dtype='i8') + 40, long_name="hybrid A coefficient at layer midpoints", units="Pa"), coord_map['hybrid_pr']) return_cube.add_aux_coord(iris.coords.AuxCoord(np.arange(hybrid_pr_len, dtype='f8') + 50, long_name="hybrid B coefficient at layer midpoints", units="1"), coord_map['hybrid_pr']) return_cube.add_aux_coord( iris.coords.AuxCoord(np.arange(lat_dim_length * lon_dim_length * time_dim_length, dtype='i8') .reshape(lat_dim_length, lon_dim_length, time_dim_length) * 100000, "surface_air_pressure", units="Pa"), [coord_map['lat'], coord_map['lon'], coord_map['time']]) return_cube.add_aux_coord(iris.coords.AuxCoord( np.arange(lat_dim_length * lon_dim_length * time_dim_length * hybrid_pr_len, dtype='i8') .reshape(lat_dim_length, lon_dim_length, time_dim_length, hybrid_pr_len) + 10, "altitude", long_name="Geopotential height at layer midpoints", units="meter"), [coord_map['lat'], coord_map['lon'], coord_map['time'], coord_map['hybrid_pr']]) return_cube.add_aux_factory(HybridPressureFactory( delta=return_cube.coord("hybrid A coefficient at layer midpoints"), sigma=return_cube.coord("hybrid B coefficient at layer midpoints"), surface_air_pressure=return_cube.coord("surface_air_pressure"))) for coord in return_cube.coords(dim_coords=True): if coord.bounds is None: coord.guess_bounds() return return_cube
def convert_datetime_to_standard_time(self): from cis.time_util import convert_obj_to_standard_date_array, cis_standard_time_unit self._data = convert_obj_to_standard_date_array(self.data) self.units = str(cis_standard_time_unit) self.metadata.calendar = cis_standard_time_unit.calendar