API reference¶
The thermal-index functions are available at the top level of the package, for
example thermofeel.calculate_utci(...) or
thermofeel.calculate_excess_heat_factor(...). The supporting functions for the
excess heat/cold factors (daily mean temperature, significance and
acclimatisation indices, heatwave severity) live in the thermofeel.excess_heat
submodule. This page is generated from the source docstrings.
All functions are vectorised over NumPy and take SI inputs; pass arrays and
wrap scalars in an array (e.g. np.array([300.0])). See
Calling convention for details.
Thermal indices and supporting quantities¶
thermofeel is a library to calculate human thermal comfort indexes.
Currently calculates the thermal indexes: * Universal Thermal Climate Index * Apparent Temperature * Heat Index Adjusted * Heat Index Simplified * Humidex * Normal Effective Temperature * Wet Bulb Globe Temperature * Wet Bulb Globe Temperature Simple * Wet Bulb Globe Temperature (Liljegren method) * Heat Force (KNMI 0-10 heat-stress scale) * Wind Chill
In support of the above indexes, it also calculates: * Globe Temperature * Mean Radiant Temperature * Mean Radiant Temperature from Globe Temperature * Relative Humidity Percentage * Saturation vapour pressure * Wet Bulb Temperature
To calculate the cos of the solar zenith angle, we suggest to use the earthkit-meteo library (github.com:ecmwf/earthkit-meteo.git)
calculate_relative_humidity_percent ¶
Relative Humidity in percent :param t2_k: (float array) 2m temperature [K] :param td_k: (float array) dew point temperature [K] returns relative humidity [%]
Uses the Magnus-Tetens saturation vapour pressure (coefficients over water) -
a different empirical form to calculate_saturation_vapour_pressure
(Hardy 1998). The result is not clamped: when td_k > t2_k (supersaturation)
it exceeds 100%.
Reference: Tetens (1930); coefficients per Murray (1967) https://doi.org/10.1175/1520-0450(1967)006<0203:OTCOSV>2.0.CO;2
Source code in thermofeel/thermofeel.py
calculate_saturation_vapour_pressure ¶
Saturation vapour pressure over water :param t2_k: (float array) 2m temperature [K] returns saturation vapor pressure over water in the pure phase [hPa] == [mBar] Reference: Hardy (1998) https://www.decatur.de/javascript/dew/resources/its90formulas.pdf
Source code in thermofeel/thermofeel.py
calculate_saturation_vapour_pressure_multiphase ¶
Saturation vapour pressure over liquid water and ice :param t2_k: (float array) 2m temperature [K] :param phase: (int array) 0 over liquid water, 1 over ice (same shape as t2_k) returns pressure of water vapor over a surface of liquid water or ice [hPa] == [mBar]
t2_k and phase are array-like (wrap a scalar in an array). Only
elements with phase equal to 0 (liquid) or 1 (ice) are computed; any
other phase value leaves that element at 0 hPa.
Reference: ECMWF IFS Documentation CY45R1 - Part IV : Physical processes (2018) pp. 116 https://doi.org/10.21957/4whwo8jw0 https://metview.readthedocs.io/en/latest/api/functions/saturation_vapour_pressure.html
Source code in thermofeel/thermofeel.py
calculate_nonsaturation_vapour_pressure ¶
Non saturated vapour pressure :param t2_k: (float array) 2m temperature [K] :param rh: (float array) relative humidity percentage [%] returns non saturated vapor pressure [hPa] == [mBar] Reference: Bureau of Meteorology (2010) http://www.bom.gov.au/info/thermal_stress/#approximation
Source code in thermofeel/thermofeel.py
scale_windspeed ¶
Scaling wind speed from 10 metres to height h :param va: (float array) 10m wind speed [m/s] :param h: (float array) height at which wind speed needs to be scaled [m] returns wind speed at height h Reference: Bröde et al. (2012) https://doi.org/10.1007/s00484-011-0454-1
Source code in thermofeel/thermofeel.py
approximate_dsrp ¶
Helper function to approximate dsrp from fdir and cossza
By geometry the direct solar radiation perpendicular to the beam is
dsrp = fdir / cos(sza) = fdir / cossza; this is applied where
cossza > threshold and left as fdir below it. The approximation
introduces large errors as cossza approaches zero, so use it only if dsrp is
not available in your dataset. fdir and cossza are array-like (wrap a
scalar in an array). To compute cossza consider using
earthkit-meteo.solar.cos_solar_zenith_angle.
:param fdir: (float array) total sky direct solar radiation at surface [W m-2]
:param cossza: (float array) cosine of solar zenith angle [dimentionless]
:param threshold: (float) minimum cossza for which fdir is divided (default 0.1)
returns direct radiation from the Sun [W m-2]
Source code in thermofeel/thermofeel.py
calculate_dew_point_from_relative_humidity ¶
Dew point temperature at 2m from relative humidity in percent :param rh: (float array) relative humidity [%] :param t2_k: (float array) 2m temperature [K] returns dew point temperature [K] Reference: Alduchov and Eskridge (1996) https://doi.org/10.1175/1520-0450(1996)035<0601:IMFAOS>2.0.CO;2
Source code in thermofeel/thermofeel.py
calculate_mean_radiant_temperature ¶
calculate_mean_radiant_temperature(
ssrd: ArrayLike,
ssr: ArrayLike,
dsrp: ArrayLike,
strd: ArrayLike,
fdir: ArrayLike,
strr: ArrayLike,
cossza: ArrayLike,
) -> np.ndarray
MRT - Mean Radiant Temperature To compute cossza consider using earhkit-meteo.solar.calculate_cos_solar_zenith_angle :param ssrd: (float array) surface solar radiation downwards [W m-2] :param ssr: (float array) surface net solar radiation [W m-2] :param dsrp: (float array) direct solar radiation [W m-2] :param strd: (float array) surface thermal radiation downwards [W m-2] :param fdir: (float array) total sky direct solar radiation at surface [W m-2] :param strr: (float array) surface net thermal radiation [W m-2] :param cossza: (float array) cosine of solar zenith angle [dimentionless] returns mean radiant temperature [K] Reference: Di Napoli et al. (2020) https://link.springer.com/article/10.1007/s00484-020-01900-5
Source code in thermofeel/thermofeel.py
calculate_utci_polynomial ¶
calculate_utci_polynomial(
t2m: ArrayLike,
mrt: ArrayLike,
va: ArrayLike,
wvp: ArrayLike,
) -> np.ndarray
Helper function to calculate the UTCI polynomial approximation :param t2m: (float array) is 2m temperature [C] :param mrt: (float array) is mean radiant temperature [C] :param va: (float array) is wind speed at 10 meters [m/s] :param wvp: (float array) is water vapour pressure [kPa] returns UTCI [K] Reference: Brode et al. (2012) https://doi.org/10.1007/s00484-011-0454-1
Source code in thermofeel/thermofeel.py
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 | |
calculate_utci ¶
calculate_utci(
t2_k: ArrayLike,
va: ArrayLike,
mrt: ArrayLike,
td_k: ArrayLike | None = None,
ehPa: ArrayLike | None = None,
) -> np.ndarray
UTCI - Universal Thermal Climate Index :param t2_k: (float array) is 2m temperature [K] :param va: (float array) is wind speed at 10 meters [m/s] :param mrt: (float array) is mean radiant temperature [K] :param td_k: (float array) is 2m dew point temperature [K] :param ehPa: (float array) is water vapour pressure [hPa] returns UTCI [K]
Validity: the polynomial is fitted for air temperature -50...+50 degC, 10 m wind speed 0.5...17 m/s, mean radiant temperature from 30 degC below to 70 degC above air temperature, and water vapour pressure up to 50 hPa. Inputs are not clamped; outside this range the approximation extrapolates.
Reference: Brode et al. (2012) https://doi.org/10.1007/s00484-011-0454-1
Source code in thermofeel/thermofeel.py
calculate_wbgt_simple ¶
WBGT - Wet Bulb Globe Temperature computed by a the simpler algorithm :param t2_k: (float array) 2m temperature [K] :param rh: (float array) relative humidity percentage [%] returns Wet Bulb Globe Temperature [K]
Validity: an empirical screening estimate (no radiation/wind term), intended
for moderate-to-warm outdoor conditions; it is not a substitute for the
physically based calculate_wbgt_liljegren where accuracy matters.
Reference: ACSM (1984) https://doi.org/10.1080/00913847.1984.11701899 See also: http://www.bom.gov.au/info/thermal_stress/#approximation https://www.jstage.jst.go.jp/article/indhealth/50/4/50_MS1352/_pdf
Source code in thermofeel/thermofeel.py
calculate_wbt ¶
Wet Bulb Temperature :param t2_k: (float array) 2m temperature [K] :param rh: (float array) relative humidity percentage [%] returns wet bulb temperature [K]
Validity: Stull's regression is fitted for relative humidity 5...99% and air temperature -20...+50 degC at standard sea-level pressure (1013.25 hPa).
Reference: Stull (2011) https://doi.org/10.1175/JAMC-D-11-0143.1
Source code in thermofeel/thermofeel.py
calculate_bgt ¶
Globe temperature :param t2_k: (float array) 2m temperature [K] :param mrt: (float array) mean radiant temperature [K] :param va: (float array) wind speed at 10 meters [m/s] returns globe temperature [K]
Solves the globe energy balance with a closed-form quartic root.
Edge case — calm air: the closed form is a 0/0 indeterminate at exactly
zero wind speed. The limit as va -> 0 is the mean radiant temperature
(with no convection the globe reaches radiative equilibrium, bgt -> mrt),
so mrt is returned where va == 0. Invalid negative wind still yields
NaN.
Reference: Guo et al. 2018 https://doi.org/10.1016/j.enbuild.2018.08.029
Source code in thermofeel/thermofeel.py
calculate_wbgt ¶
WBGT - Wet Bulb Globe Temperature :param t2_k: (float array) 2m temperature [K] :param mrt: (float array) mean radiant temperature [K] :param va: (float array) wind speed at 10 meters [m/s] :param td_k: (float array) dew point temperature [K] returns wet bulb globe temperature [K] Reference: Stull (2011) https://doi.org/10.1175/JAMC-D-11-0143.1 See also: http://www.bom.gov.au/info/thermal_stress/
Source code in thermofeel/thermofeel.py
calculate_wind_speed_2m_liljegren ¶
calculate_wind_speed_2m_liljegren(
va: ArrayLike, cossza: ArrayLike, ssrd: ArrayLike
) -> np.ndarray
Scale 10m wind speed to 2m using the Liljegren stability-dependent profile :param va: (float array) wind speed at 10 metres [m/s] :param cossza: (float array) cosine of the solar zenith angle [dimensionless] :param ssrd: (float array) instantaneous downward shortwave radiation at the surface (SSRD) [W/m2] returns wind speed at 2 metres [m/s]
This is the 10m -> 2m conversion used operationally by KNMI within the
Liljegren WBGT (va * (2/10)**p), where the power-law exponent p comes
from a Pasquill-Gifford stability class derived from the solar elevation,
incoming radiation and wind speed. The result is floored at 0.13 m/s.
Reference: Liljegren et al. (2008) https://doi.org/10.1080/15459620802310770 See also: Kong and Huber (2022) https://doi.org/10.1029/2021EF002334
Source code in thermofeel/thermofeel.py
calculate_wbgt_liljegren ¶
calculate_wbgt_liljegren(
t2_k: ArrayLike,
rh: ArrayLike,
pressure: ArrayLike,
va: ArrayLike,
ssrd: ArrayLike,
fdir: ArrayLike,
cossza: ArrayLike,
wind_scaling: str = "liljegren",
) -> np.ndarray
WBGT - Wet Bulb Globe Temperature, Liljegren method :param t2_k: (float array) 2m temperature [K] :param rh: (float array) relative humidity [%] :param pressure: (float array) surface air pressure [hPa] :param va: (float array) wind speed at 10 metres [m/s] :param ssrd: (float array) instantaneous downward shortwave radiation at the surface (SSRD) [W/m2] :param fdir: (float array) fraction of ssrd that is direct beam [dimensionless, 0-1] :param cossza: (float array) cosine of the solar zenith angle [dimensionless] :param wind_scaling: (str) how to convert the 10m wind to the 2m sensor height: "liljegren" (default, the KNMI stability-dependent profile, see calculate_wind_speed_2m_liljegren) or "brode" (the generic scale_windspeed log profile) returns wet bulb globe temperature [K]
Physically based WBGT after Liljegren et al. (2008), the method used
operationally by KNMI. The globe and natural-wet-bulb temperatures are each
solved from their steady-state energy balance by fixed-point iteration and
combined with the air temperature as WBGT = 0.7*Tnw + 0.2*Tg + 0.1*Ta.
The KNMI operational guards are applied: wind below 0.62 m/s at 10 m is
raised to that floor (it is then scaled to 2 m for the sensor model); the
direct-beam fraction is clamped to [0, 0.9] and set to 0 when the sun is at
or below 89.5 degrees zenith. Instantaneous ssrd and cossza are
supplied by the caller (e.g. via earthkit-meteo). NaN is returned where the
iteration does not converge.
The Liljegren energy-balance solvers and physical constants live in the
thermofeel.liljegren submodule.
Reference: Liljegren et al. (2008) https://doi.org/10.1080/15459620802310770 See also: Kong and Huber (2022) https://doi.org/10.1029/2021EF002334
Source code in thermofeel/thermofeel.py
calculate_heat_force ¶
Heat Force - KNMI 0-10 heat-stress communication scale (hittekracht) :param wbgt_k: (float array) wet bulb globe temperature [K] returns heat force on a 0-10 scale [dimensionless]
Translates WBGT onto the integer 0-10 "heat force" scale used by KNMI for public communication of heat stress, analogous to wind force and the UV index. The bands are fixed 2 degC intervals of WBGT (lower-closed): heat force 0 is WBGT < 14 degC, 1 is [14, 16) degC, ..., 9 is [30, 32) degC, and 10 is WBGT >= 32 degC. Values are returned as whole numbers (as a float array, so NaN inputs propagate).
Reference: KNMI Technical Report TR-26-04 (2026), Table 1.
Source code in thermofeel/thermofeel.py
calculate_excess_heat_factor ¶
calculate_excess_heat_factor(
ehi_sig: ArrayLike,
ehi_accl: ArrayLike,
clip: bool = False,
) -> np.ndarray
EXHF - Excess Heat Factor :param ehi_sig: (float array) significance index, e.g. relative to the 95th percentile of daily mean temperature over a reference period. :param ehi_accl: (float array) acclimatisation index. :param clip: (bool) clip the significance index at zero (default False). returns excess heat factor [input unit squared, e.g. K^2]
Top-level convenience wrapper for
thermofeel.excess_heat.excess_heat_factor. The supporting daily mean
temperature, significance and acclimatisation indices, and heatwave
severity, live in the thermofeel.excess_heat submodule.
Reference: Nairn and Fawcett (2014), equation (3) https://doi.org/10.3390/ijerph120100227
Source code in thermofeel/thermofeel.py
calculate_excess_cold_factor ¶
calculate_excess_cold_factor(
ehi_sig: ArrayLike,
ehi_accl: ArrayLike,
clip: bool = False,
) -> np.ndarray
EXCF - Excess Cold Factor :param ehi_sig: (float array) significance index, e.g. relative to the 5th percentile of daily mean temperature over a reference period. :param ehi_accl: (float array) acclimatisation index. :param clip: (bool) clip the significance index at zero (default False). returns excess cold factor [input unit squared, e.g. K^2]
Top-level convenience wrapper for
thermofeel.excess_heat.excess_cold_factor. The supporting daily mean
temperature, significance and acclimatisation indices live in the
thermofeel.excess_heat submodule.
Reference: Nairn (2013), equation (7) https://www.cawcr.gov.au/technical-reports/CTR_060.pdf
Source code in thermofeel/thermofeel.py
calculate_mrt_from_bgt ¶
Mean radiant temperature from globe temperature :param t2_k: (float array) 2m temperature [K] :param bgt_k: (float array) globe temperature [K] :param va: (float array) wind speed at 10 meters [m/s] returns mean radiant temperature [K] Reference: Brimicombe et al. (2023) https://doi.org/10.1029/2022GH000701
Source code in thermofeel/thermofeel.py
calculate_humidex ¶
Humidex
:param t2_k: (float array) 2m temperature [K]
:param td_k: (float array) dew point temperature [K]
returns humidex [K]
Validity: Environment Canada's index; it is most meaningful in warm, humid conditions and is not reported by Environment Canada below ~20 degC.
Reference: Environment Canada https://climate.weather.gc.ca/glossary_e.html#humidex
Source code in thermofeel/thermofeel.py
calculate_normal_effective_temperature ¶
calculate_normal_effective_temperature(
t2_k: ArrayLike, va: ArrayLike, rh: ArrayLike
) -> np.ndarray
NET - Normal Effective Temperature :param t2_k: (float array) 2m temperature [K] :param va: (float array) wind speed at 10 meters [m/s] :param rh: (float array) relative humidity percentage [%] returns normal effective temperature [K]
Validity: an empirical index derived for subtropical (Hong Kong) conditions (Li and Chan 2000); it has no sharply defined input range.
Reference: Li and Chan (2006) https://doi.org/10.1017/S1350482700001602
Source code in thermofeel/thermofeel.py
calculate_apparent_temperature ¶
Apparent Temperature - version without radiation :param t2_k: (float array) 2m temperature [K] :param va: (float array) wind speed at 10 meters [m/s] :param rh: (float array) relative humidity percentage [%] returns apparent temperature [K]
Validity: the Bureau of Meteorology non-radiation form of Steadman's apparent temperature; an empirical estimate without sharply defined input bounds.
Reference: Steadman (1984) https://doi.org/10.1175/1520-0450(1984)023%3C1674:AUSOAT%3E2.0.CO;2 See also: http://www.bom.gov.au/info/thermal_stress/#atapproximation
Source code in thermofeel/thermofeel.py
calculate_wind_chill ¶
Wind Chill :param t2_k: (float array) 2m Temperature [K] :param va: (float array) wind speed at 10 meters [m/s] returns wind chill [K] Computation is only valid for temperatures between -50°C and 5°C and wind speeds between 5km/h and 80km/h. For input values outside those ranges, computed results not be considered valid. Reference: Blazejczyk et al. (2012) https://doi.org/10.1007/s00484-011-0453-2 See also: https://web.archive.org/web/20130627223738/http://climate.weatheroffice.gc.ca/prods_servs/normals_documentation_e.html # noqa
Source code in thermofeel/thermofeel.py
calculate_heat_index_simplified ¶
Heat Index :param t2_k: (float array) 2m temperature [K] :param rh: (float array) relative humidity [%] returns heat index [K]
The regression is applied only where air temperature exceeds 20 degC (below that the air temperature is returned unchanged). Inputs are array-like (wrap a scalar in an array), as the function masks elementwise internally.
Reference: Blazejczyk et al. (2012) https://doi.org/10.1007/s00484-011-0453-2
Source code in thermofeel/thermofeel.py
calculate_heat_index_adjusted ¶
Heat Index adjusted :param t2_k: (float array) 2m temperature [K] :param td_k: (float array) 2m dewpoint temperature [K] returns heat index [K] Reference: https://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml
Source code in thermofeel/thermofeel.py
Excess heat and cold factors¶
The Excess Heat Factor and Excess Cold Factor are available at the top level as
thermofeel.calculate_excess_heat_factor(...) and
thermofeel.calculate_excess_cold_factor(...) (listed above). The
thermofeel.excess_heat submodule below additionally provides the supporting
daily mean temperature, significance and acclimatisation indices, and heatwave
severity.
daily_mean_temperature ¶
Daily mean temperature computed from min and max
:param t2_min: 2-metre daily minimum temperature.
:param t2_max: 2-metre daily maximum temperature.
Reference: Nairn and Fawcett (2014) https://doi.org/10.3390/ijerph120100227
Source code in thermofeel/excess_heat.py
significance_index ¶
Significance index
:param dmt: 3-day running mean daily mean temperature (day i, i+1, i+2).
:param threshold: climatological threshold, e.g., 95th (heat extremes)
or 5th (cold extremes) percentile of daily mean temperature over a
reference period.
Reference: Nairn and Fawcett (2014), equation (1) https://doi.org/10.3390/ijerph120100227
Source code in thermofeel/excess_heat.py
acclimatisation_index ¶
Acclimatisation index
:param dmt: 3-day running mean daily mean temperature (days i, i+1, i+2).
:param threshold: 30-day running mean daily mean temperature (days i-30, ..., i-1).
Reference: Nairn and Fawcett (2014), equation (2) https://doi.org/10.3390/ijerph120100227
Source code in thermofeel/excess_heat.py
excess_heat_factor ¶
Excess heat factor
:param ehi_sig: Significance index, e.g., computed with respect to 95th
percentile of daily mean temperature over a reference period.
:param ehi_accl: Acclimatisation index.
:param clip: Whether to clip the lower value range at zero. Disabled by default.
Reference: Nairn and Fawcett (2014), equation (3) https://doi.org/10.3390/ijerph120100227
Source code in thermofeel/excess_heat.py
heatwave_severity ¶
Heatwave severity index
:param exhf: Excess heat factor.
:param threshold: Excess heat factor threshold, the 85th percentile
of all positive excess heat factor values in a reference period.
Reference: Nairn (2018), equation (4) https://doi.org/10.3390/ijerph15112494
Source code in thermofeel/excess_heat.py
excess_cold_factor ¶
Excess cold factor
:param ehi_sig: Significance index, e.g., computed with respect to 5th
percentile of daily mean temperature over a reference period..
:param ehi_accl: Acclimatisation index.
:param clip: Whether to clip the upper value range at zero. Disabled by default.
Reference: Nairn (2013), equation (7) https://www.cawcr.gov.au/technical-reports/CTR_060.pdf