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_region_land(self): """Test how the neighbour index changes when a land constraint is imposed. Here we expect to go 'west' to the first island of land which has an altitude of 5m. So we expect [1, 4, -3].""" plugin = NeighbourSelection( land_constraint=True, search_radius=2E5, 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) expected = [[[1, 4, -3]]] self.assertArrayEqual(result.data, expected)
def test_global_tied_case_nearest(self): """Test which neighbour is returned in an artificial case in which two neighbouring grid points are identically close. First with no constraints using the iris method. We put a site exactly half way between the two islands at -60 degrees longitude ([1, 4] and [4, 4] are equal distances either side of the site). This consistently returns the western island. Note that nudging the value to -59.9 will return the island to the east as expected.""" self.global_sites[0]["longitude"] = -60.0 plugin = NeighbourSelection() result = plugin.process(self.global_sites, self.global_orography, self.global_land_mask) expected = [[[2], [4], [2]]] self.assertArrayEqual(result.data, expected)
def test_region_land_minimum_dz(self): """Test how the neighbour index changes when a land constraint is imposed and a minimum height difference condition. Here we expect to go 'west' to the second band of land that we encounter, which has an altitude closer to that of the site. So we expect [0, 4, 1].""" plugin = NeighbourSelection( land_constraint=True, minimum_dz=True, search_radius=2E5, 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) expected = [[[0, 4, 1]]] self.assertArrayEqual(result.data, expected)
def test_region_nearest(self): """Test that a cube is returned, this time using the site list in which site coordinates are defined in metres in an equal areas projection. Neighbour coordinates of [2, 4] are expected, with a vertical displacement of 2.""" 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) expected = [[[2, 4, 2]]] self.assertIsInstance(result, iris.cube.Cube) self.assertArrayEqual(result.data, expected)
def test_use_of_unique_ids(self): """Test that the returned cube has the unique_id present when they are provided. These are stored as 8 digits encoded as strings, so zero-padding is expected.""" plugin = NeighbourSelection(unique_site_id_key="met_office_site_id") sites = self.global_sites + [self.global_sites[0].copy()] sites[0]["met_office_site_id"] = sites[0]["wmo_id"] sites[1]["wmo_id"] = None sites[1]["met_office_site_id"] = 353 expected = ["00000001", "00000353"] result = plugin.process(sites, self.global_orography, self.global_land_mask) self.assertArrayEqual( result.coord("met_office_site_id").points, expected)
def test_global_attribute(self): """Test that a cube is returned with an attribute identifying the grid domain.""" plugin = NeighbourSelection() result = plugin.process(self.global_sites, self.global_orography, self.global_land_mask) expected = { 'mosg__grid_domain': 'global', 'mosg__grid_type': 'standard', 'mosg__grid_version': '1.2.0', 'mosg__model_configuration': 'gl_det' } self.assertIsInstance(result, iris.cube.Cube) self.assertDictEqual(result.attributes, expected)
def test_wmo_ids(self): """Test that the returned cube has the wmo_ids present when they are available. Should be None when they are not provided. WMO IDs are stored as 5 digits encoded as strings, so zero-padding is expected.""" plugin = NeighbourSelection() sites = self.global_sites + [ self.global_sites[0].copy(), self.global_sites[0].copy(), ] sites[1].pop("wmo_id") sites[2]["wmo_id"] = None expected = ["00001", "None", "None"] result = plugin.process(sites, self.global_orography, self.global_land_mask) self.assertArrayEqual(result.coord("wmo_id").points, expected)
def test_global_returned_site_coordinates(self): """Test that the site coordinates in the returned neighbour cube are latitudes and longitudes. Here the input site list contains site coordinates defined in latitudes and longitudes, so they should be unchanged.""" latitude_expected = np.array([self.global_sites[0]["latitude"]], dtype=np.float32) longitude_expected = np.array([self.global_sites[0]["longitude"]], dtype=np.float32) plugin = NeighbourSelection() result = plugin.process(self.global_sites, self.global_orography, self.global_land_mask) self.assertIsNotNone(result.coord("latitude")) self.assertIsNotNone(result.coord("longitude")) self.assertArrayAlmostEqual( result.coord("latitude").points, latitude_expected) self.assertArrayAlmostEqual( result.coord("longitude").points, longitude_expected)
def test_region_attribute(self): """Test that a cube is returned with an attribute identifying the grid domain.""" 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) expected = { 'mosg__grid_domain': 'region', 'mosg__grid_type': 'standard', 'mosg__grid_version': '1.2.0', 'mosg__model_configuration': 'region_det' } self.assertIsInstance(result, iris.cube.Cube) self.assertDictEqual(result.attributes, expected)
def test_global_tied_case_nearest_land_min_dz(self): """Test which neighbour is returned in an artificial case in which two neighbouring grid points are identically close. Identical to the test above except for now with both a land constraint and minimum dz constraint. The neighbouring islands have been set to have the same vertical displacement as each other from the spot site. The neigbour is found using the KDTree. Using the KDTree the neighbour to the east is returned everytime the test is run.""" self.global_sites[0]['longitude'] = -60.0 self.global_sites[0]['altitude'] = 5. self.global_orography.data[4, 4] = 5. plugin = NeighbourSelection(land_constraint=True, search_radius=1E8, minimum_dz=True) result = plugin.process(self.global_sites, self.global_orography, self.global_land_mask) expected = [[[4, 4, 0]]] self.assertArrayEqual(result.data, expected)
def test_region_returned_site_coordinates(self): """Test that the site coordinates in the returned neighbour cube are latitudes and longitudes. Here the input site list contains site coordinates defined in metres in an equal areas projection.""" 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) latitude_expected = np.array([0], dtype=np.float32) longitude_expected = np.array([-0.359327], dtype=np.float32) self.assertIsNotNone(result.coord('latitude')) self.assertIsNotNone(result.coord('longitude')) self.assertArrayAlmostEqual(result.coord('latitude').points, latitude_expected) self.assertArrayAlmostEqual(result.coord('longitude').points, longitude_expected)
def test_global_dateline(self): """Test that for a global grid with a circular longitude coordinate, the code selects the nearest neighbour matching constraints even if it falls at the opposite edge of the grid. The spot site is nearest to grid point [6, 4], and the nearest land point is at [4, 4]. However by imposing a minimum vertical displacement constraint the code will return a point across the dateline at [0, 4]. We can be sure we have crossed the dateline by the fact that there is an island of land with the same vertical displacment to the spot site between the point and the grid point returned. Therefore, the short path must be across the dateline, rather than across this island travelling west.""" self.global_sites[0]['longitude'] = 64. self.global_sites[0]['altitude'] = 3. plugin = NeighbourSelection(land_constraint=True, minimum_dz=True, search_radius=1E8) result = plugin.process(self.global_sites, self.global_orography, self.global_land_mask) expected = [[[0, 4, 2]]] self.assertArrayEqual(result.data, expected)