import numpy as np
import numba as nb
import xarray
from hdp.utils import add_history, get_version
TEMPERATURE_UNITS = ['degC', 'degK', 'degF', 'C', 'K', 'F']
HUMIDITY_UNITS = ["%", "g/g"]
[docs]
def kelvin_to_celsius(temp: xarray.DataArray) -> xarray.DataArray:
"""
Converts from temperature value from Kelvin to Celsius.
:param temp: Temperature DataArray in degrees Kelvin
:type temp: xarray.DataArray
:return: Temperature DataArray in degrees Celsius
:rtype: xarray.DataArray
"""
attrs = temp.attrs
temp -= 273.15
attrs["units"] = "degC"
temp.attrs = attrs
temp = add_history(temp, "HDP converted units from Kelvin to Celsius.")
return temp
[docs]
def fahrenheit_to_celsius(temp: xarray.DataArray) -> xarray.DataArray:
"""
Converts from temperature value from Fahrenheit to Celsius.
:param temp: Temperature DataArray in degrees Fahrenheit
:type temp: xarray.DataArray
:return: Temperature DataArray in degrees Celsius
:rtype: xarray.DataArray
"""
attrs = temp.attrs
temp = (temp - 32) / 1.8
attrs["units"] = "degC"
temp.attrs = attrs
temp = add_history(temp, "HDP converted units from Fahrenheit to Celsius.")
return temp
[docs]
def celsius_to_fahrenheit(temp: xarray.DataArray) -> xarray.DataArray:
"""
Converts from temperature value from Celsius to Fahrenheit.
:param temp: Temperature DataArray in degrees Celsius
:type temp: xarray.DataArray
:return: Temperature DataArray in degrees Fahrenheit
:rtype: xarray.DataArray
"""
attrs = temp.attrs
temp = (temp * 1.8) + 32
attrs["units"] = "degF"
temp.attrs = attrs
temp = add_history(temp, "HDP converted units from Celsius to Fahrenheit.")
return temp
@nb.vectorize([nb.float32(nb.float32, nb.float32)])
def heat_index(temp: float, rel_humid: float) -> float:
"""
Calculates heat index from temperature and relative humidity.
This function relies on a regression of the National Weather Service heat index.
It has an error of +/- 1.3 degrees Fahrenheit.
https://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml
:param temp: Temperature value in degrees Fahrenheit
:type temp: float
:param rel_humid: Relative humidity on scale from 0 (no moisture) to 100 (fully saturated).
:type rel_humid: float
:return: Heat index value in degrees Fahrenheit
:rtype: float
"""
hi = 0.5 * (temp + 61.0 + ((temp - 68.0)*1.2) + (rel_humid*0.094))
if hi > 80:
hi = -42.379
hi += 2.04901523*temp
hi += 10.14333127*rel_humid
hi += -0.22475541*temp*rel_humid
hi += -0.00683783*(temp**2)
hi += -0.05481717*(rel_humid**2)
hi += 0.00122874*(temp**2)*rel_humid
hi += 0.00085282*temp*(rel_humid**2)
hi += -0.00000199*((rel_humid*temp)**2)
if rel_humid < 13 and 80 <= temp <= 112:
hi -= ((13 - rel_humid)/4)*np.sqrt((np.abs(17 - np.abs(temp - 95))/17))
elif rel_humid > 85 and 80 <= temp <= 87:
hi += ((rel_humid - 85)/10) * ((87 - temp)/5)
return hi
def heat_index_map_wrapper(ds):
hi_da = xarray.apply_ufunc(heat_index,
ds["temp"].astype(np.float32),
ds["rh"].astype(np.float32),
dask="parallelized",
input_core_dims=[[], []],
output_core_dims=[[]],
output_dtypes=[np.float32],
dask_gufunc_kwargs={
'allow_rechunk': False
})
return hi_da
[docs]
def apply_heat_index(temp: xarray.DataArray, rh: xarray.DataArray) -> xarray.DataArray:
"""
Calculates heat index from temperature and relative humidity DataArrays leveraging Dask.
:param temp: Temperature DataArray in degrees Fahrenheit
:type temp: xarray.DataArray
:param rh: Relative humidity DataArray in percentage [0-100]
:type rh: xarray.DataArray
:return: Heat index value in degrees Fahrenheit
:rtype: xarray.DataArray
"""
assert temp.attrs["units"] == "degF"
assert rh.attrs["units"] == "%"
hi_da = heat_index_map_wrapper(
xarray.Dataset({
"temp": temp,
"rh": rh
}),
)
hi_da = hi_da.rename(f"{temp.name}_hi")
hi_da.attrs["baseline_variable"] = hi_da.name
hi_da = add_history(hi_da, f"Converted to heat index using '{rh.name}' relative humidity, renamed from '{temp.name}' to '{hi_da.name}'.")
return hi_da
[docs]
def convert_temp_units(temp_ds: xarray.DataArray) -> xarray.DataArray:
"""
Determines and converts temperature units to degrees Celsius.
:param temp_ds: Temperature DataArray with one of the supported units
:type temp_ds: xarray.DataArray
:return: Temperature DataArray in degrees Celsius
:rtype: xarray.DataArray
"""
if temp_ds.attrs["units"] == "K" or temp_ds.attrs["units"] == "degK":
temp_ds = kelvin_to_celsius(temp_ds)
elif temp_ds.attrs["units"] == "F" or temp_ds.attrs["units"] == "degF":
temp_ds = fahrenheit_to_celsius(temp_ds)
return temp_ds