Skip to main content

heatstress

Vectorised heat-stress indices for public-health, climate-adaptation and occupational-safety analysis: NWS Heat Index, Humidex, Apparent Temperature, wet-bulb temperature, WBGT (full Liljegren et al. 2008 model, shade/indoor, ISO 7243, BoM approximation), UTCI, Wind Chill, mean radiant temperature, solar position, risk categories and heatwave detection, including the Excess Heat Factor.

  • Pure numpy core; every function takes scalars, numpy arrays, pandas Series or xarray DataArrays and returns the same kind of object.
  • Units are explicit: degC by default with a units="C" | "F" | "K" switch on temperature functions, wind in m/s, pressure in hPa, radiation in W/m2, humidity in %. Converters are in heatstress.units.
  • Inputs outside a formula's validity range raise a ValidityRangeWarning; NaN propagates.
  • Validated against published reference values and independent implementations (see Validation).
  • A command-line tool for single values and CSV time series.

Disclaimer. heatstress computes screening indices from weather data. It is not medical advice and is not a substitute for on-site measurement, an occupational health programme, clinical judgement or the official warnings of your national meteorological or public-health service.

Installation

pip install heatstress            # numpy only
pip install "heatstress[pandas]"  # + pandas helpers (DataFrames, events_to_frame)
pip install "heatstress[xarray]"  # + xarray/pandas

Python 3.9 or newer.

Quick start

import heatstress as hs

hs.heat_index(35, 60)                     # 45.1 degC (NWS algorithm)
hs.heat_index(95, 50, units="F")          # 105.2 degF
hs.heat_index_category(45.1)              # 'danger'

hs.humidex(30, dew_point=15)              # 34.0
hs.apparent_temperature(30, 50, wind=2)   # 31.6 degC
hs.wet_bulb_psychrometric(25, 50)         # 17.9 degC
hs.utci(30, tmrt=50, wind=2, rh=50)       # 34.6 degC
hs.utci_category(34.6)                    # 'strong heat stress'

# Outdoor WBGT from standard weather data (Liljegren et al. 2008)
z = hs.solar_zenith("2026-07-15T10:00Z", lat=38.42, lon=27.14)
w = hs.wbgt_liljegren_components(t=33, rh=55, wind=1.5, solar=850, zenith=z, pressure=1008)
w.wbgt, w.globe_temperature, w.natural_wet_bulb    # 32.7, 49.7, 27.7 degC
hs.work_rest_allocation(w.wbgt, "moderate")   # ACGIH TLV work-rest guidance

# Heatwaves on a daily series
import pandas as pd
tmax = pd.Series(..., index=pd.date_range("1991-01-01", periods=..., freq="D"))
thr = hs.calendar_day_percentile(tmax, tmax.index, q=90, window=15, baseline=(1991, 2020))
events = hs.heatwave_events(tmax, thr, min_duration=3)
hs.events_to_frame(events)

ehf = hs.excess_heat_factor(tmean_daily)      # Nairn & Fawcett (2015)
hs.ehf_severity(ehf)                          # none / low-intensity / severe / extreme

Indices

