def _calculate_apparent_temperature(
    temperature: ndarray,
    wind_speed: ndarray,
    relative_humidity: ndarray,
    pressure: ndarray,
) -> ndarray:
    """
    Calculates the apparent temperature from 10 m wind speed, temperature
    and actual vapour pressure using the linear regression equation
    for shade described in A Universal Scale of Apparent Temperature,
    Steadman, 1984, page 1686, table 5.

    The method used to determine the original values used for the regression
    equation takes into account many variables which are detailed in Steadman's
    paper.

    The paper calculates apparent temperature for wind speeds up to 20 m/s.
    Here, the apparent temperature regression equation has been used for all
    wind speeds.

    This function looks up a value for the saturation vapour pressure of
    water vapour using the temperature and a table of values. These tabulated
    values are found using lookup_svp and are corrected to the saturated
    vapour pressure in air using pressure_correct_svp, both functions are from
    the WetBulbTemperature plugin which makes use of the Goff-Gratch method.

    Args:
        temperature:
            Temperatures in degrees celsius
        wind_speed:
            10m wind speeds in metres per second
        relative_humidity:
            Relative humidities (fractional)
        pressure:
            Pressure in Pa

    Returns:
        Apparent temperatures in degrees celsius

    References:
        Steadman, R. (1984). A Universal Scale of Apparent Temperature.
        Journal of Climate and Applied Meteorology, 23(12), pp.1674-1687
    """
    t_kelvin = temperature + 273.15
    svp = calculate_svp_in_air(t_kelvin, pressure)
    avp = 0.001 * svp * relative_humidity
    apparent_temperature = (
        -2.7 + 1.04 * temperature + 2.0 * avp - 0.65 * wind_speed
    ).astype(np.float32)
    return apparent_temperature
Exemple #2
0
 def test_calculate_svp_in_air(self):
     """Test pressure-corrected SVP values"""
     expected = np.array([[0.01362905, 208.47170252, 25187.76423485]])
     result = calculate_svp_in_air(self.temperature, self.pressure)
     np.testing.assert_allclose(result, expected, rtol=1e-5, atol=1e-5)
Exemple #3
0
    def process(self, temperature, humidity, pressure, uwind, vwind,
                topography):
        """
        Calculate precipitation enhancement over orography on high resolution
        grid. Input diagnostics are all expected to be on the same grid, and
        are regridded to match the orography.

        Args:
            temperature (iris.cube.Cube):
                Temperature at top of boundary layer
            humidity (iris.cube.Cube):
                Relative humidity at top of boundary layer
            pressure (iris.cube.Cube):
                Pressure at top of boundary layer
            uwind (iris.cube.Cube):
                Positive eastward wind vector component at top of boundary
                layer
            vwind (iris.cube.Cube):
                Positive northward wind vector component at top of boundary
                layer
            topography (iris.cube.Cube):
                Height of topography above sea level on high resolution (1 km)
                UKPP domain grid

        Returns:
            iris.cube.Cube:
                Precipitation enhancement due to orography in m/s.
        """
        # check input variable cube coordinates match
        unmatched_coords = compare_coords(
            [temperature, pressure, humidity, uwind, vwind])

        if any(item.keys() for item in unmatched_coords):
            msg = 'Input cube coordinates {} are unmatched'
            raise ValueError(msg.format(unmatched_coords))

        # check one of the input variable cubes is a 2D spatial field (this is
        # equivalent to checking all cubes whose coords are matched above)
        msg = 'Require 2D fields as input; found {} dimensions'
        if temperature.ndim > 2:
            raise ValueError(msg.format(temperature.ndim))
        check_for_x_and_y_axes(temperature)

        # check the topography cube is a 2D spatial field
        if topography.ndim > 2:
            raise ValueError(msg.format(topography.ndim))
        check_for_x_and_y_axes(topography)

        # regrid variables to match topography and populate class instance
        self._regrid_and_populate(temperature, humidity, pressure,
                                  uwind, vwind, topography)

        # calculate saturation vapour pressure
        self.svp = calculate_svp_in_air(
            self.temperature.data, self.pressure.data)

        # calculate site-specific orographic enhancement
        point_orogenh_data = self._point_orogenh()

        # integrate upstream component
        grid_coord_km = self.topography.coord(axis='x').copy()
        grid_coord_km.convert_units('km')
        self.grid_spacing_km = (
            grid_coord_km.points[1] - grid_coord_km.points[0])

        orogenh_data = self._add_upstream_component(point_orogenh_data)

        # create data cubes on the two required output grids
        orogenh = self._create_output_cube(orogenh_data, temperature)

        return orogenh
Exemple #4
0
 def test_calculate_svp_in_air(self):
     """Test pressure-corrected SVP values"""
     expected = np.array([[0.01362905, 208.47170252, 25187.76423485]])
     result = calculate_svp_in_air(self.temperature, self.pressure)
     self.assertArrayAlmostEqual(result, expected)