Esempio n. 1
0
    def orography_derived_temperature_lapse_rate(self,
                                                 cube,
                                                 sites,
                                                 neighbours,
                                                 orography,
                                                 no_neighbours=9):
        """
        Crude lapse rate method that uses temperature variation and height
        variation across local nodes to derive lapse rate. Temperature vs.
        height data points are fitted with a least-squares method to determine
        the gradient.

        This method is highly prone to noise given the small number of points
        involved and the variable degree to which elevation changes across
        these points.

        Args:
        -----
        cube : iris.cube.Cube
            A cube of screen level temperatures at a single time.

        sites/neighbours/no_neighbours : See process() above.

        orography : numpy.array
            Array of orography data on a grid that corresponds to the grid of
            the diagnostic cube.

        Returns:
        --------
        iris.cube.Cube containing data extracted from the screen level
        temperature cube at spotdata sites which has then been adjusted using
        a temperature lapse rate calculated using local variations in
        temperature with orography.

        """
        def _local_lapse_rate(cube, orography, node_list):
            """
            Least-squares fit to local temperature and altitude data for grid
            points defined by node_list to calculate a local lapse rate.

            """
            y_data = cube.data[node_list]
            x_data = orography[node_list]
            matrix = np.stack([x_data, np.ones(len(x_data))], axis=0).T
            gradient, intercept = lstsq(matrix, y_data)[0]
            return [gradient, intercept]

        data = np.empty(shape=(len(sites)), dtype=float)

        for i_site, site in enumerate(sites.itervalues()):
            i, j = neighbours['i'][i_site], neighbours['j'][i_site]

            altitude = site['altitude']
            if np.isnan(altitude):
                msg = ('orography_derived_temperature_lapse_rate method '
                       'requires site to have an altitude. Leaving value '
                       'unchanged.')
                warnings.warn(msg)
                data[i_site] = cube.data[i, j]
                continue

            edgepoint = neighbours['edgepoint'][i_site]
            node_list = nearest_n_neighbours(i, j, no_neighbours)
            if edgepoint:
                node_list = node_edge_check(node_list, cube)

            llr = _local_lapse_rate(cube, orography, node_list)
            data[i_site] = llr[0] * altitude + llr[1]

        return self.make_cube(cube, data, sites)
Esempio n. 2
0
    def minimum_height_error_neighbour(self, cube, sites, orography,
                                       land_mask=None,
                                       default_neighbours=None,
                                       no_neighbours=9):
        """
        Find the horizontally nearest neighbour, then relax the conditions
        to find the neighbouring point in the "no_neighbours" nearest nodes to
        the input coordinate that minimises the height difference. This is
        typically used for temperature, where vertical displacement can be much
        more important that horizontal displacement in determining the
        conditions.

        A vertical displacement bias may be applied with the vertical_bias
        keyword; whether to prefer grid points above or below the site, or
        neither.

        A land constraint may be applied that requires a land grid point be
        selected for a site that is over land. Currently this is established
        by checking that the nearest grid point barring any other conditions
        is a land point. If a site is a sea point it will use the nearest
        neighbour as there should be no vertical displacement difference with
        other sea points.

        Args:
            cube/sites : See process() above.

            default_neighbours (numpy.array):
                An existing list of neighbours from which variations are made
                using specified options (e.g. land_constraint). If unset the
                fast_nearest_neighbour method will be used to build this list.

            orography (numpy.array):
                Array of orography data extracted from an iris.cube.Cube that
                corresponds to the grids on which all other input diagnostics
                will be provided.

            land_mask (numpy.array):
                Array of land_mask data extracted from an iris.cube.Cube that
                corresponds to the grids on which all other input diagnostics
                will be provided.

            no_neighbours (int):
                Number of grid points about the site to consider when relaxing
                the nearest neighbour condition. If unset this defaults to 9.
                e.g. consider a 5x5 grid of points -> no_neighbours = 25.

        Returns:
            neighbours (numpy.array):
                See process() above.

        """

        # Use the default nearest neighbour list as a starting point, and
        # if for some reason it is missing, recreate the list using the fast
        # method.
        if default_neighbours is None:
            neighbours = self.fast_nearest_neighbour(cube, sites,
                                                     orography=orography)
        else:
            neighbours = default_neighbours

        for i_site, site in enumerate(sites.values()):

            altitude = site['altitude']

            # If site altitude is set with np.nan this method cannot be used.
            if altitude == np.nan:
                continue

            i, j, dz_nearest = (neighbours['i'][i_site],
                                neighbours['j'][i_site],
                                neighbours['dz'][i_site])
            edgepoint = neighbours['edgepoint'][i_site]

            node_list = nearest_n_neighbours(i, j, no_neighbours)
            if edgepoint:
                node_list = node_edge_check(node_list, cube)

            if self.land_constraint:
                # Check that we are considering a land point and that at least
                # one neighbouring point is also land. If not no modification
                # is made to the nearest neighbour coordinates.

                neighbour_nodes = nearest_n_neighbours(i, j, no_neighbours,
                                                       exclude_self=True)
                if edgepoint:
                    neighbour_nodes = node_edge_check(neighbour_nodes, cube)
                if not land_mask[i, j] or not any(land_mask[neighbour_nodes]):
                    continue

                # Filter the node_list to keep only land points
                # (land_mask == 1).
                node_list = ConditionalListExtract('not_equal_to').process(
                    land_mask, node_list, 0)

            dzs = altitude - orography[node_list]
            dz_subset = apply_bias(self.vertical_bias, dzs)

            ij_min = index_of_minimum_difference(dzs, subset_list=dz_subset)
            i_min, j_min = list_entry_from_index(node_list, ij_min)
            dz_min = abs(altitude - orography[i_min, j_min])

            # Test to ensure that if multiple vertical displacements are the
            # same we don't select a more distant point because of array
            # ordering.
            if not np.isclose(dz_min, abs(dz_nearest)):
                neighbours[i_site] = i_min, j_min, dzs[ij_min], edgepoint

        return neighbours