Index Function Inputs Source Validity / notes
Heat Index heat_index T, RH NWS/WPC algorithm: Steadman (1979) simple formula, Rothfusz (1990) regression and the two NWS adjustments Regression error +/-1.3 degF; warning above 120 degF air temperature or 150 degF HI
Rothfusz regression only rothfusz_regression T, RH Rothfusz (1990) NWS SR 90-23 For comparison only
Humidex humidex T, RH or dew point Masterton & Richardson (1979), Environment Canada MSC vapour-pressure form
Apparent Temperature apparent_temperature T, RH, wind (10 m), optional net radiation Steadman (1994), Australian Bureau of Meteorology Shade and radiation forms
Wet bulb (Stull) wet_bulb_stull T, RH Stull (2011) J. Appl. Meteor. Climatol. 50:2267 RH 5-99 %, T -20 to 50 degC, 1013 hPa; error -1 to +0.65 K (larger in cold, dry air)
Wet bulb (psychrometric) wet_bulb_psychrometric T, RH, pressure WMO No. 8 (2018) Annex 4.B psychrometer equation, Newton solver Ventilated wet bulb, wick over water
WBGT outdoor wbgt_liljegren, wbgt_liljegren_components, globe_temperature_liljegren, natural_wet_bulb_liljegren T, RH, wind, global solar, zenith, pressure, optional direct fraction Liljegren et al. (2008) J. Occup. Environ. Hyg. 5:645 50.8 mm globe; 2 m wind (other heights scaled with a power law); solar capped at 0.85 x top of atmosphere as in the reference code
WBGT shade / indoor wbgt_shade T, RH, pressure (wind for method="liljegren") Simplified 0.7 Tw + 0.3 Ta, or Liljegren with no sun and 0.7 Tnwb + 0.3 Tg No solar load
WBGT from measurements wbgt_from_measurements Tnwb, Tg, (Ta) ISO 7243:2017 Indoor and outdoor formulas
WBGT approximation wbgt_bom T, RH Australian Bureau of Meteorology Approximation: assumes moderately high radiation and light wind; can be off by several degrees
UTCI utci T, Tmrt, wind (10 m), RH Broede et al. (2012) Int. J. Biometeorol. 56:481, 6th-order polynomial, 210 coefficients from the reference program UTCI_a002.f90 Ta -50 to 50 degC, Tmrt-Ta -30 to 70 K, wind 0.5-17 m/s, vapour pressure <= 5 kPa; out-of-range values NaN by default; wind below 0.5 m/s raised to 0.5
Wind Chill wind_chill T, wind (10 m) NWS/MSC (2001), Osczevski & Bluestein (2005) T <= 10 degC; MSC light-wind formula below 4.8 km/h
Feels like feels_like T, RH, wind NWS convention Wind chill / heat index / air temperature
Mean radiant temperature mean_radiant_temperature Tg, T, air speed ISO 7726:1998 Annex B Forced or natural convection; 150 mm globe, emissivity 0.95 by default
Solar position solar_position, solar_zenith time, lat, lon NOAA solar calculator (Meeus 1991) Geometric zenith, no refraction
Humidity helpers saturation_vapour_pressure, vapour_pressure, dew_point, relative_humidity_from_dew_point, relative_humidity_from_vapour_pressure T, RH / Td / e Magnus (Alduchov & Eskridge 1996), WMO, Buck (1981), BoM, Hardy (1998) ITS-90 Over liquid water

Risk categories

Function Scale
heat_index_category NWS: caution 80-89 degF, extreme caution 90-102, danger 103-124, extreme danger >= 125
humidex_category Environment Canada: < 30, 30-39, 40-45, >= 46
utci_category 10 UTCI stress classes from extreme cold (< -40) to extreme heat stress (> 46)
work_rest_allocation ACGIH TLV (acclimatized) / Action Limit (unacclimatized) WBGT work-rest table as printed in the OSHA Technical Manual Sec. III Ch. 4, for light / moderate / heavy / very heavy work, with a clothing adjustment value
iso7243_reference_wbgt, iso7243_assessment ISO 7243:2017 Annex A reference WBGT 56.7 - 11.5 log10(M) (acclimatized) and 59.9 - 14.1 log10(M) (unacclimatized)
ehf_severity Nairn & Fawcett (2015): low-intensity, severe (EHF >= EHF85), extreme (EHF >= 3 EHF85)

Heatwave detection

  • percentile_threshold and calendar_day_percentile (centred moving window of calendar days, optional baseline years, 29 February merged with 28 February).
  • heatwave_mask, consecutive_runs and heatwave_events find spells of at least min_duration consecutive days above a scalar or day-varying threshold and report start, end, duration, peak and exceedance statistics; events_to_frame converts them to a DataFrame.
  • excess_heat_factor: EHI_sig = T3 - T95, EHI_accl = T3 - T30prev, EHF = EHI_sig x max(1, EHI_accl), attributed to the first day of the three-day window; T95 from a reference period (t95= or reference=).

Command line

$ heatstress hi 35 --rh 60
              heat_index: 45.1 degC
                category: danger

$ heatstress wbgt 33 --rh 55 --wind 1.5 --solar 850 --time 2026-07-15T10:00Z --lat 38.42 --lon 27.14
$ heatstress utci 30 --tmrt 55 --wind 2 --rh 50
$ heatstress windchill 0 --wind 15 --wind-units mph --units F
$ heatstress series weather.csv -o indices.csv --lat 38.42 --lon 27.14

Every value command accepts --units C|F|K and --json; wind accepts --wind-units ms|kmh|mph|kn. heatstress series reads a CSV, recognises common column names (time, t/temperature, rh, td, wind, solar/ghi, pressure, tmrt, tg, zenith, lat, lon), and appends every index that the columns allow (UTCI needs tmrt or a globe temperature; the Liljegren WBGT needs wind, solar and either a zenith column or time plus coordinates). The same logic is available in Python as hs.compute_indices(df).

