# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# (C) British Crown Copyright 2017-2019 Met Office.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
"""Module containing feels like temperature calculation plugins"""
import numpy as np
from cf_units import Unit
from improver.psychrometric_calculations.psychrometric_calculations import (
WetBulbTemperature)
[docs]def calculate_wind_chill(temperature, wind_speed):
"""
Calculates the wind chill from 10 m wind speed and temperature based on
the wind chill temperature index from a linear regression equation detailed
in THE NEW WIND CHILL EQUIVALENT TEMPERATURE CHART, Osczevski and
Bluestein, 2005, table 2.
Args:
temperature (iris.cube.Cube):
Cube of air temperatures
wind_speed (iris.cube.Cube):
Cube of 10m wind speeds
Returns:
iris.cube.Cube:
Cube of wind chill temperatures. The units of wind chill will be the
same as the units of the temperature cube when it is input into the
function.
References:
Osczevski, R. and Bluestein, M. (2005). THE NEW WIND CHILL EQUIVALENT
TEMPERATURE CHART. Bulletin of the American Meteorological Society,
86(10), pp.1453-1458.
Osczevski, R. and Bluestein, M. (2008). Comments on Inconsistencies in
the New Windchill Chart at Low Wind Speeds. Journal of Applied
Meteorology and Climatology, 47(10), pp.2737-2738.
Science background:
The 2005 Osczevski and Bluestein paper outlines the research and the
assumptions made, and the 2008 paper clarifies poorly explained sections
of the first paper.
A brief summary of their assumptions are given below:
The model aims to determine a worst-case scenario of wind chill. The wind
speed "threshold" of 4.8 kph (1.34 m/s) stated in the 2005 papers does not
refer to a threshold placed on the input windspeed data, which has no upper
limit, but is the walking speed of an average person. This is therefore
used as the minimum wind speed in their wind chill computer model, because
even where wind speed is zero, a person would still experience wind chill
from the act of walking (the model assumes that the person is walking into
the wind). The model introduces a compensation factor where it assumes that
the wind speed at 1.5 m (face level) is 2/3 that measured at 10 m. It also
takes into account the thermal resistance of the skin on the human cheek
with the assumption that the face is the most exposed area of skin
during winter.
The equation outlined in their paper is also not the equation used in their
model (which was computationally expensive) but rather it is a linear
regression equation which mimics the output of their model where wind
speeds are greater than 3kph (0.8m/s) (clarified in the 2008 paper). The
assumption being that lower wind speeds are usually not measured or
reported accurately anyway.
"""
temp_units = temperature.copy().units
wind_units = wind_speed.copy().units
# convert temperature units
temperature.convert_units('celsius')
# convert wind speed to km/h
wind_speed.convert_units('km h-1')
eqn_component = (wind_speed.data)**0.16
wind_chill_data = (
13.12 + 0.6215 * temperature.data - 11.37 * eqn_component +
0.3965 * temperature.data * eqn_component).astype(np.float32)
wind_chill = temperature.copy(data=wind_chill_data)
wind_chill.rename("wind_chill")
wind_chill.convert_units(temp_units)
temperature.convert_units(temp_units)
wind_speed.convert_units(wind_units)
return wind_chill
[docs]def calculate_apparent_temperature(temperature, wind_speed,
relative_humidity, pressure):
"""
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 (iris.cube.Cube):
Cube of air temperatures
wind_speed (iris.cube.Cube):
Cube of 10m wind speeds
relative_humidity (iris.cube.Cube):
Cube of relative humidities
pressure (iris.cube.Cube):
Cube of air pressure
Returns:
iris.cube.Cube:
Cube of apparent temperatures. The units of apparent temperature
will be the same as the units of the temperature cube when it is input
into the function.
References:
Steadman, R. (1984). A Universal Scale of Apparent Temperature.
Journal of Climate and Applied Meteorology, 23(12), pp.1674-1687
"""
# take a copy of each cube's original units
temp_units = temperature.copy().units
wind_units = wind_speed.copy().units
pressure_units = pressure.copy().units
relative_humidity_units = relative_humidity.copy().units
# ensure units are correct
wind_speed.convert_units('m s-1')
pressure.convert_units('Pa')
relative_humidity.convert_units('1')
temperature.convert_units('K')
avp = temperature.copy()
avp.units = Unit('Pa')
# look up saturated vapour pressure
svp = WetBulbTemperature().lookup_svp(temperature.data)
# convert to SVP in air
svp = WetBulbTemperature().pressure_correct_svp(
svp, temperature.data, pressure.data)
# convert temperature units
temperature.convert_units('celsius')
# calculate actual vapour pressure
# and convert relative humidities to fractional values
avp_data = svp*relative_humidity.data
avp = avp.copy(data=avp_data)
avp.rename("actual_vapour_pressure")
avp.convert_units('kPa')
# calculate apparent temperature
apparent_temperature_data = (
-2.7 + 1.04 * temperature.data + 2.0 * avp.data -
0.65 * wind_speed.data).astype(np.float32)
apparent_temperature = temperature.copy(data=apparent_temperature_data)
apparent_temperature.rename("apparent_temperature")
apparent_temperature.convert_units(temp_units)
# convert units back to input units
temperature.convert_units(temp_units)
wind_speed.convert_units(wind_units)
pressure.convert_units(pressure_units)
relative_humidity.convert_units(relative_humidity_units)
return apparent_temperature
[docs]def calculate_feels_like_temperature(temperature, wind_speed,
relative_humidity, pressure):
"""
Calculates the feels like temperature using a combination of
the wind chill index and Steadman's apparent temperature equation with
the following method:
If temperature < 10 degress C: The feels like temperature is equal to
the wind chill.
If temperature > 20 degress C: The feels like temperature is equal to
the apparent temperature.
If 10 <= temperature <= 20 degrees C: A weighting (alpha) is calculated
in order to blend between the wind chill and the apparent temperature.
Args:
temperature (iris.cube.Cube):
Cube of air temperatures
wind_speed (iris.cube.Cube):
Cube of 10m wind speeds
relative_humidity (iris.cube.Cube):
Cube of relative humidities
pressure (iris.cube.Cube):
Cube of air pressure
Returns:
iris.cube.Cube:
Cube of feels like temperatures. The units of feels like temperature
will be the same as the units of the temperature cube when it is input
into the function.
"""
temp_units = temperature.units
# convert temperature units
temperature.convert_units('celsius')
wind_chill = calculate_wind_chill(temperature, wind_speed)
apparent_temperature = calculate_apparent_temperature(
temperature, wind_speed, relative_humidity, pressure)
t_data = temperature.data
feels_like_temperature_data = np.zeros(t_data.shape, dtype=np.float32)
# if temperature < 10 degrees Celsius:
feels_like_temperature_data[t_data < 10] = wind_chill.data[t_data < 10]
# if temperature >= 10 degrees Celsius and <= 20 degrees Celsius:
# calculate weighting and blend between wind chill index
# and Steadman equation
alpha = (t_data-10.0)/10.0
temp_flt = (alpha*apparent_temperature.data + ((1-alpha)*wind_chill.data))
t_data_between = (t_data >= 10) & (t_data <= 20)
feels_like_temperature_data[t_data_between] = temp_flt[t_data_between]
# if temperature > 20 Celsius:
feels_like_temperature_data[t_data > 20] = (
apparent_temperature.data[t_data > 20])
feels_like_temperature = temperature.copy(data=feels_like_temperature_data)
feels_like_temperature.rename("feels_like_temperature")
feels_like_temperature.convert_units(temp_units)
temperature.convert_units(temp_units)
return feels_like_temperature