Пример #1
0
    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)
Пример #2
0
    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.]]])
        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)
Пример #3
0
    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 = "fd40f6d5a8e0a347f181d87bcfd445fa"
        self.assertIsInstance(result, str)
        self.assertEqual(result, expected)
Пример #4
0
    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)
Пример #5
0
    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 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)
Пример #7
0
 def _get_grid_hash(cube):
     try:
         cube_hash = cube.attributes['model_grid_hash']
     except KeyError:
         cube_hash = create_coordinate_hash(cube)
     return cube_hash
Пример #8
0
    def process(self, sites, orography, land_mask):
        """
        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 (list of dict):
                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 (iris.cube.Cube):
                A cube of orography, used to obtain the grid point altitudes.
            land_mask (iris.cube.Cube):
                A land mask cube for the model/grid from which grid point
                neighbours are being selected.
        Returns:
            neighbour_cube (iris.cube.Cube):
                A cube containing both the spot site information and for each
                the grid point indices of its nearest neighbour as per the
                imposed constraints.
        """
        index_nodes = []
        # 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.
        orography = enforce_coordinate_ordering(
            orography, [orography.coord(axis='x').name(),
                        orography.coord(axis='y').name()])
        land_mask = 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)

        # 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=1)
        data = np.expand_dims(data, 1).astype(np.float32)

        # Create a cube of neighbours
        neighbour_cube = build_spotdata_cube(
            data, 'grid_neighbours', 1, site_altitudes.astype(np.float32),
            site_y_coords.astype(np.float32), site_x_coords.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
Пример #9
0
    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')

        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