Validation

All comparisons are in the test suite (tests/test_reference_values.py) unless noted.

Check Reference Result
UTCI polynomial 1000 official test conditions, Broede et al. UTCI-Test-Data (doi:10.5281/zenodo.5503968, CC BY 4.0), column UTCI_polynomial max deviation 0.050 degC (= rounding of the published values)
UTCI vs UTCI-Fiala model same data set, 921 cases with wind <= 17 m/s RMS 1.17 K (Broede et al. report about 1.1 K)
UTCI vs independent code thermofeel 2.3.0 calculate_utci_polynomial max 1e-12 degC (scratch cross-check)
Heat index vs NWS chart 135 cells of the NWS heat index chart bare Rothfusz regression within 0.92 degF; full NWS algorithm within 0.92 degF where no adjustment applies and within 2.29 degF in the high-humidity cells where NWS adds its adjustment
Heat index vs independent code thermofeel calculate_heat_index_adjusted, 5000 random cases T <= 43 degC max 0.08 degC (scratch cross-check)
Liljegren WBGT, globe, natural wet bulb thermofeel 2.3.0 thermofeel.liljegren (KNMI port of the reference C code), 3000 random cases max 4e-13 degC; 8 frozen vectors in the tests
Wet bulb Stull (2011) worked example 20 degC / 50 % -> 13.7 degC; psychrometric table 25 degC / 50 % -> 17.9 degC exact to the published precision; Stull vs psychrometric within +/-1 K for 0-45 degC, RH 20-95 %
Wind chill NWS wind chill chart (6 cells); thermofeel within 0.6 degF; identical to thermofeel
Humidex Environment Canada example 30 degC / Td 15 degC -> 34 exact; thermofeel max 0.04
Apparent temperature thermofeel calculate_apparent_temperature identical (scratch cross-check)
Mean radiant temperature ISO 7726 formula; thermofeel max 0.06 degC (thermofeel uses 273.15 K in place of ISO's 273)
Solar position pvlib 0.16.1 NREL SPA, 4000 random times 1990-2060 and latitudes +/-80 deg zenith max 0.017 deg, Earth-Sun distance max 7e-5 AU; 6 frozen cases in the tests
Excess Heat Factor thermofeel thermofeel.excess_heat with pandas rolling windows max 4e-11

Units and conventions

  • Temperatures: degC unless units="F" or "K" (applies to all temperature inputs and outputs of that call).
  • Relative humidity in %, wind speed in m/s (10 m for Apparent Temperature, Wind Chill and UTCI; 2 m for Liljegren unless wind_height is given), pressure in hPa (station pressure), solar radiation in W/m2 (global horizontal).
  • Results come back as Python floats for scalar input, numpy arrays for array input, and pandas Series / xarray DataArrays (same index or coordinates) when the first array argument is one.

Citation

See CITATION.cff. Please also cite the original authors of each index you use.

License

MIT (c) 2026 Yusuf Eminoğlu. The UTCI test data shipped in tests/data are by Broede et al., CC BY 4.0.

Metadata

Release files for heatstress 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for heatstress 0.1.1
File Size Uploaded Provenance Actions
heatstress-0.1.1.tar.gz 77.8 kB With provenance Details for heatstress-0.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for heatstress 0.1.1
File Interpreter ABI Platform Actions
heatstress-0.1.1-py3-none-any.whl Python 3 none any Details for heatstress-0.1.1-py3-none-any.whl

Total release size: 127.9 kB

Release files / heatstress-0.1.1.tar.gz

Download URL heatstress-0.1.1.tar.gz
Size 77.8 kB
Tags Source
SHA-256 checksum
How to use checksums
571c98bde3bd45361aae476b4c0795a3da7deb312b0715977e322f79d9018340
BLAKE2b-256 checksum
How to use checksums
d5ea79ff13baf83429e0b2929266d1307f6c148f4c622d833d3dbbba34a7de64
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.

Transparency log

Release files / heatstress-0.1.1-py3-none-any.whl

Download URL heatstress-0.1.1-py3-none-any.whl
Size 50.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1f83305361c4b5d05af7f8af27d115538b2d3d85e767fe384cc9f3cc0e263e34
BLAKE2b-256 checksum
How to use checksums
08819cbd6547e0a317291344035ff3cd4c9aa38c6067b0302b9e902e1da8b020
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.

Transparency log

Security for heatstress 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for heatstress-0.1.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page