def test_variation(self): """Test that two cubes with slightly different coordinates return different hashes.""" hash_input1 = set_up_variable_cube(np.zeros((3, 3)).astype(np.float32)) hash_input2 = hash_input1.copy() latitude = hash_input2.coord("latitude") latitude_values = latitude.points * 1.001 latitude = latitude.copy(points=latitude_values) hash_input2.remove_coord("latitude") hash_input2.add_dim_coord(latitude, 0) result1 = create_coordinate_hash(hash_input1) result2 = create_coordinate_hash(hash_input2) self.assertNotEqual(result1, result2)
def setUp(self): """Set up cubes for use in testing.""" data = np.ones(9).reshape(3, 3).astype(np.float32) self.reference_cube = set_up_variable_cube(data, spatial_grid="equalarea") self.cube1 = self.reference_cube.copy() self.cube2 = self.reference_cube.copy() self.unmatched_cube = set_up_variable_cube(data, spatial_grid="latlon") self.diagnostic_cube_hash = create_coordinate_hash(self.reference_cube) neighbours = np.array([[[0.0], [0.0], [0.0]]]) altitudes = np.array([0]) latitudes = np.array([0]) longitudes = np.array([0]) wmo_ids = np.array([0]) grid_attributes = ["x_index", "y_index", "vertical_displacement"] neighbour_methods = ["nearest"] self.neighbour_cube = build_spotdata_cube( neighbours, "grid_neighbours", 1, altitudes, latitudes, longitudes, wmo_ids, grid_attributes=grid_attributes, neighbour_methods=neighbour_methods, ) self.neighbour_cube.attributes[ "model_grid_hash"] = self.diagnostic_cube_hash
def test_basic(self): """Test the expected hash is returned for a given cube.""" hash_input = set_up_variable_cube(np.zeros((3, 3)).astype(np.float32)) result = create_coordinate_hash(hash_input) expected = "54812a6fed0f92fe75d180d63a6bd6c916407ea1e7e5fd32a5f20f86ea997fac" self.assertIsInstance(result, str) self.assertEqual(result, expected)
def test_basic(self): """Test the expected hash is returned for a given cube.""" hash_input = set_up_variable_cube(np.zeros((3, 3)).astype(np.float32)) result = create_coordinate_hash(hash_input) expected = "b26ca16d28f6e06ea4573fd745f55750c6dd93a06891f1b4ff0c6cd50585ac08" self.assertIsInstance(result, str) self.assertEqual(result, expected)
def test_global_attribute(self): """Test that a cube is returned with a model_grid_hash that matches that of the global input grids.""" expected = create_coordinate_hash(self.global_orography) plugin = NeighbourSelection() result = plugin.process(self.global_sites, self.global_orography, self.global_land_mask) self.assertIsInstance(result, iris.cube.Cube) self.assertEqual(result.attributes["model_grid_hash"], expected)
def test_region_attribute(self): """Test that a cube is returned with a model_grid_hash that matches that of the regional input grids.""" expected = create_coordinate_hash(self.region_orography) plugin = NeighbourSelection( site_coordinate_system=self.region_projection.as_cartopy_crs(), site_x_coordinate='projection_x_coordinate', site_y_coordinate='projection_y_coordinate') result = plugin.process(self.region_sites, self.region_orography, self.region_land_mask) self.assertIsInstance(result, iris.cube.Cube) self.assertEqual(result.attributes['model_grid_hash'], expected)
def test_probability_cube(self): """Ensure that the plugin exits with value error if the spot data cube is in probability space. """ diagnostic_cube_hash = create_coordinate_hash(self.lapse_rate_cube) data = np.ones((3, 3, 3), dtype=np.float32) threshold_points = np.array([276, 277, 278], dtype=np.float32) probability_cube = set_up_probability_cube( data, threshold_points, spp__relative_to_threshold="above") probability_cube.attributes["model_grid_hash"] = diagnostic_cube_hash plugin = SpotLapseRateAdjust() msg = ("Input cube has a probability coordinate which cannot be lapse " "rate adjusted. Input data should be in percentile or " "deterministic space only.") with self.assertRaisesRegex(ValueError, msg): plugin(probability_cube, self.neighbour_cube, self.lapse_rate_cube)
def _get_grid_hash(cube): try: cube_hash = cube.attributes["model_grid_hash"] except KeyError: cube_hash = create_coordinate_hash(cube) return cube_hash
def setUp(self): """ Set up cubes and sitelists for use in testing SpotExtraction. The envisaged scenario is an island (1) surrounded by water (0). Land-sea Orography Diagnostic 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 0 1 1 1 0 0 1 2 1 0 5 6 7 8 9 0 1 1 1 0 0 2 3 2 0 10 11 12 13 14 0 1 1 1 0 0 1 2 1 0 15 16 17 18 19 0 0 0 0 0 0 0 0 0 0 20 21 22 23 24 """ # Set up diagnostic data cube and neighbour cubes. diagnostic_data = np.arange(25).reshape(5, 5) # Grid attributes must be included in diagnostic cubes so their removal # can be tested attributes = { "mosg__grid_domain": "global", "mosg__grid_type": "standard" } self.cell_methods = (iris.coords.CellMethod("maximum", coords="time", intervals="1 hour"), ) time = dt(2020, 6, 15, 12) frt = time - timedelta(hours=6) diagnostic_cube_yx = set_up_variable_cube( diagnostic_data.T, name="air_temperature", units="K", attributes=attributes, domain_corner=(0, 0), grid_spacing=10, time=time, frt=frt, ) diagnostic_cube_yx.cell_methods = self.cell_methods diagnostic_cube_xy = diagnostic_cube_yx.copy() enforce_coordinate_ordering( diagnostic_cube_xy, [ diagnostic_cube_xy.coord(axis="x").name(), diagnostic_cube_xy.coord(axis="y").name(), ], anchor_start=False, ) times = np.array([time + timedelta(hours=i) for i in range(-3, 2)]) # Create as int64 values time_points = np.array([_create_time_point(time) for time in times]) bounds = np.array([[ _create_time_point(time - timedelta(hours=1)), _create_time_point(time), ] for time in times]) # Broadcast the times to a 2-dimensional grid that matches the diagnostic # data grid time_points = np.broadcast_to(time_points, (5, 5)) bounds = np.broadcast_to(bounds, (5, 5, 2)) # Create a 2-dimensional auxiliary time coordinate self.time_aux_coord = iris.coords.AuxCoord( time_points, "time", bounds=bounds, units=TIME_COORDS["time"].units) diagnostic_cube_2d_time = diagnostic_cube_yx.copy() diagnostic_cube_2d_time.coord("time").rename("time_in_local_timezone") diagnostic_cube_2d_time.add_aux_coord(self.time_aux_coord, data_dims=(0, 1)) expected_indices = [[0, 0], [0, 0], [2, 2], [2, 2]] points = [ self.time_aux_coord.points[y, x] for y, x in expected_indices ] bounds = [ self.time_aux_coord.bounds[y, x] for y, x in expected_indices ] self.expected_spot_time_coord = self.time_aux_coord.copy(points=points, bounds=bounds) diagnostic_cube_hash = create_coordinate_hash(diagnostic_cube_yx) # neighbours, each group is for a point under two methods, e.g. # [ 0. 0. 0.] is the nearest point to the first spot site, whilst # [ 1. 1. -1.] is the nearest land point to the same site. neighbours = np.array([ [[0.0, 0.0, 2.0, 2.0], [0.0, 0.0, 2.0, 2.0], [0.0, -1.0, 0.0, 1.0]], [[1.0, 1.0, 2.0, 2.0], [1.0, 1.0, 2.0, 2.0], [-1.0, 0.0, 0.0, 1.0]], ]) self.altitudes = np.array([0, 1, 3, 2]) self.latitudes = np.array([10, 10, 20, 20]) self.longitudes = np.array([10, 10, 20, 20]) self.wmo_ids = np.arange(4) self.unique_site_id = np.arange(4) self.unique_site_id_key = "met_office_site_id" grid_attributes = ["x_index", "y_index", "vertical_displacement"] neighbour_methods = ["nearest", "nearest_land"] neighbour_cube = build_spotdata_cube( neighbours, "grid_neighbours", 1, self.altitudes, self.latitudes, self.longitudes, self.wmo_ids, unique_site_id=self.unique_site_id, unique_site_id_key=self.unique_site_id_key, grid_attributes=grid_attributes, neighbour_methods=neighbour_methods, ) neighbour_cube.attributes["model_grid_hash"] = diagnostic_cube_hash coordinate_cube = neighbour_cube.extract( iris.Constraint(neighbour_selection_method_name="nearest") & iris.Constraint(grid_attributes_key=["x_index", "y_index"])) coordinate_cube.data = np.rint(coordinate_cube.data).astype(int) self.diagnostic_cube_xy = diagnostic_cube_xy self.diagnostic_cube_yx = diagnostic_cube_yx self.diagnostic_cube_2d_time = diagnostic_cube_2d_time self.neighbours = neighbours self.neighbour_cube = neighbour_cube self.coordinate_cube = coordinate_cube self.expected_attributes = self.diagnostic_cube_xy.attributes for attr in MOSG_GRID_ATTRIBUTES: self.expected_attributes.pop(attr, None) self.expected_attributes["title"] = "unknown" self.expected_attributes[ "model_grid_hash"] = self.neighbour_cube.attributes[ "model_grid_hash"]
def setUp(self): """ Set up cubes for use in testing SpotLapseRateAdjust. Inputs are envisaged as follows: Gridded Lapse rate Orography Temperatures (not used directly) (x DALR) A B C A B C A B C a 2 1 1 1 1 1 270 270 270 b 1 2 1 1 4 1 270 280 270 c 1 1 2 1 1 1 270 270 270 Spot (note the neighbours are identified with the A-C, a-c indices above) Site Temperature Altitude Nearest DZ MinDZ DZ neighbour neighbour 0 280 3 Ac 2 Bb -1 1 270 4 Bb 0 Bb 0 2 280 0 Ca -1 Ca -1 """ # Set up lapse rate cube lapse_rate_data = np.ones(9).reshape(3, 3).astype(np.float32) * DALR lapse_rate_data[0, 2] = 2 * DALR lapse_rate_data[1, 1] = 2 * DALR lapse_rate_data[2, 0] = 2 * DALR self.lapse_rate_cube = set_up_variable_cube(lapse_rate_data, name="lapse_rate", units="K m-1", spatial_grid="equalarea") diagnostic_cube_hash = create_coordinate_hash(self.lapse_rate_cube) # Set up neighbour and spot diagnostic cubes y_coord, x_coord = construct_xy_coords(3, 3, "equalarea") y_coord = y_coord.points x_coord = x_coord.points # neighbours, each group is for a point under two methods, e.g. # [ 0. 0. 0.] is the nearest point to the first spot site, whilst # [ 1. 1. -1.] is the nearest point with minimum height difference. neighbours = np.array([[[0., 0., 2.], [1., 1., -1.]], [[1., 1., 0.], [1., 1., 0.]], [[2., 2., -1.], [2., 2., -1.]]]) altitudes = np.array([3, 4, 0]) latitudes = np.array([y_coord[0], y_coord[1], y_coord[2]]) longitudes = np.array([x_coord[0], x_coord[1], x_coord[2]]) wmo_ids = np.arange(3) grid_attributes = ['x_index', 'y_index', 'vertical_displacement'] neighbour_methods = ['nearest', 'nearest_minimum_dz'] self.neighbour_cube = build_spotdata_cube( neighbours, 'grid_neighbours', 1, altitudes, latitudes, longitudes, wmo_ids, grid_attributes=grid_attributes, neighbour_methods=neighbour_methods) self.neighbour_cube.attributes['model_grid_hash'] = ( diagnostic_cube_hash) time, = iris_time_to_datetime(self.lapse_rate_cube.coord("time")) frt, = iris_time_to_datetime( self.lapse_rate_cube.coord("forecast_reference_time")) time_bounds = None time_coords = construct_scalar_time_coords(time, time_bounds, frt) time_coords = [item[0] for item in time_coords] # This temperature cube is set up with the spot sites having obtained # their temperature values from the nearest grid sites. temperatures_nearest = np.array([280, 270, 280]) self.spot_temperature_nearest = build_spotdata_cube( temperatures_nearest, 'air_temperature', 'K', altitudes, latitudes, longitudes, wmo_ids, scalar_coords=time_coords) self.spot_temperature_nearest.attributes['model_grid_hash'] = ( diagnostic_cube_hash) # This temperature cube is set up with the spot sites having obtained # their temperature values from the nearest minimum vertical # displacment grid sites. The only difference here is for site 0, which # now gets its temperature from Bb (see doc-string above). temperatures_mindz = np.array([270, 270, 280]) self.spot_temperature_mindz = build_spotdata_cube( temperatures_mindz, 'air_temperature', 'K', altitudes, latitudes, longitudes, wmo_ids, scalar_coords=time_coords) self.spot_temperature_mindz.attributes['model_grid_hash'] = ( diagnostic_cube_hash)
def setUp(self): """ Set up cubes and sitelists for use in testing SpotExtraction. The envisaged scenario is an island (1) surrounded by water (0). Land-sea Orography Diagnostic 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 0 1 1 1 0 0 1 2 1 0 5 6 7 8 9 0 1 1 1 0 0 2 3 2 0 10 11 12 13 14 0 1 1 1 0 0 1 2 1 0 15 16 17 18 19 0 0 0 0 0 0 0 0 0 0 20 21 22 23 24 """ # Set up diagnostic data cube and neighbour cubes. diagnostic_data = np.arange(25).reshape(5, 5) xcoord = iris.coords.DimCoord(np.linspace(0, 40, 5), standard_name="longitude", units="degrees") ycoord = iris.coords.DimCoord(np.linspace(0, 40, 5), standard_name="latitude", units="degrees") # Grid attributes must be included in diagnostic cubes so their removal # can be tested attributes = { "mosg__grid_domain": "global", "mosg__grid_type": "standard" } self.cell_methods = (iris.coords.CellMethod("maximum", coords="time", intervals="1 hour"), ) diagnostic_cube_xy = iris.cube.Cube( diagnostic_data, standard_name="air_temperature", units="K", dim_coords_and_dims=[(ycoord, 1), (xcoord, 0)], attributes=attributes, cell_methods=self.cell_methods, ) diagnostic_cube_yx = iris.cube.Cube( diagnostic_data.T, standard_name="air_temperature", units="K", dim_coords_and_dims=[(ycoord, 0), (xcoord, 1)], attributes=attributes, cell_methods=self.cell_methods, ) diagnostic_cube_hash = create_coordinate_hash(diagnostic_cube_yx) # neighbours, each group is for a point under two methods, e.g. # [ 0. 0. 0.] is the nearest point to the first spot site, whilst # [ 1. 1. -1.] is the nearest land point to the same site. neighbours = np.array([ [[0.0, 0.0, 2.0, 2.0], [0.0, 0.0, 2.0, 2.0], [0.0, -1.0, 0.0, 1.0]], [[1.0, 1.0, 2.0, 2.0], [1.0, 1.0, 2.0, 2.0], [-1.0, 0.0, 0.0, 1.0]], ]) self.altitudes = np.array([0, 1, 3, 2]) self.latitudes = np.array([10, 10, 20, 20]) self.longitudes = np.array([10, 10, 20, 20]) self.wmo_ids = np.arange(4) self.unique_site_id = np.arange(4) self.unique_site_id_key = "met_office_site_id" grid_attributes = ["x_index", "y_index", "vertical_displacement"] neighbour_methods = ["nearest", "nearest_land"] neighbour_cube = build_spotdata_cube( neighbours, "grid_neighbours", 1, self.altitudes, self.latitudes, self.longitudes, self.wmo_ids, unique_site_id=self.unique_site_id, unique_site_id_key=self.unique_site_id_key, grid_attributes=grid_attributes, neighbour_methods=neighbour_methods, ) neighbour_cube.attributes["model_grid_hash"] = diagnostic_cube_hash coordinate_cube = neighbour_cube.extract( iris.Constraint(neighbour_selection_method_name="nearest") & iris.Constraint(grid_attributes_key=["x_index", "y_index"])) coordinate_cube.data = np.rint(coordinate_cube.data).astype(int) self.diagnostic_cube_xy = diagnostic_cube_xy self.diagnostic_cube_yx = diagnostic_cube_yx self.neighbours = neighbours self.neighbour_cube = neighbour_cube self.coordinate_cube = coordinate_cube self.expected_attributes = self.diagnostic_cube_xy.attributes for attr in MOSG_GRID_ATTRIBUTES: self.expected_attributes.pop(attr, None) self.expected_attributes["title"] = "unknown" self.expected_attributes[ "model_grid_hash"] = self.neighbour_cube.attributes[ "model_grid_hash"]
def process( self, sites: List[Dict[str, Any]], orography: Cube, land_mask: Cube ) -> Cube: """ Using the constraints provided, find the nearest grid point neighbours to the given spot sites for the model/grid given by the input cubes. Returned is a cube that contains the defining characteristics of the spot sites (e.g. x coordinate, y coordinate, altitude) and the indices of the selected grid point neighbour. Args: sites: A list of dictionaries defining the spot sites for which neighbours are to be found. e.g.: [{'altitude': 11.0, 'latitude': 57.867000579833984, 'longitude': -5.632999897003174, 'wmo_id': 3034}] orography: A cube of orography, used to obtain the grid point altitudes. land_mask: A land mask cube for the model/grid from which grid point neighbours are being selected, with land points set to one and sea points set to zero. Returns: A cube containing both the spot site information and for each the grid point indices of its nearest neighbour as per the imposed constraints. """ # Check if we are dealing with a global grid. self.global_coordinate_system = orography.coord(axis="x").circular # Exclude regional grids with spatial dimensions other than metres. if not self.global_coordinate_system: if not orography.coord(axis="x").units == "metres": msg = ( "Cube spatial coordinates for regional grids must be" "in metres to match the defined search_radius." ) raise ValueError(msg) # Ensure land_mask and orography are on the same grid. if not orography.dim_coords == land_mask.dim_coords: msg = "Orography and land_mask cubes are not on the same " "grid." raise ValueError(msg) # Enforce x-y coordinate order for input cubes. enforce_coordinate_ordering( orography, [orography.coord(axis="x").name(), orography.coord(axis="y").name()], ) enforce_coordinate_ordering( land_mask, [land_mask.coord(axis="x").name(), land_mask.coord(axis="y").name()], ) # Remap site coordinates on to coordinate system of the model grid. site_x_coords = np.array([site[self.site_x_coordinate] for site in sites]) site_y_coords = np.array([site[self.site_y_coordinate] for site in sites]) site_coords = self._transform_sites_coordinate_system( site_x_coords, site_y_coords, orography.coord_system().as_cartopy_crs() ) # Exclude any sites falling outside the domain given by the cube and # notify the user. ( sites, site_coords, site_x_coords, site_y_coords, ) = self.check_sites_are_within_domain( sites, site_coords, site_x_coords, site_y_coords, orography ) # Find nearest neighbour point using quick iris method. nearest_indices = self.get_nearest_indices(site_coords, orography) # Create an array containing site altitudes, using the nearest point # orography height for any that are unset. site_altitudes = np.array( [site.get(self.site_altitude, None) for site in sites] ) site_altitudes = np.where( np.isnan(site_altitudes.astype(float)), orography.data[tuple(nearest_indices.T)], site_altitudes, ) # If further constraints are being applied, build a KD Tree which # includes points filtered by constraint. if self.land_constraint or self.minimum_dz: # Build the KDTree, an internal test for the land_constraint checks # whether to exclude sea points from the tree. tree, index_nodes = self.build_KDTree(land_mask) # Site coordinates made cartesian for global coordinate system if self.global_coordinate_system: site_coords = self.geocentric_cartesian( orography, site_coords[:, 0], site_coords[:, 1] ) if not self.minimum_dz: # Query the tree for the nearest neighbour, in this case a land # neighbour is returned along with the distance to it. distances, node_indices = tree.query([site_coords]) # Look up the grid coordinates that correspond to the tree node (land_neighbour_indices,) = index_nodes[node_indices] # Use the found land neighbour if it is within the # search_radius, otherwise use the nearest neighbour. distances = np.array([distances[0], distances[0]]).T nearest_indices = np.where( distances < self.search_radius, land_neighbour_indices, nearest_indices, ) else: # Query the tree for self.node_limit nearby neighbours. distances, node_indices = tree.query( [site_coords], distance_upper_bound=self.search_radius, k=self.node_limit, ) # Loop over the sites and for each choose the returned # neighbour with the minimum vertical displacement. for index, (distance, indices) in enumerate( zip(distances[0], node_indices[0]) ): grid_point = self.select_minimum_dz( orography, site_altitudes[index], index_nodes, distance, indices ) # None is returned if the tree query returned no neighbours # within the search radius. if grid_point is not None: nearest_indices[index] = grid_point # Calculate the vertical displacements between the chosen grid point # and the spot site. vertical_displacements = ( site_altitudes - orography.data[tuple(nearest_indices.T)] ) # Create a list of WMO IDs if available. These are stored as strings # to accommodate the use of 'None' for unset IDs. wmo_ids = [str(site.get("wmo_id", None)) for site in sites] # Construct a name to describe the neighbour finding method employed method_name = self.neighbour_finding_method_name() # Create an array of indices and displacements to return data = np.stack( (nearest_indices[:, 0], nearest_indices[:, 1], vertical_displacements), axis=0, ) data = np.expand_dims(data, 0).astype(np.float32) # Regardless of input sitelist coordinate system, the site coordinates # are stored as latitudes and longitudes in the neighbour cube. if self.site_coordinate_system != ccrs.PlateCarree(): lon_lats = self._transform_sites_coordinate_system( site_x_coords, site_y_coords, ccrs.PlateCarree() ) longitudes = lon_lats[:, 0] latitudes = lon_lats[:, 1] else: longitudes = site_x_coords latitudes = site_y_coords # Create a cube of neighbours neighbour_cube = build_spotdata_cube( data, "grid_neighbours", 1, site_altitudes.astype(np.float32), latitudes.astype(np.float32), longitudes.astype(np.float32), wmo_ids, neighbour_methods=[method_name], grid_attributes=["x_index", "y_index", "vertical_displacement"], ) # Add a hash attribute based on the model grid to ensure the neighbour # cube is only used with a compatible grid. grid_hash = create_coordinate_hash(orography) neighbour_cube.attributes["model_grid_hash"] = grid_hash return neighbour_cube
def setUp(self): """ Set up cubes and sitelists for use in testing SpotExtraction. The envisaged scenario is an island (1) surrounded by water (0). Land-sea Orography Diagnostic 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 0 1 1 1 0 0 1 2 1 0 5 6 7 8 9 0 1 1 1 0 0 2 3 2 0 10 11 12 13 14 0 1 1 1 0 0 1 2 1 0 15 16 17 18 19 0 0 0 0 0 0 0 0 0 0 20 21 22 23 24 """ # Set up diagnostic data cube and neighbour cubes. diagnostic_data = np.arange(25).reshape(5, 5) xcoord = iris.coords.DimCoord(np.linspace(0, 40, 5), standard_name='longitude', units='degrees') ycoord = iris.coords.DimCoord(np.linspace(0, 40, 5), standard_name='latitude', units='degrees') # Grid attributes must be included in diagnostic cubes so their removal # can be tested attributes = { 'mosg__grid_domain': 'global', 'mosg__grid_type': 'standard' } diagnostic_cube_xy = iris.cube.Cube(diagnostic_data, standard_name="air_temperature", units='K', dim_coords_and_dims=[(ycoord, 1), (xcoord, 0)], attributes=attributes) diagnostic_cube_yx = iris.cube.Cube(diagnostic_data.T, standard_name="air_temperature", units='K', dim_coords_and_dims=[(ycoord, 0), (xcoord, 1)], attributes=attributes) diagnostic_cube_hash = create_coordinate_hash(diagnostic_cube_yx) # neighbours, each group is for a point under two methods, e.g. # [ 0. 0. 0.] is the nearest point to the first spot site, whilst # [ 1. 1. -1.] is the nearest land point to the same site. neighbours = np.array([[[0., 0., 0.], [1., 1., -1.]], [[0., 0., -1.], [1., 1., 0.]], [[2., 2., 0.], [2., 2., 0.]], [[2., 2., 1.], [2., 2., 1.]]]) altitudes = np.array([0, 1, 3, 2]) latitudes = np.array([10, 10, 20, 20]) longitudes = np.array([10, 10, 20, 20]) wmo_ids = np.arange(4) grid_attributes = ['x_index', 'y_index', 'vertical_displacement'] neighbour_methods = ['nearest', 'nearest_land'] neighbour_cube = build_spotdata_cube( neighbours, 'grid_neighbours', 1, altitudes, latitudes, longitudes, wmo_ids, grid_attributes=grid_attributes, neighbour_methods=neighbour_methods) neighbour_cube.attributes['model_grid_hash'] = diagnostic_cube_hash coordinate_cube = neighbour_cube.extract( iris.Constraint(neighbour_selection_method_name='nearest') & iris.Constraint(grid_attributes_key=['x_index', 'y_index'])) coordinate_cube.data = np.rint(coordinate_cube.data).astype(int) self.latitudes = latitudes self.longitudes = longitudes self.diagnostic_cube_xy = diagnostic_cube_xy self.diagnostic_cube_yx = diagnostic_cube_yx self.neighbours = neighbours self.neighbour_cube = neighbour_cube self.coordinate_cube = coordinate_cube self.expected_attributes = self.diagnostic_cube_xy.attributes for attr in MOSG_GRID_ATTRIBUTES: self.expected_attributes.pop(attr, None) self.expected_attributes["title"] = "unknown" self.expected_attributes["model_grid_hash"] = ( self.neighbour_cube.attributes['model_grid_hash'])