from collections.abc import Iterable
from typing import overload
from typing import TypeVar
from typing import Union
import numpy as np
import numpy.typing as npt
from ._thermal_comfort import thermal_comfort_mod
T = TypeVar('T', bound=Union[np.floating, np.integer])
# autopep8: off
@overload
def mean_radiant_temp(
ta: float,
tg: float,
v: float,
d: float = 0.15,
e: float = 0.95,
) -> float: ...
@overload
def mean_radiant_temp(
ta: Iterable[float],
tg: Iterable[float],
v: Iterable[float],
d: Union[Iterable[float], float] = 0.15,
e: Union[Iterable[float], float] = 0.95,
) -> npt.NDArray[T]: ...
@overload
def mean_radiant_temp(
ta: npt.NDArray[T],
tg: npt.NDArray[T],
v: npt.NDArray[T],
d: Union[npt.NDArray[T], float] = 0.15,
e: Union[npt.NDArray[T], float] = 0.95,
) -> npt.NDArray[T]: ...
# autopep8: on
[docs]
def mean_radiant_temp(
ta: Union[npt.NDArray[T], float, Iterable[float]],
tg: Union[npt.NDArray[T], float, Iterable[float]],
v: Union[npt.NDArray[T], float, Iterable[float]],
d: Union[npt.NDArray[T], float, Iterable[float]] = 0.15,
e: Union[npt.NDArray[T], float, Iterable[float]] = 0.95,
) -> Union[npt.NDArray[T], float]:
"""
Calculate the mean radiant temperature based on DIN EN ISO 7726.
Based on the air velocity, this function will decide whether to use the
natural or forced convection.
Calculate hcg (the coefficient of heat transfer) for both natural and forced
convection. Check which one is higher and use that (defined in Section B.2.3)
This function performs better for larger arrays. For smaller arrays, the
numpy-based function outperforms this function.
:param ta: air temperature in °C
:param tg: black globe temperature in °C
:param v: air velocity in m/s
:param d: diameter of the black globe in m (default 0.15 m)
:param e: emissivity of the black globe (default 0.95)
:returns: returns the mean radiant temperature in °C
"""
tg = np.array(tg)
v = np.array(v)
ta = np.array(ta)
# check if we're using the default values for d and e
if isinstance(d, float):
d = np.full_like(tg, d, dtype=float)
else:
d = np.array(d)
if isinstance(e, float):
e = np.full_like(tg, e, dtype=float)
else:
e = np.array(e)
# 1. check for correct shape
if not (
tg.ndim <= 1 and v.ndim <= 1 and ta.ndim <= 1 and
d.ndim <= 1 and e.ndim <= 1
):
raise TypeError(
'Only arrays with one dimension are allowed. '
'Please reshape your array accordingly',
)
# 2. check for same length
if not (tg.size == v.size == ta.size == d.size == e.size):
raise ValueError('All arrays must have the same length')
# 3. check for value ranges
if np.any(d <= 0):
raise ValueError('The globe diameter (d) must be positive')
if np.any((e < 0) | (e > 1)):
raise ValueError('The emissivity (e) must be between 0 and 1')
result = thermal_comfort_mod.mean_radiant_temp(tg=tg, v=v, ta=ta, d=d, e=e)
# check if we have a single value
if result.size == 1:
return result.item()
else:
return result
# autopep8: off
@overload
def wet_bulb_temp(
ta: float,
rh: float,
) -> float: ...
@overload
def wet_bulb_temp(
ta: Iterable[float],
rh: Iterable[float],
) -> npt.NDArray[T]: ...
@overload
def wet_bulb_temp(
ta: npt.NDArray[T],
rh: npt.NDArray[T],
) -> npt.NDArray[T]: ...
# autopep8: on
[docs]
def wet_bulb_temp(
ta: Union[npt.NDArray[T], float, Iterable[float]],
rh: Union[npt.NDArray[T], float, Iterable[float]],
) -> Union[npt.NDArray[T], float]:
"""Calculate the wet bulb temperature following the Stull (2011) equation
:param ta: air temperature in °C
:param rh: relative humidity in %
:returns: returns the wet bulb temperature in °C
**References**
- Stull, R., 2011. Wet-Bulb Temperature from Relative Humidity and
Air Temperature. J. Appl. Meteorol. Climatol. 50, 2267-2269.
https://doi.org/10.1175/JAMC-D-11-0143.1
"""
ta = np.array(ta)
rh = np.array(rh)
# 1. check for correct shape
if not (ta.ndim <= 1 and rh.ndim <= 1):
raise TypeError(
'Only arrays with one dimension are allowed. '
'Please reshape your array accordingly',
)
# 2. check for same length
if not (ta.size == rh.size):
raise ValueError('All arrays must have the same length')
result = thermal_comfort_mod.wet_bulb_temp(ta=ta, rh=rh)
# check if we have a single value
if result.size == 1:
return result.item()
else:
return result
# autopep8: off
@overload
def sat_vap_press_water(ta: float) -> float: ...
@overload
def sat_vap_press_water(ta: Iterable[float]) -> npt.NDArray[T]: ...
@overload
def sat_vap_press_water(ta: npt.NDArray[T]) -> npt.NDArray[T]: ...
# autopep8: on
[docs]
def sat_vap_press_water(
ta: Union[npt.NDArray[T], float, Iterable[float]],
) -> Union[npt.NDArray[T], float]:
"""Calculate the saturation vapor pressure **over water** following the
equation in VDI 3786 sheet 04.
This functions is optimized on 1D-array operations, however also scalars may
be provided.
:param ta: air temperature in °C
:returns: returns the saturation vapor pressure in hPa
**References**
- Sonntag, D. (1990). Important new values of the physical con- stants of 1986,
vapour pressure formulations based on the ITC-90, and psychrometer formulae.
Z. Meteorol., 40, 340–344.
"""
ta = np.array(ta)
# 1. check for correct shape
if not ta.ndim <= 1:
raise TypeError(
'Only arrays with one dimension are allowed. '
'Please reshape your array accordingly',
)
# 2. check for value ranges
if np.any((ta < -45) | (ta > 60)):
raise ValueError('The air temperature (ta) must be between -45 and 60 °C')
result = thermal_comfort_mod.sat_vap_press_water(ta=ta)
# check if we have a single value
if result.size == 1:
return result.item()
else:
return result
# autopep8: off
@overload
def sat_vap_press_ice(ta: float) -> float: ...
@overload
def sat_vap_press_ice(ta: Iterable[float]) -> npt.NDArray[T]: ...
@overload
def sat_vap_press_ice(ta: npt.NDArray[T]) -> npt.NDArray[T]: ...
# autopep8: on
[docs]
def sat_vap_press_ice(
ta: Union[npt.NDArray[T], float, Iterable[float]],
) -> Union[npt.NDArray[T], float]:
"""Calculate the saturation vapor pressure **over ice** following the
equation in VDI 3786 sheet 04.
This functions is optimized on 1D-array operations, however also scalars may
be provided.
:param ta: air temperature in °C
:returns: returns the saturation vapor pressure in hPa
**References**
- Sonntag, D. (1990). Important new values of the physical con- stants of 1986,
vapour pressure formulations based on the ITC-90, and psychrometer formulae.
Z. Meteorol., 40, 340–344.
"""
ta = np.array(ta)
# 1. check for correct shape
if not ta.ndim <= 1:
raise TypeError(
'Only arrays with one dimension are allowed. '
'Please reshape your array accordingly',
)
# 2. check for value ranges
if np.any((ta < -65) | (ta > 0.01)):
raise ValueError('The air temperature (ta) must be between -65 and 0.01 °C')
result = thermal_comfort_mod.sat_vap_press_ice(ta=ta)
# check if we have a single value
if result.size == 1:
return result.item()
else:
return result
# autopep8: off
@overload
def dew_point(
ta: float,
rh: float,
) -> float: ...
@overload
def dew_point(
ta: Iterable[float],
rh: Iterable[float],
) -> npt.NDArray[T]: ...
@overload
def dew_point(
ta: npt.NDArray[T],
rh: npt.NDArray[T],
) -> npt.NDArray[T]: ...
# autopep8: on
[docs]
def dew_point(
ta: Union[npt.NDArray[T], float, Iterable[float]],
rh: Union[npt.NDArray[T], float, Iterable[float]],
) -> Union[npt.NDArray[T], float]:
"""Calculate the dew point following the equation in VDI 3786 sheet 04.
For temperatures values grate or equal to 0 °C, the dew point is calculated
using the saturation vapor pressure over water. For temperatures below 0 °C,
the dew point is calculated using the saturation vapor pressure over ice.
This functions is optimized on 1D-array operations, however also scalars may
be provided.
:param ta: air temperature in °C
:param rh: relative humidity in %
:returns: returns the dew point temperature in °C
**References**
- Sonntag, D. (1990). Important new values of the physical con- stants of 1986,
vapour pressure formulations based on the ITC-90, and psychrometer formulae.
Z. Meteorol., 40, 340–344.
- Sonntag, D. (1994). Advancements in the field of hygrometry.
Meteorologische Zeitschrift, 51–66. https://doi.org/10.1127/metz/3/1994/51
"""
ta = np.array(ta)
rh = np.array(rh)
# 1. check for correct shape
if not (ta.ndim <= 1 and rh.ndim <= 1):
raise TypeError(
'Only arrays with one dimension are allowed. '
'Please reshape your array accordingly',
)
# 2. check for same length
if not (ta.size == rh.size):
raise ValueError('All arrays must have the same length')
result = thermal_comfort_mod.dew_point(ta=ta, rh=rh)
# check if we have a single value
if result.size == 1:
return result.item()
else:
return result
# autopep8: off
@overload
def absolute_humidity(
ta: float,
rh: float,
) -> float: ...
@overload
def absolute_humidity(
ta: Iterable[float],
rh: Iterable[float],
) -> npt.NDArray[T]: ...
@overload
def absolute_humidity(
ta: npt.NDArray[T],
rh: npt.NDArray[T],
) -> npt.NDArray[T]: ...
# autopep8: on
[docs]
def absolute_humidity(
ta: Union[npt.NDArray[T], float, Iterable[float]],
rh: Union[npt.NDArray[T], float, Iterable[float]],
) -> Union[npt.NDArray[T], float]:
"""Calculate the absolute humidity above water following the equation in
VDI 3786 sheet 04.
This functions is optimized on 1D-array operations, however also scalars may
be provided.
:param ta: air temperature in °C
:param rh: relative humidity in %
:returns: returns the absolute humidity in g/m³
"""
ta = np.array(ta)
rh = np.array(rh)
# 1. check for correct shape
if not (ta.ndim <= 1 and rh.ndim <= 1):
raise TypeError(
'Only arrays with one dimension are allowed. '
'Please reshape your array accordingly',
)
# 2. check for same length
if not (ta.size == rh.size):
raise ValueError('All arrays must have the same length')
result = thermal_comfort_mod.absolute_humidity(ta=ta, rh=rh)
# check if we have a single value
if result.size == 1:
return result.item()
else:
return result
# autopep8: off
@overload
def specific_humidity(
ta: float,
rh: float,
p: float = 1013.25,
) -> float: ...
@overload
def specific_humidity(
ta: Iterable[float],
rh: Iterable[float],
p: Union[Iterable[float], float] = 1013.25,
) -> npt.NDArray[T]: ...
@overload
def specific_humidity(
ta: npt.NDArray[T],
rh: npt.NDArray[T],
p: Union[npt.NDArray[T], float] = 1013.25,
) -> npt.NDArray[T]: ...
# autopep8: on
[docs]
def specific_humidity(
ta: Union[npt.NDArray[T], float, Iterable[float]],
rh: Union[npt.NDArray[T], float, Iterable[float]],
p: Union[npt.NDArray[T], float, Iterable[float]] = 1013.25,
) -> Union[npt.NDArray[T], float]:
"""Calculate the specific humidity above water following the equation in
VDI 3786 sheet 04.
This functions is optimized on 1D-array operations, however also scalars may
be provided.
:param ta: air temperature in °C
:param rh: relative humidity in %
:param p: air pressure in hPa (default 1013.25 hPa)
:returns: returns the absolute humidity in g/kg
"""
ta = np.array(ta)
rh = np.array(rh)
# when we use the default, we need to reshape the arrays
if isinstance(p, float):
p = np.full_like(ta, p, dtype=float)
else:
p = np.array(p)
# 1. check for correct shape
if not (ta.ndim <= 1 and rh.ndim <= 1 and p.ndim <= 1):
raise TypeError(
'Only arrays with one dimension are allowed. '
'Please reshape your array accordingly',
)
# 2. check for same length
if not (ta.size == rh.size == p.size):
raise ValueError('All arrays must have the same length')
result = thermal_comfort_mod.specific_humidity(ta=ta, rh=rh, p=p)
# check if we have a single value
if result.size == 1:
return result.item()
else:
return result