Models#

This section of the documentation provides detailed documentation on the various thermal comfort models, heat and cold indexes implemented in the pythermalcomfort package. These models are used to assess and predict thermal comfort, heat stress, physiological variables and more as a function of both environmental and personal parameters. Each model is accompanied by detailed descriptions, usage examples, and references to relevant standards and research. This documentation aims to provide a comprehensive understanding of how to use these models to evaluate thermal comfort in various scenarios. Please note that models are shown in alphabetical order and are not sorted based on their accuracy.

Adaptive Model#

The adaptive thermal comfort model is a method that relates indoor design temperatures or acceptable temperature ranges to outdoor meteorological or climatological parameters. It’s specifically intended for occupant-controlled naturally conditioned spaces, where the thermal conditions are primarily regulated by occupants through the use of openings in the building envelope, such as windows. The adaptive model is based on the idea that people in naturally ventilated spaces adjust to their environment through a variety of behavioural and physiological adaptations.

Below are some key characteristics and criteria of the adaptive model, according to the ASHRAE 55-2023 standard [55ASHRAE2023]:

Applicability: The adaptive model can only be applied in spaces that meet specific criteria:
  • There is no mechanical cooling or heating system in operation.

  • Occupants have metabolic rates ranging from 1.0 to 1.5 met.

  • Occupants are able to adjust their clothing to indoor or outdoor thermal conditions within a range of 0.5 to 1.0 clo.

  • The prevailing mean outdoor temperature is between 10°C (50°F) and 33.5°C (92.3°F).

  • The space has operable fenestration that can be readily opened and adjusted by the occupants.

Methodology: The model uses the prevailing mean outdoor air temperature to determine acceptable indoor operative temperatures. The prevailing mean outdoor temperature is calculated as a running average of the mean daily outdoor temperatures over a period of days.
  • The calculation of the prevailing mean outdoor temperature gives more weight to recent days, since these have a greater influence on occupants’ comfort temperatures.

Comfort Zone: The adaptive model defines comfort zones differently from the PMV model. Instead of using a heat balance approach to determine an ideal temperature, it relies on an empirical model that links satisfaction with the prevailing mean outdoor temperature.
  • The comfort zone is defined by the 80% or 90% acceptability limits.

  • The model does not require estimation of clothing values, since it accounts for clothing adaptation by relating the range of satisfactory indoor temperatures to the outdoor climate.

Air Speed: When using the adaptive model, no humidity or air speed limits are required. However, if elevated air movement is present, air movement extensions to the comfort zone’s lower and upper operative temperature limits can be used.

Underlying Basis: The adaptive model is derived from a global database of measurements taken primarily in office buildings. It is based on the observation that people adapt to their environment over time.

Purpose: The adaptive model is specifically intended for use in naturally conditioned spaces, where occupants have a degree of control over their environment. It is not appropriate for other types of spaces.

The adaptive model, in contrast to the PMV model, does not rely on a heat-balance approach. Instead, it uses an empirical model that relates satisfaction to the prevailing mean outdoor air temperature. The comfort zone is defined by the percentage of people who find the environment acceptable. The adaptive model is based on the idea that people adjust to their environment over time.

The use of the adaptive model requires documentation of the compliance time periods, the prevailing mean outdoor design temperature, and any increased air speed adjustments.

ASHRAE 55#

pythermalcomfort.models.adaptive_ashrae.adaptive_ashrae(tdb, tr, t_running_mean, v, units='SI', limit_inputs=True, round_output=True)[source]#

Calculate the adaptive thermal comfort based on ASHRAE 55.

The adaptive model relates indoor design temperatures or acceptable temperature ranges to outdoor meteorological or climatological parameters. The adaptive model can only be used in occupant-controlled naturally conditioned spaces that meet all the following criteria:

  • There is no mechanical cooling or heating system in operation.

  • Occupants have a metabolic rate between 1.0 and 1.5 met.

  • Occupants are free to adapt their clothing within a range as wide as 0.5 and 1.0 clo.

  • The prevailing mean (running mean) outdoor temperature is between 10 and 33.5 °C.

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, default in [°C] or [°F] if units = ‘IP’.

  • tr (float or list of floats) – Mean radiant temperature, default in [°C] or [°F] if units = ‘IP’.

  • t_running_mean (float or list of floats) – Running mean temperature, default in [°C] or [°F] if units = ‘IP’.

    Note

    The running mean temperature can be calculated using the function pythermalcomfort.utilities.running_mean_outdoor_temperature().

  • v (float or list of floats) – Air speed, default in [m/s] or [fps] if units = ‘IP’.

  • units (str, optional) – Units system, ‘SI’ or ‘IP’. Defaults to ‘SI’.

  • limit_inputs (bool, optional) – If True, returns nan for inputs outside standard limits. Defaults to True.

    Note

    ASHRAE 55 2020 limits: 10 < tdb [°C] < 40, 10 < tr [°C] < 40, 0 < vr [m/s] < 2, 10 < t_running_mean [°C] < 33.5.

  • round_output (bool, optional) – If True, rounds t_cmf to one decimal place in SI before the comfort bounds are derived, so tmp_cmf_80_low, tmp_cmf_80_up, tmp_cmf_90_low and tmp_cmf_90_up inherit that rounding. If False, t_cmf and the derived bounds are returned at full precision. Under units='IP' the rounded SI value is then converted to °F, so IP outputs carry the extra decimals from the °C-to-°F conversion. Defaults to True.

Returns:

AdaptiveASHRAE – A dataclass containing the results. See AdaptiveASHRAE for more details.

Examples

from pythermalcomfort.models import adaptive_ashrae

results = adaptive_ashrae(tdb=25, tr=25, t_running_mean=20, v=0.1)
print(results)
# AdaptiveASHRAE(tmp_cmf=np.float64(24.0), tmp_cmf_80_low=np.float64(20.5), tmp_cmf_80_up=np.float64(27.5), tmp_cmf_90_low=np.float64(21.5), tmp_cmf_90_up=np.float64(26.5), acceptability_80=array(True), acceptability_90=array(True))

print(results.acceptability_80)  # or use print(results["acceptability_80"])
# True
# The conditions you entered are considered to be comfortable for 80% of the occupants.

# You can also pass arrays as input to the function
results = adaptive_ashrae(tdb=[25, 26], tr=25, t_running_mean=20, v=0.1)
print(results)
# AdaptiveASHRAE(tmp_cmf=array([24., 24.]), tmp_cmf_80_low=array([20.5, 20.5]), tmp_cmf_80_up=array([27.5, 27.5]), tmp_cmf_90_low=array([21.5, 21.5]), tmp_cmf_90_up=array([26.5, 26.5]), acceptability_80=array([ True,  True]), acceptability_90=array([ True,  True]))

# For users who want to use the IP system
results = adaptive_ashrae(tdb=77, tr=77, t_running_mean=68, v=0.3, units="IP")
print(results)
# AdaptiveASHRAE(tmp_cmf=np.float64(75.2), tmp_cmf_80_low=np.float64(68.9), tmp_cmf_80_up=np.float64(81.5), tmp_cmf_90_low=np.float64(70.7), tmp_cmf_90_up=np.float64(79.7), acceptability_80=array(True), acceptability_90=array(True))

adaptive_ashrae(tdb=25, tr=25, t_running_mean=9, v=0.1)
# AdaptiveASHRAE(tmp_cmf=np.float64(nan), ... acceptability_90=array(False))
# The adaptive thermal comfort model can only be used if the running mean temperature is higher than 10°C.
class pythermalcomfort.classes_return.AdaptiveASHRAE(tmp_cmf, tmp_cmf_80_low, tmp_cmf_80_up, tmp_cmf_90_low, tmp_cmf_90_up, acceptability_80, acceptability_90)[source]#

A dataclass to store the results of the adaptive thermal comfort model based on ASHRAE 55.

Variables:
  • tmp_cmf (float or list of floats) – Comfort temperature at a specific running mean temperature, default in [°C] or [°F].

  • tmp_cmf_80_low (float or list of floats) – Lower acceptable comfort temperature for 80% occupants, default in [°C] or [°F].

  • tmp_cmf_80_up (float or list of floats) – Upper acceptable comfort temperature for 80% occupants, default in [°C] or [°F].

  • tmp_cmf_90_low (float or list of floats) – Lower acceptable comfort temperature for 90% occupants, default in [°C] or [°F].

  • tmp_cmf_90_up (float or list of floats) – Upper acceptable comfort temperature for 90% occupants, default in [°C] or [°F].

  • acceptability_80 (bool or list of bools) – Acceptability for 80% occupants.

  • acceptability_90 (bool or list of bools) – Acceptability for 90% occupants.

EN 16798-1 2019#

pythermalcomfort.models.adaptive_en.adaptive_en(tdb, tr, t_running_mean, v, units='SI', limit_inputs=True, round_output=True)[source]#

Calculate the adaptive thermal comfort based on EN 16798-1 2019 [16798EN2019].

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, default in [°C] or [°F] if units = ‘IP’.

  • tr (float or list of floats) – Mean radiant temperature, default in [°C] or [°F] if units = ‘IP’.

  • t_running_mean (float or list of floats) – Running mean temperature, default in [°C] or [°F] if units = ‘IP’.

    Note

    The running mean temperature can be calculated using the function pythermalcomfort.utilities.running_mean_outdoor_temperature().

  • v (float or list of floats) – Air speed, default in [m/s] or [fps] if units = ‘IP’.

    Note

    Indoor operative temperature correction is applicable for buildings equipped with fans or personal systems providing building occupants with personal control over air speed at occupant level. For operative temperatures above 25°C the comfort zone upper limit can be increased by 1.2 °C (0.6 < v < 0.9 m/s), 1.8 °C (0.9 < v < 1.2 m/s), 2.2 °C (v > 1.2 m/s).

  • units ({‘SI’, ‘IP’}) – Select the SI (International System of Units) or the IP (Imperial Units) system.

  • limit_inputs (bool, default True) – If True, returns NaN for inputs outside the standard applicability limits.

  • round_output (bool, default True) – If True, rounds the returned comfort temperature and category bounds to one decimal place in the output unit (rounding is applied after any IP unit conversion). If False, returns the unrounded values.

Returns:

AdaptiveEN – A dataclass containing the results. See AdaptiveEN for more details.

Examples

from pythermalcomfort.models import adaptive_en

results = adaptive_en(tdb=25, tr=25, t_running_mean=20, v=0.1)
print(results)
# AdaptiveEN(tmp_cmf=np.float64(25.4), acceptability_cat_i=np.True_, acceptability_cat_ii=np.True_, ...)

print(results.acceptability_cat_i)  # or print(results["acceptability_cat_i"])
# True
# The conditions you entered are considered to comply with Category I

# For users who want to use the IP system, units="IP" or "ip" are both valid
results = adaptive_en(tdb=77, tr=77, t_running_mean=68, v=0.3, units="IP")
print(results)
# AdaptiveEN(tmp_cmf=np.float64(77.7), acceptability_cat_i=np.True_, ...)

results = adaptive_en(tdb=25, tr=25, t_running_mean=9, v=0.1)
print(results)
# AdaptiveEN(tmp_cmf=np.float64(nan), acceptability_cat_i=np.False_, ...)
# The adaptive thermal comfort model can only be used
# if the running mean temperature is between 10 °C and 30 °C.
class pythermalcomfort.classes_return.AdaptiveEN(tmp_cmf, acceptability_cat_i, acceptability_cat_ii, acceptability_cat_iii, tmp_cmf_cat_i_up, tmp_cmf_cat_ii_up, tmp_cmf_cat_iii_up, tmp_cmf_cat_i_low, tmp_cmf_cat_ii_low, tmp_cmf_cat_iii_low)[source]#

Dataclass to store the results of the adaptive thermal comfort calculation based on EN 16798-1 2019.

Variables:
  • tmp_cmf (float or list of floats) – Comfort temperature at that specific running mean temperature, default in [°C] or in [°F].

  • acceptability_cat_i (bool or list of bools) – If the indoor conditions comply with comfort category I.

  • acceptability_cat_ii (bool or list of bools) – If the indoor conditions comply with comfort category II.

  • acceptability_cat_iii (bool or list of bools) – If the indoor conditions comply with comfort category III.

  • tmp_cmf_cat_i_up (float or list of floats) – Upper acceptable comfort temperature for category I, default in [°C] or in [°F].

  • tmp_cmf_cat_ii_up (float or list of floats) – Upper acceptable comfort temperature for category II, default in [°C] or in [°F].

  • tmp_cmf_cat_iii_up (float or list of floats) – Upper acceptable comfort temperature for category III, default in [°C] or in [°F].

  • tmp_cmf_cat_i_low (float or list of floats) – Lower acceptable comfort temperature for category I, default in [°C] or in [°F].

  • tmp_cmf_cat_ii_low (float or list of floats) – Lower acceptable comfort temperature for category II, default in [°C] or in [°F].

  • tmp_cmf_cat_iii_low (float or list of floats) – Lower acceptable comfort temperature for category III, default in [°C] or in [°F].

Apparent Temperature (AT)#

pythermalcomfort.models.at.at(tdb, rh, v, q=None, round_output=True)[source]#

Calculate the Apparent Temperature (AT). The AT is defined as the temperature at the reference humidity level producing the same amount of discomfort as that experienced under the current ambient temperature, humidity, and solar radiation [Steadman1984]. In other words, the AT is an adjustment to the dry bulb temperature based on the relative humidity value. Absolute humidity with a dew point of 14°C is chosen as a reference.

It includes the chilling effect of the wind at lower temperatures. [Blazejczyk2012]

Note

Two formulas for AT are in use by the Australian Bureau of Meteorology: one includes solar radiation and the other one does not (http://www.bom.gov.au/info/thermal_stress/ , 29 Sep 2021). Please specify q if you want to estimate AT with solar load.

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C]

  • rh (float or list of floats) – Relative humidity, [%]

  • v (float or list of floats) – Wind speed 10m above ground level, [m/s]

  • q (float or list of floats, optional) – Net radiation absorbed per unit area of body surface [W/m2]

  • round_output (bool, default True) – If True, rounds the output value; if False, does not round it.

Returns:

AT – Dataclass containing the apparent temperature, [°C]. See AT for more details.

Examples

from pythermalcomfort.models import at

at(tdb=25, rh=30, v=0.1)
# AT(at=24.1)
class pythermalcomfort.classes_return.AT(at)[source]#

Dataclass to store the results of the Apparent Temperature (AT) calculation.

Variables:

at (float or list of floats) – Apparent temperature, [°C]

Ankle draft#

pythermalcomfort.models.ankle_draft.ankle_draft(tdb, tr, vr, rh, met, clo, v_ankle, units='SI', limit_inputs=True)[source]#

Calculate the percentage of thermally dissatisfied people with the ankle draft (0.1 m) above floor level [Liu2017].

This equation is only applicable for vr < 0.2 m/s (40 fps).

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, default in [°C] or [°F] if units = ‘IP’.

    Note

    The air temperature is the average value over two heights: 0.6 m (24 in.) and 1.1 m (43 in.) for seated occupants, and 1.1 m (43 in.) and 1.7 m (67 in.) for standing occupants.

  • tr (float or list of floats) – Mean radiant temperature, default in [°C] or [°F] if units = ‘IP’.

  • vr (float or list of floats) – Relative air speed, default in [m/s] or [fps] if units = ‘IP’.

    Note

    vr is the relative air speed caused by body movement and not the air speed measured by the air speed sensor. The relative air speed is the sum of the average air speed measured by the sensor plus the activity-generated air speed (Vag). Vag is the activity-generated air speed caused by motion of individual body parts. vr can be calculated using the function pythermalcomfort.utilities.v_relative().

  • rh (float or list of floats) – Relative humidity, [%].

  • met (float or list of floats) – Metabolic rate, [met].

  • clo (float or list of floats) – Clothing insulation, [clo].

    Note

    this is the basic insulation also known as the intrinsic clothing insulation value of the clothing ensemble (Icl,r), this is the thermal insulation from the skin surface to the outer clothing surface, including enclosed air layers, under actual environmental conditions. This value is not the total insulation (IT,r). The dynamic clothing insulation, clo, can be calculated using the function pythermalcomfort.utilities.clo_dynamic_ashrae().

  • v_ankle (float or list of floats) – Air speed at 0.1 m (4 in.) above the floor, default in [m/s] or [fps] if units = ‘IP’.

  • units ({‘SI’, ‘IP’}) – Select the SI (International System of Units) or the IP (Imperial Units) system.

  • limit_inputs (bool, optional) – By default, if the inputs are outside the standard applicability limits the function returns nan. If False, returns values even if input values are outside the applicability limits of the model. Defaults to True. The applicability limits are 10 < tdb [°C] < 40, 10 < tr [°C] < 40, 0 < vr [m/s] < 0.2, 1 < met [met] < 4, and 0 < clo [clo] < 1.5.

Returns:

AnkleDraft – Dataclass containing the results of the ankle draft calculation. See AnkleDraft for more details.

Examples

from pythermalcomfort.models import ankle_draft

results = ankle_draft(25, 25, 0.2, 50, 1.2, 0.5, 0.3, units="SI")
print(results)
# AnkleDraft(ppd_ad=18.5, acceptability=True)
class pythermalcomfort.classes_return.AnkleDraft(ppd_ad, acceptability)[source]#

Dataclass to store the results of the ankle draft calculation.

Variables:
  • ppd_ad (float or list of floats) – Predicted Percentage of Dissatisfied occupants with ankle draft, [%].

  • acceptability (bool or list of bools) – Indicates if the air speed at the ankle level is acceptable according to ASHRAE 55 2020 standard.

Clothing prediction#

pythermalcomfort.models.clo_tout.clo_tout(tout, units='SI')[source]#

Calculate representative clothing insulation Icl based on outdoor air temperature at 06:00 a.m. [Schiavon2013].

Parameters:
  • tout (float or list of floats) – Outdoor air temperature at 06:00 a.m., default in [°C] or [°F] if units = ‘IP’.

  • units (str, optional) – Select the SI (International System of Units) or the IP (Imperial Units) system. Supported values are ‘SI’ and ‘IP’. Defaults to ‘SI’.

Returns:

CloTOut – A dataclass containing the representative clothing insulation Icl. See CloTOut for more details. To access the clo_tout value, use the clo_tout attribute of the returned CloTOut instance, e.g., result.clo_tout.

Raises:
  • TypeError – If tout is not a float, int, NumPy array, or a list of floats or integers.

  • ValueError – If an invalid unit is provided or non-numeric elements are found in tout.

Notes

Note

The ASHRAE 55 2020 states that it is acceptable to determine the clothing insulation Icl using this equation in mechanically conditioned buildings [55ASHRAE2023].

Note

Limitations: - This equation may not be accurate for extreme temperature ranges.

Examples

from pythermalcomfort.models import clo_tout

result = clo_tout(tout=27)
print(result.clo_tout)  # 0.46

result = clo_tout(tout=[27, 25])
print(result.clo_tout)  # array([0.46, 0.47])
class pythermalcomfort.classes_return.CloTOut(clo_tout)[source]#

Dataclass to represent the clothing insulation Icl as a function of outdoor air temperature.

Variables:

clo_tout (float or list of floats) – Representative clothing insulation Icl.

Cooling Effect (CE)#

pythermalcomfort.models.cooling_effect.cooling_effect(tdb, tr, vr, rh, met, clo, wme=0, units='SI')[source]#

Return the value of the Cooling Effect (CE) calculated in compliance with the ASHRAE 55 2020 Standard [55ASHRAE2023]. The CE of the elevated air speed is the value that, when subtracted equally from both the average air temperature and the mean radiant temperature, yields the same SET under still air as in the first SET calculation under elevated air speed. The cooling effect is calculated only for air speed higher than 0.1 m/s.

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, default in [°C] or [°F] if units = ‘IP’.

  • tr (float or list of floats) – Mean radiant temperature, default in [°C] or [°F] if units = ‘IP’.

  • vr (float or list of floats) – Relative air speed, default in [m/s] or [fps] if units = ‘IP’.

    Note

    The cooling effect is calculated only for air speed higher than 0.1 m/s. If the air speed is lower than 0.1 m/s the function will return 0.

    Note

    vr is the relative air speed caused by body movement and not the air speed measured by the air speed sensor. The relative air speed is the sum of the average air speed measured by the sensor plus the activity-generated air speed (Vag). Where Vag is the activity-generated air speed caused by motion of individual body parts. vr can be calculated using the function pythermalcomfort.utilities.v_relative().

  • rh (float or list of floats) – Relative humidity, [%].

  • met (float or list of floats) – Metabolic rate, [met].

  • clo (float or list of floats) – Clothing insulation, [clo].

    Note

    this is the basic insulation also known as the intrinsic clothing insulation value of the clothing ensemble (Icl,r), this is the thermal insulation from the skin surface to the outer clothing surface, including enclosed air layers, under actual environmental conditions. This value is not the total insulation (IT,r). The dynamic clothing insulation, clo, can be calculated using the function pythermalcomfort.utilities.clo_dynamic_ashrae().

  • wme (float or list of floats, optional) – External work, [met]. Defaults to 0.

  • units ({‘SI’, ‘IP’}, optional) – Select the SI (International System of Units) or the IP (Imperial Units) system. Supported values are ‘SI’ and ‘IP’. Defaults to ‘SI’.

Returns:

CE – A dataclass containing the Cooling Effect value. See CoolingEffect for more details. To access the ce value, use the ce attribute of the returned CoolingEffect instance, e.g., result.ce.

Notes

Note

Limitations: - This equation may not be accurate for extreme temperature ranges.

Examples

from pythermalcomfort.models import cooling_effect

result = cooling_effect(tdb=25, tr=25, vr=0.3, rh=50, met=1.2, clo=0.5)
print(result.ce)  # 1.68

result = cooling_effect(
    tdb=[25, 77],
    tr=[25, 77],
    vr=[0.3, 1.64],
    rh=[50, 50],
    met=[1.2, 1],
    clo=[0.5, 0.6],
    units="IP",
)
print(result.ce)  # [0, 3.95]
class pythermalcomfort.classes_return.CE(ce)[source]#

Dataclass to represent the Cooling Effect (CE).

Variables:

ce (float or list of floats) – Cooling Effect value.

Discomfort Index (DI)#

pythermalcomfort.models.discomfort_index.discomfort_index(tdb, rh)[source]#

Calculate the Discomfort Index (DI).

The index is essentially an effective temperature based on air temperature and humidity. The discomfort index is usually divided into 6 discomfort categories and it only applies to warm environments: [Polydoros2015]

  • class 1 - DI < 21 °C - No discomfort

  • class 2 - 21 <= DI < 24 °C - Less than 50% feels discomfort

  • class 3 - 24 <= DI < 27 °C - More than 50% feels discomfort

  • class 4 - 27 <= DI < 29 °C - Most of the population feels discomfort

  • class 5 - 29 <= DI < 32 °C - Everyone feels severe stress

  • class 6 - DI >= 32 °C - State of medical emergency

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C].

  • rh (float or list of floats) – Relative humidity, [%].

Returns:

DI – A dataclass containing the Discomfort Index and its classification. See DI for more details. To access the di and discomfort_condition values, use the respective attributes of the returned DI instance, e.g., result.di.

Examples

from pythermalcomfort.models import discomfort_index

result = discomfort_index(tdb=25, rh=50)
print(result.di)  # 22.1
print(result.discomfort_condition)  # Less than 50% feels discomfort

result = discomfort_index(tdb=[25, 30], rh=[50, 60])
print(result.di)  # [22.1, 27.3]
print(
    result.discomfort_condition
)  # ['Less than 50% feels discomfort', 'Most of the population feels discomfort']
class pythermalcomfort.classes_return.DI(di, discomfort_condition)[source]#

Dataclass to represent the Discomfort Index (DI) and its classification.

Variables:
  • di (float or list of floats) – Discomfort Index, [°C].

  • discomfort_condition (str or list of str) – Classification of the thermal comfort conditions according to the discomfort index.

Environmental Stress Index (ESI)#

pythermalcomfort.models.esi.esi(tdb, rh, sol_radiation_global, round_output=True)[source]#

Calculate the Environmental Stress Index (ESI) [Moran2001].

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C].

  • rh (float or list of floats) – Relative humidity, [%].

  • sol_radiation_global (float or list of floats) – Global solar radiation, [W/m2].

  • round_output (bool, optional) – If True, rounds output value. If False, it does not round it. Defaults to True.

Returns:

ESI – A dataclass containing the Environmental Stress Index. See ESI for more details. To access the esi value, use the esi attribute of the returned ESI instance, e.g., result.esi.

Examples

from pythermalcomfort.models import esi

result = esi(tdb=30.2, rh=42.2, sol_radiation_global=766)
print(result.esi)  # 26.2

result = esi(tdb=[30.2, 27.0], rh=[42.2, 68.8], sol_radiation_global=[766, 289])
print(result.esi)  # [26.2, 25.6]
class pythermalcomfort.classes_return.ESI(esi)[source]#

Dataclass to represent the Environmental Stress Index (ESI).

Variables:

esi (float or list of floats) – Environmental Stress Index.

Gagge two-node model#

pythermalcomfort.models.two_nodes_gagge.two_nodes_gagge(tdb, tr, v, rh, met, clo, wme=0, body_surface_area=1.8258, p_atm=101325, position='standing', max_skin_blood_flow=90, round_output=True, max_sweating=500, w_max=False, calculate_ce=False)[source]#

Gagge Two-node model of human temperature regulation Gagge et al (1986) [Gagge1986].

[Gagge1986] This model can be used to calculate a variety of indices, including:

  • Gagge’s version of Fanger’s Predicted Mean Vote (PMV). This function uses the Fanger’s PMV equations but it replaces the heat loss and gain terms with those calculated by the two-node model developed by Gagge et al. (1986) [Gagge1986].

  • PMV SET and the predicted thermal sensation based on SET [Gagge1986]. This function is similar in all aspects to the pythermalcomfort.models.pmv_gagge() however, it uses the pythermalcomfort.models.set() equation to calculate the dry heat loss by convection.

  • Thermal discomfort (DISC) as the relative thermoregulatory strain necessary to restore a state of comfort and thermal equilibrium by sweating [Gagge1986]. DISC is described numerically as: comfortable and pleasant (0), slightly uncomfortable but acceptable (1), uncomfortable and unpleasant (2), very uncomfortable (3), limited tolerance (4), and intolerable (5). The range of each category is ± 0.5 numerically. In the cold, the classical negative category descriptions used for Fanger’s PMV apply [Gagge1986].

  • Heat gains and losses via convection, radiation, and conduction.

  • The Standard Effective Temperature (SET).

  • The New Effective Temperature (ET).

  • The Predicted Thermal Sensation (TSENS).

  • The Predicted Percent Dissatisfied Due to Draft (PD).

  • Predicted Percent Satisfied With the Level of Air Movement (PS).

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C].

  • tr (float or list of floats) – Mean radiant temperature, [°C].

  • v (float or list of floats) – Air speed, [m/s].

  • rh (float or list of floats) – Relative humidity, [%].

  • met (float or list of floats) – Metabolic rate, [met].

  • clo (float or list of floats) – Clothing insulation, [clo].

  • wme (float or list of floats, optional) – External work, [met]. Defaults to 0.

  • body_surface_area (float or list of floats, optional) – Body surface area, default value 1.8258 [m2]. Defaults to 1.8258.

    Note

    The body surface area can be calculated using the function pythermalcomfort.utilities.body_surface_area().

  • p_atm (float or list of floats, optional) – Atmospheric pressure, default value 101325 [Pa]. Defaults to 101325.

  • position (str, optional) – Select either “sitting” or “standing”. Defaults to “standing”.

  • max_skin_blood_flow (float or list of floats, optional) – Maximum blood flow from the core to the skin, [kg/h/m2]. Defaults to 90.

  • round_output (bool, optional) – If True, rounds output value. If False, it does not round it. Defaults to True.

  • max_sweating (float or list of floats, optional) – Maximum rate at which regulatory sweat is generated, [kg/h/m2]. Defaults to 500.

  • w_max (float or list of floats, optional) – Maximum skin wettedness (w) adimensional. Ranges from 0 and 1. Defaults to False.

Returns:

GaggeTwoNodes – A dataclass containing the results of the two-node model of human temperature regulation. See GaggeTwoNodes for more details. To access the results, use the corresponding attributes of the returned GaggeTwoNodes instance, e.g., result.e_skin.

Examples

from pythermalcomfort.models import two_nodes_gagge

result = two_nodes_gagge(tdb=25, tr=25, v=0.1, rh=50, clo=0.5, met=1.2)
print(result.w)  # 100.0

result = two_nodes_gagge(tdb=[25, 25], tr=25, v=0.3, rh=50, met=1.2, clo=0.5)
print(result.e_skin)  # [100.0, 100.0]
class pythermalcomfort.classes_return.GaggeTwoNodes(e_skin, e_rsw, e_max, q_sensible, q_skin, q_res, t_core, t_skin, m_bl, m_rsw, w, w_max, set, et, pmv_gagge, pmv_set, disc, t_sens)[source]#

Dataclass to represent the results of the two-node model of human temperature regulation.

Variables:
  • e_skin (float or list of floats) – Total rate of evaporative heat loss from skin, [W/m2]. Equal to e_rsw + e_diff.

  • e_rsw (float or list of floats) – Rate of evaporative heat loss from sweat evaporation, [W/m2].

  • e_max (float or list of floats) – Maximum rate of evaporative heat loss from skin, [W/m2].

  • q_sensible (float or list of floats) – Sensible heat loss from skin, [W/m2].

  • q_skin (float or list of floats) – Total rate of heat loss from skin, [W/m2]. Equal to q_sensible + e_skin.

  • q_res (float or list of floats) – Total rate of heat loss through respiration, [W/m2].

  • t_core (float or list of floats) – Core temperature, [°C].

  • t_skin (float or list of floats) – Skin temperature, [°C].

  • m_bl (float or list of floats) – Skin blood flow, [kg/h/m2].

  • m_rsw (float or list of floats) – Rate at which regulatory sweat is generated, [mL/h/m2].

  • w (float or list of floats) – Skin wettedness, adimensional. Ranges from 0 to 1.

  • w_max (float or list of floats) – Skin wettedness (w) practical upper limit, adimensional. Ranges from 0 to 1.

  • set (float or list of floats) – Standard Effective Temperature (SET).

  • et (float or list of floats) – New Effective Temperature (ET).

  • pmv_gagge (float or list of floats) – PMV Gagge.

  • pmv_set (float or list of floats) – PMV SET.

  • disc (float or list of floats) – Thermal discomfort.

  • t_sens (float or list of floats) – Predicted Thermal Sensation.

Two-node model sleep#

pythermalcomfort.models.two_nodes_gagge_sleep.two_nodes_gagge_sleep(tdb, tr, v, rh, clo, thickness_quilt, wme=0, p_atm=101325, **kwargs)[source]#

Adaption of the Gagge two-node model for sleep thermal environment, developed by Yan, S., Xiong, J., Kim, J. and de Dear, R. [Yan2022].

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C].

    Note

    tdb, tr, v, rh, clo and thickness must have the same length. This length will be the duration of the simulation.

  • tr (float or list of floats) – Mean radiant temperature, [°C].

  • v (float or list of floats) – Air speed, [m/s].

  • rh (float or list of floats) – Relative humidity, [%].

  • clo (float or list of floats) – Clothing insulation, [clo].

  • thickness_quilt (float or list of floats) – Thickness of the quilt. [cm].

  • wme (float, optional) – External work, [met]. Defaults to 0.

  • p_atm (float, optional) – Atmospheric pressure, default value 101325 [Pa]. Defaults to 101325.

  • **kwargs (dict) – Keyword arguments:

    • ltimeint, optional

      Number of time steps for each iteration. Defaults to 1.

    • heightfloat, optional

      Height of the person, [cm]. Defaults to 171.

    • weightfloat, optional

      Weight of the person, [kg]. Defaults to 70.

    • c_swfloat, optional

      Driving coefficient for regulatory sweating. Defaults to 170.

    • c_dilfloat, optional

      Driving coefficient for vasodilation. Defaults to 120.

    • c_strfloat, optional

      Driving coefficient for vasoconstriction. Defaults to 0.5.

    • temp_skin_neutralfloat, optional

      Skin temperature at neutral conditions, [°C]. Defaults to 33.7.

    • temp_core_neutralfloat, optional

      Core temperature at neutral conditions, [°C]. Defaults to 36.8.

    • e_skinfloat, optional

      Total evaporative heat loss, [W]. Defaults to 0.094.

    • alfafloat, optional

      Dynamic fraction of total body mass assigned to the skin node. Defaults to 0.1.

    • skin_blood_flowfloat, optional

      Skin-blood-flow rate per unit surface area, [kg/h/m2]. Defaults to 6.3.

    • met_shiveringfloat, optional

      Metabolic rate due to shivering, [met]. Defaults to 0.

Returns:

GaggeTwoNodesSleep – A dataclass containing the results of the Gagge two-node model for sleep thermal environment. See GaggeTwoNodesSleep for more details. To access the results, use the corresponding attributes of the returned instance, e.g. result.e_skin.

class pythermalcomfort.classes_return.GaggeTwoNodesSleep(set, t_core, t_skin, wet, t_sens, disc, e_skin, met_shivering, alfa, skin_blood_flow)[source]#

Dataclass to represent the results of the two-node sleep model.

Variables:
  • set (float or list of floats) – Standard Effective Temperature (SET).

  • t_core (float or list of floats) – Core temperature, [°C].

  • t_skin (float or list of floats) – Skin temperature, [°C].

  • wet (float or list of floats) – Skin wettedness, adimensional. Ranges from 0 to 1.

  • t_sens (float or list of floats) – Predicted Thermal Sensation.

  • disc (float or list of floats) – Thermal discomfort.

  • e_skin (float or list of floats) – Total rate of evaporative heat loss from skin, [W/m2]. Equal to e_rsw + e_diff.

  • met_shivering (float or list of floats) – Metabolic rate due to shivering, [W/m2].

  • alfa (float or list of floats) – Dynamic fraction of total body mass assigned to the skin node (dimensionless).

  • skin_blood_flow (float or list of floats) – Skin-blood-flow rate per unit surface area, [kg/h/m2].

Gagge-Ji two-node model#

pythermalcomfort.models.two_nodes_gagge_ji.two_nodes_gagge_ji(tdb, tr, v, met, clo, vapor_pressure, wme=0, body_surface_area=1.8258, p_atm=101325, position='sitting', **kwargs)[source]#

Two-node model for older people under hot and cold exposures proposed by Ji et al. [Ji2022].

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C].

  • tr (float or list of floats) – Mean radiant temperature, [°C].

  • v (float or list of floats) – Air speed, [m/s].

  • met (float or list of floats) – Metabolic rate, [met].

  • clo (float or list of floats) – Clothing insulation, [clo].

  • vapor_pressure (float or list of floats) – Vapor pressure, [torr].

    Note

    Vapor pressure can be calculated using the relative humidity and the saturation vapor pressure, which can be computed using the function pythermalcomfort.utilities.p_sat_torr().

  • wme (float or list of floats) – External work, [met]. Defaults to 0.

  • body_surface_area (float or list of floats) – Body surface area, [m2]. Defaults to 1.8258.

    Note

    Body surface area can be calculated with weight and height using the function pythermalcomfort.utilities.body_surface_area().

  • p_atm (float or list of floats) – Atmospheric pressure, [Pa]. Defaults to 101325.

  • position (str, optional) – Select either “sitting” or “standing”. Defaults to “sitting”.

Other Parameters:
  • body_weight (float, optional) – Body weight, [kg]. Defaults to 70 kg.

  • length_time_simulation (int, optional) – Length of the simulation, [minutes]. Defaults to 120 minutes.

  • initial_skin_temp (float, optional) – Initial skin temperature, [°C]. Defaults to 36.8 °C.

  • initial_core_temp (float, optional) – Initial core temperature, [°C]. Defaults to 36.49 °C.

  • acclimatized (bool, optional) – If True, the model assumes the person is acclimatized to heat. Defaults to True.

Returns:

GaggeTwoNodesJi – A dataclass containing the simulated core and skin temperatures over time. See GaggeTwoNodesJi for more details. To access the individual attributes, use the corresonding attrubute of the returned instance, e.g. result.t_core or result.t_skin.

Example

from pythermalcomfort.models import two_nodes_gagge_ji
from pythermalcomfort.utilities import body_surface_area
from pythermalcomfort.utilities import p_sat_torr

rh = 20
vapor_pressure = rh * p_sat_torr(tdb=36.5) / 100

result = two_nodes_gagge_ji(
    tdb=36.5,
    tr=36.5,
    v=0.25,
    met=0.95,
    clo=0.1,
    vapor_pressure=vapor_pressure,
    wme=0,
    body_surface_area=body_surface_area(weight=80.1, height=1.8),
    p_atm=101325,
    position="sitting",
    acclimatized=True,
    body_weight=80.1,
    length_time_simulation=120,
)
class pythermalcomfort.classes_return.GaggeTwoNodesJi(t_core, t_skin)[source]#

Dataclass to represent the results of the Gagge-Ji model of human temperature.

Variables:
  • t_core (float or list of floats) – Core temperature, [°C].

  • t_skin (float or list of floats) – Skin temperature, [°C].

Heat Index (HI)#

The Heat Index (HI) is a commonly used metric to estimate apparent temperature (AT) incorporating the effects of humidity based on Steadman’s model [Steadman1979] of human thermoregulation.

Lu and Romps (2022) [lu] found that Steadman’s model produces unrealistic results under extreme conditions, such as excessively hot and humid or cold and dry environments, rendering the heat index undefined. For instance, at 80% relative humidity, the heat index is only valid within a temperature range of 288–304 K. To address this issue, Lu and Romps (2022) [lu] developed a new model that extends the range of validity of the heat index.

pythermalcomfort therefore includes two equations to calculate the Heat Index. One in accordance with the new Lu and Romps (2022) model which is an extension of the first version of Steadman’s (1979) apparent temperature heat_index_lu. The other is developed by Rothfusz (1990) and it is a simplified model derived by multiple regression analysis in temperature and relative humidity from the first version of Steadman’s (1979) apparent temperature (AT) [Rothfusz1990] heat_index_rothfusz.

Heat Index (HI) Lu and Romps (2022)#

pythermalcomfort.models.heat_index_lu.heat_index_lu(tdb, rh, round_output=True)[source]#

Calculate the Heat Index (HI) in accordance with the Lu and Romps (2022) model [lu].

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C].

  • rh (float or list of floats) – Relative humidity, [%].

  • round_output (bool, optional) – If True, rounds output value. If False, it does not round it. Defaults to True.

Returns:

HI – A dataclass containing the Heat Index. See HI for more details. To access the hi value, use the hi attribute of the returned HI instance, e.g., result.hi.

Examples

from pythermalcomfort.models import heat_index_lu

result = heat_index_lu(tdb=25, rh=50)
print(result.hi)  # 25.9
class pythermalcomfort.classes_return.HI(hi, stress_category=None)[source]#

Dataclass to represent the Heat Index (HI).

Variables:

hi (float or list of floats) – Heat Index, [°C] or [°F] depending on the units.

Heat Index (HI) Rothfusz (1990)#

pythermalcomfort.models.heat_index_rothfusz.heat_index_rothfusz(tdb, rh, round_output=True, limit_inputs=True)[source]#

Calculate the Heat Index (HI) in accordance with the Rothfusz (1990) model [Rothfusz1990].

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C].

  • rh (float or list of floats) – Relative humidity, [%].

  • round_output (bool, optional) – If True, rounds output value. If False, it does not round it. Defaults to True.

  • limit_inputs (bool, optional) – If True, limits the inputs to the standard applicability limits. Defaults to True.

    Note

    By default, if the inputs are outside the standard applicability limits the function returns NaN. If False returns heat index values even if input values are outside the applicability limits of the model.

    The applicability limit is tdb >= 27°C.

Returns:

HI – A dataclass containing the Heat Index. See HI for more details. To access the hi value, use the hi attribute of the returned HI instance, e.g., result.hi.

Examples

from pythermalcomfort.models import heat_index_rothfusz

result = heat_index_rothfusz(tdb=29, rh=50)
print(result.hi)  # 29.7
print(result.stress_category)  # "caution"
class pythermalcomfort.classes_return.HI(hi, stress_category=None)[source]

Dataclass to represent the Heat Index (HI).

Variables:

hi (float or list of floats) – Heat Index, [°C] or [°F] depending on the units.

Humidex#

pythermalcomfort.models.humidex.humidex(tdb, rh, model='rana', round_output=True)[source]#

Calculate the humidex (short for “humidity index”). It has been developed by the Canadian Meteorological service. It was introduced in 1965 and then it was revised by Masterson and Richardson (1979) [Masterson1979]. It aims to describe how hot, humid weather is felt by the average person. The Humidex differs from the heat index in being related to the dew point rather than relative humidity [Havenith2016].

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C].

  • rh (float or list of floats) – Relative humidity, [%].

  • model (str, optional) – The model to be used for the calculation. Options are ‘rana’ and ‘masterson’. Defaults to ‘rana’.

    Note

    The ‘rana’ model is the Humidex model proposed by Rana et al. (2013). The ‘masterson’ model is the Humidex model proposed by Masterson and Richardson (1979) [Masterson1979].

  • round_output (bool, optional) – If True, rounds output value. If False, it does not round it. Defaults to True.

Returns:

Humidex – A dataclass containing the Humidex value and its discomfort category. See Humidex for more details. To access the humidex and discomfort values, use the respective attributes of the returned Humidex instance, e.g., result.humidex.

Examples

from pythermalcomfort.models import humidex

result = humidex(tdb=25, rh=50)
print(result.humidex)  # 28.2
print(result.discomfort)  # Little or no discomfort

result = humidex(tdb=[25, 30], rh=[50, 60], round_output=False)
print(result.humidex)  # [28.2, 39.1]
print(result.discomfort)
# ['Little or no discomfort', 'Evident discomfort']
class pythermalcomfort.classes_return.Humidex(humidex, discomfort)[source]#

Dataclass to represent the Humidex and its discomfort category.

Variables:
  • humidex (float or list of floats) – Humidex value, [°C].

  • discomfort (str or list of str) – Degree of comfort or discomfort as defined in Havenith and Fiala (2016).

Joint system thermoregulation model (JOS-3)#

class pythermalcomfort.models.jos3.JOS3(height=1.72, weight=74.43, fat=15, age=20, sex='male', ci=2.59, bmr_equation='harris-benedict', bsa_equation='dubois')[source]#

JOS-3 model simulates human thermal physiology including skin temperature, core temperature, sweating rate, etc. for the whole body and 17 local body parts.

This model was developed at Shin-ichi Tanabe Laboratory, Waseda University and was derived from 65 Multi-Node model (https://doi.org/10.1016/S0378-7788(02)00014-2) and JOS-2 model (https://doi.org/10.1016/j.buildenv.2013.04.013).

To use this model, create an instance of the JOS3 class with optional body parameters such as body height, weight, age, sex, etc.

Environmental conditions such as air temperature, mean radiant temperature, air velocity, etc. can be set using the setter methods. (ex. X.tdb, X.tr, X.v) If you want to set the different conditions in each body part, set them as a 17 lengths of list, dictionary, or numpy array format.

List or numpy array format input must be 17 lengths and means the order of “head”, “neck”, “chest”, “back”, “pelvis”, “left_shoulder”, “left_arm”, “left_hand”, “right_shoulder”, “right_arm”, “right_hand”, “left_thigh”, “left_leg”, “left_foot”, “right_thigh”, “right_leg” and “right_foot”.

The model output includes local and mean skin temperature, local core temperature, local and mean skin wettedness, and heat loss from the skin etc. The model output can be accessed using the dict_results() method and be converted to a csv file using the to_csv method. Each output parameter also can be accessed using getter methods. (ex. X.t_skin, X.t_skin_mean, X.t_core)

If you use this package, please cite us as follows and mention the version of pythermalcomfort used: Y. Takahashi, A. Nomoto, S. Yoda, R. Hisayama, M. Ogata, Y. Ozeki, S. Tanabe, Thermoregulation Model JOS-3 with New Open Source Code, Energy & Buildings (2020), doi: https://doi.org/10.1016/j.enbuild.2020.110575

Note: To maintain consistency in variable names for pythermalcomfort, some variable names differ from those used in the original paper.

Variables:
  • tdb (float or list of floats) – Dry bulb air temperature.

  • tr (float or list of floats) – Mean radiant temperature.

  • to (float or list of floats) – Operative temperature.

  • rh (float or list of floats) – Relative humidity.

  • v (float or list of floats) – Air velocity.

  • clo (float or list of floats) – Clothing insulation.

  • posture (str) – Posture of the subject.

  • par (float) – Physical activity ratio.

  • t_body (numpy.ndarray) – Body temperature.

  • bsa (numpy.ndarray) – Body surface area.

  • r_t (numpy.ndarray) – Radiative heat transfer coefficient.

  • r_et (numpy.ndarray) – Evaporative heat transfer coefficient.

  • w (numpy.ndarray) – Skin wettedness.

  • w_mean (float) – Mean skin wettedness.

  • t_skin_mean (float) – Mean skin temperature.

  • t_skin (numpy.ndarray) – Skin temperature.

  • t_core (numpy.ndarray) – Core temperature.

  • t_cb (numpy.ndarray) – Central blood temperature.

  • t_artery (numpy.ndarray) – Arterial blood temperature.

  • t_vein (numpy.ndarray) – Venous blood temperature.

  • t_superficial_vein (numpy.ndarray) – Superficial venous blood temperature.

  • t_muscle (numpy.ndarray) – Muscle temperature.

  • t_fat (numpy.ndarray) – Fat temperature.

  • body_names (list of str) – Names of the body parts.

  • results (dict) – Dictionary of the simulation results.

  • bmr (float) – Basal metabolic rate.

  • version (str) – Version of the JOS3 model.

Returns:

  • cardiac_output – cardiac output (the sum of the whole blood flow) [L/h]

  • dt – time step [sec]

  • q_res – heat loss by respiration [W]

  • q_skin2env – total heat loss from the skin (each body part) [W]

  • q_thermogenesis_total – total thermogenesis of the whole body [W]

  • simulation_time – simulation times [sec]

  • t_core – core temperature (each body part) [°C]

  • t_skin – skin temperature (each body part) [°C]

  • t_skin_mean – mean skin temperature [°C]

  • w – skin wettedness (each body part) [-]

  • w_mean – mean skin wettedness [-]

  • weight_loss_by_evap_and_res – weight loss by the evaporation and respiration of the whole body [g/sec]

  • age – age [years]

  • bf_ava_foot – AVA blood flow rate of one foot [L/h]

  • bf_ava_hand – AVA blood flow rate of one hand [L/h]

  • bf_core – core blood flow rate (each body part) [L/h]

  • bf_fat – fat blood flow rate (each body part) [L/h]

  • bf_muscle – muscle blood flow rate (each body part) [L/h]

  • bf_skin – skin blood flow rate (each body part) [L/h]

  • bsa – body surface area (each body part) [m2]

  • clo – clothing insulation (each body part) [clo]

  • e_max – maximum evaporative heat loss from the skin (each body part) [W]

  • e_skin – evaporative heat loss from the skin (each body part) [W]

  • e_sweat – evaporative heat loss from the skin by only sweating (each body part) [W]

  • fat – body fat rate [%]

  • height – body height [m]

  • par – physical activity ratio [-]

  • q_bmr_core – core thermogenesis by basal metabolism (each body part) [W]

  • q_bmr_fat – fat thermogenesis by basal metabolism (each body part) [W]

  • q_bmr_muscle – muscle thermogenesis by basal metabolism (each body part) [W]

  • q_bmr_skin – skin thermogenesis by basal metabolism (each body part) [W]

  • q_nst – core thermogenesis by non-shivering (each body part) [W]

  • q_res_latent – latent heat loss by respiration (each body part) [W]

  • q_res_sensible – sensible heat loss by respiration (each body part) [W]

  • q_shiv – core or muscle thermogenesis by shivering (each body part) [W]

  • q_skin2env_latent – latent heat loss from the skin (each body part) [W]

  • q_skin2env_sensible – sensible heat loss from the skin (each body part) [W]

  • q_thermogenesis_core – core total thermogenesis (each body part) [W]

  • q_thermogenesis_fat – fat total thermogenesis (each body part) [W]

  • q_thermogenesis_muscle – muscle total thermogenesis (each body part) [W]

  • q_thermogenesis_skin – skin total thermogenesis (each body part) [W]

  • q_work – core or muscle thermogenesis by work (each body part) [W]

  • r_et – total clothing evaporative heat resistance (each body part) [(m2*kPa)/W]

  • r_t – total clothing heat resistance (each body part) [(m2*K)/W]

  • rh – relative humidity (each body part) [%]

  • sex (str) – sex

  • t_artery – arterial temperature (each body part) [°C]

  • t_cb – central blood temperature [°C]

  • t_core_set – core set point temperature (each body part) [°C]

  • t_fat – fat temperature (each body part) [°C]

  • t_muscle – muscle temperature (each body part) [°C]

  • t_skin_set – skin set point temperature (each body part) [°C]

  • t_superficial_vein – superficial vein temperature (each body part) [°C]

  • t_vein – vein temperature (each body part) [°C]

  • tdb – dry bulb air temperature (each body part) [°C]

  • to – operative temperature (each body part) [°C]

  • tr – mean radiant temperature (each body part) [°C]

  • v – air velocity (each body part) [m/s]

  • weight – body weight [kg]

Examples

Build a model and set a body built Create an instance of the JOS3 class with optional body parameters such as body height, weight, age, sex, etc.

from pythermalcomfort.classes_return import get_attribute_values
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import os
from pythermalcomfort.models import JOS3
from pythermalcomfort.jos3_functions.parameters import (
    local_clo_typical_ensembles,
)

model = JOS3(
    height=1.7,
    weight=60,
    fat=20,
    age=30,
    sex="male",
    bmr_equation="japanese",
    bsa_equation="fujimoto",
)
# Set environmental conditions such as air temperature, mean radiant temperature using the setter methods.
# Set the first condition
# Environmental parameters can be input as int, float, list, dict, numpy array format.
model.tdb = 28  # Air temperature [°C]
model.tr = 30  # Mean radiant temperature [°C]
model.rh = 40  # Relative humidity [%]
model.v = np.asarray(  # Air velocity [m/s]
    [
        0.2,  # head
        0.4,  # neck
        0.4,  # chest
        0.1,  # back
        0.1,  # pelvis
        0.4,  # left shoulder
        0.4,  # left arm
        0.4,  # left hand
        0.4,  # right shoulder
        0.4,  # right arm
        0.4,  # right hand
        0.1,  # left thigh
        0.1,  # left leg
        0.1,  # left foot
        0.1,  # right thigh
        0.1,  # right leg
        0.1,  # right foot
    ]
)
model.clo = get_attribute_values(
    local_clo_typical_ensembles[
        "briefs, socks, undershirt, work jacket, work pants, safety shoes"
    ]["local_body_part"]
)
# par should be input as int, float.
model.par = 1.2  # Physical activity ratio [-], assuming a sitting position
# posture should be input as int (0, 1, or 2) or str ("standing", "sitting" or "lying").
# (0="standing", 1="sitting" or 2="lying")
model.posture = "sitting"  # Posture [-], assuming a sitting position

# Run JOS-3 model
model.simulate(
    times=30,  # Number of loops of a simulation
    dtime=60,  # Time delta [sec]. The default is 60.
)  # Exposure time = 30 [loops] * 60 [sec] = 30 [min]
# Set the next condition (You only need to change the parameters that you want to change)
model.to = 20  # Change operative temperature
model.v = {  # Air velocity [m/s], assuming to use a desk fan
    "head": 0.2,
    "neck": 0.4,
    "chest": 0.4,
    "back": 0.1,
    "pelvis": 0.1,
    "left_shoulder": 0.4,
    "left_arm": 0.4,
    "left_hand": 0.4,
    "right_shoulder": 0.4,
    "right_arm": 0.4,
    "right_hand": 0.4,
    "left_thigh": 0.1,
    "left_leg": 0.1,
    "left_foot": 0.1,
    "right_thigh": 0.1,
    "right_leg": 0.1,
    "right_foot": 0.1,
}
# Run JOS-3 model
model.simulate(
    times=60,  # Number of loops of a simulation
    dtime=60,  # Time delta [sec]. The default is 60.
)  # Additional exposure time = 60 [loops] * 60 [sec] = 60 [min]
# Set the next condition (You only need to change the parameters that you want to change)
model.tdb = 30  # Change air temperature [°C]
model.tr = 35  # Change mean radiant temperature [°C]
# Run JOS-3 model
model.simulate(
    times=30,  # Number of loops of a simulation
    dtime=60,  # Time delta [sec]. The default is 60.
)  # Additional exposure time = 30 [loops] * 60 [sec] = 30 [min]

# The easiest way to access the results is to use the `results` method
results = model.results()
# you can then use dot notation to access the results
print(
    results.t_skin_mean
)  # Print the mean skin temperature or you can use print(results['t_skin_mean'])
# some attributes have results for each body part, you can access them by using the body part name
print(results.t_skin.head)  # Print the skin temperature of the head

# You can also save the results as a pandas dataframe and plot them
df = pd.DataFrame(
    [results.t_skin.head, results.t_skin.pelvis]
).transpose()  # Make pandas.DataFrame
df.plot()  # Plot time series of local skin temperature.
plt.legend(["Head", "Pelvis"])  # Reset the legends
plt.ylabel("Skin temperature [°C]")  # Set y-label as 'Skin temperature [°C]'
plt.xlabel("Time [min]")  # Set x-label as 'Time [min]'
plt.show()  # Show the plot
__init__(height=1.72, weight=74.43, fat=15, age=20, sex='male', ci=2.59, bmr_equation='harris-benedict', bsa_equation='dubois')[source]#

Initialize a new instance of the JOS3 class, which models and simulates various physiological parameters related to human thermoregulation.

This class uses mathematical models to calculate and predict body temperature, basal metabolic rate, body surface area, and other related parameters.

Parameters:
  • height (float, optional) – Body height in meters.

  • weight (float, optional) – Body weight in kilograms.

  • fat (float, optional) – Fat percentage.

  • age (int, optional) – Age in years.

  • sex (str, optional) – Sex (“male” or “female”).

  • ci (float, optional) – Cardiac index in liters per minute per square meter.

  • bmr_equation (str, optional) – The equation used to calculate basal metabolic rate (BMR). Options are “harris-benedict” for Caucasian data (DOI: doi.org/10.1073/pnas.4.12.370) or “japanese” for Ganpule’s equation (DOI: doi.org/10.1038/sj.ejcn.1602645).

  • bsa_equation (str, optional) – The equation used to calculate body surface area (BSA). Choose one from pythermalcomfort.utilities.BodySurfaceAreaEquations.

Returns:

None.

Examples

Create an instance of the JOS3 class with optional body parameters:

jos3_model = JOS3(height=1.75, weight=70, age=25, sex="female")
dict_results()[source]#

Get simulation results as a dictionary.

This method returns the results of the JOS-3 model simulation as a dictionary, where each key corresponds to a specific parameter and the value is an array containing the time series data for that parameter.

Returns:

dict – A dictionary of the simulation results. Keys are parameter names, and values are arrays containing the time series data for each parameter.

Examples

Access the results after running a simulation:

jos3_model = JOS3(height=1.75, weight=70, age=25, sex="female")
jos3_model.simulate(times=30, dtime=60)
results = jos3_model.dict_results()
print(
    results["t_skin_mean"]
)  # Access the mean skin temperature time series
results()[source]#

Consolidate the results into a single JOS3Output instance. This makes it very easy to access the time series data for each parameter.

Returns:

JOS3Output – A single JOS3Output instance where each attribute is an array of values representing the time series data for that attribute.

Examples

from pythermalcomfort.models import JOS3

model = JOS3(height=1.75, weight=70, age=25, sex="female")
model.simulate(times=3, dtime=60)
output = model.results()
print(output.t_skin_mean)
print(output.t_skin.head)
simulate(times, dtime=60, output=True)[source]#

Run the JOS-3 model simulation.

This method executes the JOS-3 model for a specified number of loops, simulating human thermoregulation over time. The results of each simulation step can be recorded and accessed later.

Parameters:
  • times (int) – Number of loops of the simulation.

  • dtime (int or float, optional) – Time delta in seconds for each simulation step. Default is 60.

  • output (bool, optional) – If True, records the parameters at each simulation step. Default is True.

Returns:

None

Examples

Create an instance of the JOS3 class and run the simulation:

from pythermalcomfort.models import JOS3

# Create an instance of the JOS3 class
jos3_model = JOS3(height=1.75, weight=70, age=25, sex="female")

# Run the simulation for 30 loops with a time delta of 60 seconds
jos3_model.simulate(times=30, dtime=60)

# Access the results
results = jos3_model.dict_results()
print(results)
to_csv(path=None, folder=None, unit=True, meaning=True)[source]#

Export results as csv format.

Parameters:
  • path (str, optional) – Output path. If you don’t use the default file name, set a name. The default is None.

  • folder (str, optional) – Output folder. If you use the default file name with the current time, set a only folder path. The default is None.

  • unit (bool, optional) – Write units in csv file. The default is True.

  • meaning (bool, optional) – Write meanings of the parameters in csv file. The default is True.

Returns:

None

Examples

from pythermalcomfort.models import JOS3

# Create an instance of the JOS3 model
model = JOS3()

# Simulate for 60 steps
model.simulate(60)

# Export results to a CSV file
model.to_csv()

Normal Effective Temperature (NET)#

pythermalcomfort.models.net.net(tdb, rh, v, round_output=True)[source]#

Calculate the Normal Effective Temperature (NET). Missenard (1933) devised a formula for calculating effective temperature. The index establishes a link between the same condition of the organism’s thermoregulatory capability (warm and cold perception) and the surrounding environment’s temperature and humidity. The index is calculated as a function of three meteorological factors: air temperature, relative humidity of air, and wind speed. This index allows to calculate the effective temperature felt by a person. Missenard original equation was then used to calculate the Normal Effective Temperature (NET), by considering normal atmospheric pressure and a normal human body temperature (37°C). The NET is still in use in Germany, where medical check-ups for subjects working in the heat are decided on by prevailing levels of ET, depending on metabolic rates. The NET is also constantly monitored by the Hong Kong Observatory [Blazejczyk2012]. In central Europe the following thresholds are in use: <1°C = very cold; 1-9 = cold; 9-17 = cool; 17-21 = fresh; 21-23 = comfortable; 23-27 = warm; >27°C = hot [Blazejczyk2012].

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C].

  • rh (float or list of floats) – Relative humidity, [%].

  • v (float or list of floats) – Wind speed [m/s] at 1.2 m above the ground.

  • round_output (bool, optional) – If True, rounds output value. If False, it does not round it. Defaults to True.

Returns:

NET – A dataclass containing the Normal Effective Temperature. See Net for more details. To access the net value, use the net attribute of the returned Net instance, e.g., result.net.

Examples

from pythermalcomfort.models import net

result = net(tdb=37, rh=100, v=0.1)
print(result.net)  # 37.0

result = net(tdb=[37, 30], rh=[100, 60], v=[0.1, 0.5], round_output=False)
print(result.net)  # [37.0, 26.38977535]
class pythermalcomfort.classes_return.NET(net)[source]#

Dataclass to represent the Normal Effective Temperature (NET).

Variables:

net (float or list of floats) – Normal Effective Temperature, [°C].

Predicted Heat Strain (PHS) Index#

pythermalcomfort.models.phs.phs(tdb, tr, v, rh, met, clo, posture, wme=0, round_output=True, model='7933-2023', **kwargs)[source]#

Calculate the Predicted Heat Strain (PHS).

The PHS is calculated in compliance with the ISO 7933:2004 [7933ISO2004] or 2023 Standard [7933ISO2023]. The ISO 7933 provides a method for the analytical evaluation and interpretation of the thermal stress experienced by a subject in a hot environment. It describes a method for predicting the sweat rate and the internal core temperature that the human body will develop in response to the working conditions.

The PHS model can be used to predict the: heat by respiratory convection, heat flow by respiratory evaporation, steady state mean skin temperature, instantaneous value of skin temperature, heat accumulation associated with the metabolic rate, maximum evaporative heat flow at the skin surface, predicted sweat rate, predicted evaporative heat flow, and rectal temperature.

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C].

  • tr (float or list of floats) – Mean radiant temperature, [°C].

  • v (float or list of floats) – Air speed, [m/s].

  • rh (float or list of floats) – Relative humidity, [%].

  • met (float or list of floats) – Metabolic rate, [met].

  • clo (float or list of floats) – Clothing insulation, [clo].

  • posture (string or list of strings) – a string value presenting posture of person “sitting”, “standing”, or “crouching”

  • wme (float or list of floats) – external work, [met] default 0

  • round_output (bool, optional) – If True, rounds output value. If False, it does not round it. Defaults to True.

  • model (str, optional) – Select the model you want to use to calculate the PHS. The default option is “7933-2023”, and the other option is “7933-2004”.

Other Parameters:
  • limit_inputs (bool, optional) – If True, limits the input parameters to the standard’s applicability limits. Defaults to True.

    Note

    By default, if the inputs are outside the standard applicability limits the function returns nan. If False returns values even if input values are outside the applicability limits of the model.

    The 7933 limits are 15 < tdb [°C] < 50, 0 < tr [°C] < 60, 0 < vr [m/s] < 3, 1.7 < met [met] < 7.5, and 0.1 < clo [clo] < 1.

  • i_mst (float, optional) – Static moisture permeability index, [dimensionless]. Defaults to 0.38.

  • a_p (float, optional) – Fraction of the body surface covered by the reflective clothing, [dimensionless]. Defaults to 0.54.

  • drink (int, optional) – 1 if workers can drink freely, 0 otherwise. Defaults to 1.

  • weight (float, optional) – Body weight, [kg]. Defaults to 75.

  • height (float, optional) – Height, [m]. Defaults to 1.8.

  • walk_sp (float, optional) – Walking speed, [m/s]. Defaults to 0.

  • theta (float, optional) – Angle between walking direction and wind direction, [degrees]. Defaults to 0.

  • acclimatized (int, optional) – 100 if acclimatized subject, 0 otherwise. Defaults to 100.

  • duration (int, optional) – Duration of the work sequence, [minutes]. Defaults to 480.

  • f_r (float, optional) – Emissivity of the reflective clothing, [dimensionless]. Defaults to 0.97 in the 2004 standard and 0.42 in the 2023 standard.

  • t_sk (float, optional) – Mean skin temperature when worker starts working, [°C]. Defaults to 34.1.

  • t_cr (float, optional) – Mean core temperature when worker starts working, [°C]. Defaults to 36.8.

  • t_re (float, optional) – Mean rectal temperature when worker starts working, [°C]. If False in the 2004 standard, then t_re = t_cr, whereas in the 2023 standard t_re = 36.8 °C

  • t_cr_eq (float, optional) – Mean core temperature as a function of met when worker starts working, [°C]. If False in the 2004 standard, then t_cr_eq = t_cr, whereas in the 2023 standard t_cr_eq = 36.8 °C.

  • t_sk_t_cr_wg (float, optional) – Initial weighting fraction for skin/core temperature coupling, dimensionless.

  • sweat_rate_watt (float, optional) – Initial instantaneous regulatory sweat (evaporative) rate at the skin, per unit area, [W·m⁻²]. This is an instantaneous rate (W/m²) used at each simulation time step.

  • evap_load_wm2_min (float, optional) – Initial accumulated evaporative load per unit area. Input value is expected as an instantaneous rate in [W·m⁻²]; during the simulation the value is updated by adding instantaneous rates each minute and therefore the accumulated quantity represents the sum over minutes (units W·min·m⁻²). It is intended for carry over between consecutive simulation segments.

Returns:

PHS – A dataclass containing the Predicted Heat Strain. See PHS for more details. To access the individual attributes, use the corresponding attribute of the returned PHS instance, e.g., result.t_re.

Examples

from pythermalcomfort.models import phs

result = phs(
    tdb=40,
    tr=40,
    rh=33.85,
    v=0.3,
    met=2.5,
    clo=0.5,
    posture="standing",
    wme=0,
    duration=480,
)
print(result.t_re)  # 37.5

result = phs(
    tdb=[40, 45],
    tr=[40, 45],
    v=[0.3, 0.4],
    rh=[33.85, 40],
    met=[2.5, 2.6],
    clo=[0.5, 0.6],
    posture=["standing", "standing"],
    wme=[0, 0],
    duration=480,
)
print(result.t_re)  # [37.5 42.5]

# example: pass previous results as initial values to chain simulations
from pythermalcomfort.models import phs

# first simulation
result = phs(
    tdb=40,
    tr=40,
    v=0.3,
    rh=50,
    met=2.5,
    clo=0.5,
    posture="standing",
    wme=0,
    round_output=False,
    duration=60,
)
print(result.t_re)  # 37.8
# second simulation - using previous results as initial values
# NOTE: when chaining runs, prefer round_output=False to avoid rounding drift.
result = phs(
    tdb=40,
    tr=40,
    v=0.3,
    rh=50,
    met=2.5,
    clo=0.5,
    posture="standing",
    wme=0,
    round_output=False,
    duration=60,
    t_sk=result.t_sk,
    t_cr=result.t_cr,
    t_re=result.t_re,
    t_cr_eq=result.t_cr_eq,
    t_sk_t_cr_wg=result.t_sk_t_cr_wg,
    sweat_rate_watt=result.sweat_rate_watt,
    evap_load_wm2_min=result.evap_load_wm2_min,
)
print(result.t_re)  # 38.5
class pythermalcomfort.classes_return.PHS(t_re, t_sk, t_cr, t_cr_eq, t_sk_t_cr_wg, d_lim_loss_50, d_lim_loss_95, d_lim_t_re, sweat_loss_g, sweat_rate_watt, evap_load_wm2_min)[source]#

Dataclass to represent the Predicted Heat Strain (PHS).

Variables:
  • t_re (float or list of floats) – Rectal temperature, [°C].

  • t_sk (float or list of floats) – Skin temperature, [°C].

  • t_cr (float or list of floats) – Core temperature, [°C].

  • t_cr_eq (float or list of floats) – Core temperature as a function of the metabolic rate, [°C].

  • t_sk_t_cr_wg (float or list of floats) – Fraction of the body mass at the skin temperature.

  • d_lim_loss_50 (float or list of floats) – Maximum allowable exposure time limited by cumulative sweat mass loss for a mean worker (dehydration constraint), [minutes].

  • d_lim_loss_95 (float or list of floats) – Maximum allowable exposure time limited by cumulative sweat mass loss for 95% of the working population (dehydration constraint), [minutes].

  • d_lim_t_re (float or list of floats) – Maximum allowable exposure time for heat storage, [minutes].

  • sweat_loss_g (float or list of floats) – Cumulative evaporated sweat mass for the whole person over the simulated duration, [grams (g)]. This is a total mass per person (not per unit area).

  • sweat_rate_watt (float or list of floats) – Instantaneous evaporative heat flux due to regulatory sweating at the skin, per unit area, [W·m⁻²]. Used at each simulation time step.

  • evap_load_wm2_min (float or list of floats) – Accumulated evaporative load per unit area over the simulated duration, [W·min·m⁻²]. Computed as the running sum of the instantaneous evaporative heat flux (W·m⁻²) at each 1 minute step. Intended for chaining simulation segments.

Physiological Equivalent Temperature (PET)#

pythermalcomfort.models.pet_steady.pet_steady(tdb, tr, v, rh, met, clo, p_atm=1013.25, position='sitting', age=23, sex='male', weight=75, height=1.8, wme=0)[source]#

Calculate the steady physiological equivalent temperature (PET) using the Munich Energy-balance Model for Individuals (MEMI) to simulate the human body’s thermal state in a medically realistic manner.

PET is defined as the air temperature at which, in a typical indoor setting the heat budget of the human body is balanced with the same core and skin temperature as under the complex outdoor conditions to be assessed [Hoppe1999]. The following assumptions are made for the indoor reference climate: tdb = tr, v = 0.1 m/s, water vapour pressure = 12 hPa, clo = 0.9 clo, and met = 1.37 met + basic metabolism. PET allows a layperson to compare the total effects of complex thermal circumstances outside with his or her own personal experience indoors in this way. This function solves the heat balances without accounting for heat storage in the human body.

The PET was originally proposed by Hoppe [Hoppe1999]. In 2018, Walther and Goestchel [Walther2018] proposed a correction of the original model, purging the errors in the PET calculation routine, and implementing a state-of-the-art vapour diffusion model. Walther and Goestchel (2018) model is therefore used to calculate the PET.

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C].

  • tr (float or list of floats) – Mean radiant temperature, [°C].

  • v (float or list of floats) – Wind speed, [m/s].

  • rh (float or list of floats) – Relative humidity, [%].

  • met (float or list of floats) – Metabolic rate, [met].

  • clo (float or list of floats) – Clothing insulation, [clo].

  • p_atm (float or list of floats, optional) – Atmospheric pressure, [hPa]. Defaults to 1013.25.

  • position (str or list of str, optional) – Position of the person “sitting”, “standing”, “standing, forced convection”. Defaults to “sitting”.

  • age (int or list of ints, optional) – Age of the person. Defaults to 23.

  • sex (str or list of str, optional) – Sex of the person “male” or “female”. Defaults to “male”

  • weight (float or list of floats, optional) – Weight of the person, [kg]. Defaults to 75.

  • height (float or list of floats, optional) – Height of the person, [m]. Defaults to 1.8.

  • wme (float or list of floats, optional) – External work, [met]. Defaults to 0.

Returns:

PETSteady – A dataclass containing the Physiological Equivalent Temperature. See PETSteady for more details. To access the pet value, use the pet attribute of the returned PETSteady instance, e.g., result.pet.

Examples

from pythermalcomfort.models import pet_steady

result = pet_steady(tdb=25, tr=25, v=0.1, rh=50, met=1.2, clo=0.5)
print(result.pet)  # 24.67

result = pet_steady(
    tdb=[25, 30],
    tr=[25, 30],
    v=[0.1, 0.2],
    rh=[50, 60],
    met=[1.2, 1.4],
    clo=[0.5, 0.6],
)
print(result.pet)  # [24.67 31.46]
class pythermalcomfort.classes_return.PETSteady(pet)[source]#

Dataclass to represent the Physiological Equivalent Temperature (PET).

Variables:

pet (float or list of floats) – Physiological Equivalent Temperature.

Predicted Mean Vote (PMV) and Predicted Percentage of Dissatisfied (PPD)#

The Predicted Mean Vote (PMV) is an index that aims to predict the mean value of thermal sensation votes from a large group of people, based on a seven-point scale ranging from “cold” (-3) to “hot” (+3). It was developed by Fanger [Fanger1970].

The PMV is designed to predict the average thermal sensation of a large group of people exposed to the same environment [7730ISO2005]. It calculates the heat balance of a typical occupant and relates their thermal gains or losses to their predicted mean thermal sensation [55ASHRAE2023].

The PMV can be used to check if a thermal environment meets comfort criteria and to establish requirements for different levels of acceptability. The PMV model is applicable to healthy men and women exposed to indoor environments where thermal comfort is desirable, but moderate deviations from thermal comfort occur, in the design of new environments or the assessment of existing ones. The PMV is intended to be used for moderate thermal environments.

The PMV calculation considers several factors:
  • Dry-bulb air temperature (T db).

  • Mean radiant temperature (T r).

  • Relative humidity (RH).

  • Relative air velocity (V r), the relative air speed caused by body movement and not the air speed measured by the air speed sensor

  • Metabolic rate (M), which is the rate of heat production in the body. Metabolic rate data is available in the ASHRAE Handbook—Fundamentals and users should use their judgement to match activities to comparable activities in the table.

  • Clothing insulation (I cl,r), dynamic intrinsic insulation, this is the thermal insulation from the skin surface to the outer clothing surface, including enclosed air layers, under the environmental conditions.

The PMV model is applicable when the six main parameters are within specific intervals. These values are specified by the ASHRAE 55 [55ASHRAE2023] and ISO 7730 standards [7730ISO2005]. The ISO also states that the PMV model is only applicable for PMV between -2 and +2 [7730ISO2005].

There are several formulations of the PMV model that have been developed over the years. The two most commonly used are the original PMV model [Fanger1970] and the ASHRAE 55 PMV model [55ASHRAE2023]. Tartarini and Schiavon (2025) [Tartarini2025PMV] compared the accuracy of the PMV models implemented in the ISO 7730:2005 and ASHRAE 55:2023 standards and found that the ISO 7730:2005 model has a higher accuracy than the ASHRAE 55:2023 model. However, it should be noted that both PMV models have low accuracy in predicting thermal sensation votes, especially outside thermal neutrality [Tartarini2025PMV]. For this reason, it is recommended that the use of the PMV be restricted to values between -0.5 and +0.5 [Tartarini2025PMV], where an environment may be deemed thermally neutral by a large group of occupants.

The Predicted Percentage Dissatisfied (PPD) is an index that provides a quantitative prediction of the percentage of people likely to feel too warm or too cool in a given environment. The PPD is derived from the PMV. Specifically, the PPD predicts the number of thermally dissatisfied persons among a large group of people, where thermally dissatisfied people are those who would vote hot, warm, cool, or cold on a seven-point thermal sensation scale.

PMV formulations#

After Fanger developed the original PMV model which it is still included in the ISO 7330 in its original form, several other PMV formulations have been proposed. These include but are not limited to:

ISO 7730 - PMV and PPD#

pythermalcomfort.models.pmv_ppd_iso.pmv_ppd_iso(tdb, tr, vr, rh, met, clo, wme=0, model='7730-2005', units='SI', limit_inputs=True, round_output=True)[source]#

Return Predicted Mean Vote (PMV) and Predicted Percentage of Dissatisfied (PPD) calculated in accordance with the ISO 7730.

The ISO uses the same formulation of the PMV as published by Fanger [Fanger1970].

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C] in [°F] if units = ‘IP’

  • tr (float or list of floats) – Mean radiant temperature, [°C] in [°F] if units = ‘IP’

  • vr (float or list of floats) – Relative air speed, [m/s] in [fps] if units = ‘IP’

    Note

    vr is the relative air speed caused by body movement and not the air speed measured by the air speed sensor. The relative air speed is the sum of the average air speed measured by the sensor plus the activity-generated air speed (Vag). Where Vag is the activity-generated air speed caused by motion of individual body parts. vr can be calculated using the function pythermalcomfort.utilities.v_relative().

  • rh (float or list of floats) – Relative humidity, [%].

  • met (float or list of floats) – Metabolic rate, [met].

  • clo (float or list of floats) – Clothing insulation, [clo].

    Note

    this is the basic insulation also known as the intrinsic clothing insulation value of the clothing ensemble (Icl,r), this is the thermal insulation from the skin surface to the outer clothing surface, including enclosed air layers, under actual environmental conditions. This value is not the total insulation (IT,r). The dynamic clothing insulation, clo, can be calculated using the function pythermalcomfort.utilities.clo_dynamic_iso().

  • wme (float or list of floats, optional) – External work, [met]. Defaults to 0.

  • model (str, optional) – Select the model you want to use to calculate the PMV. Currently, the only option available is “7730-2005”.

  • units (str, optional) – Select the SI (International System of Units) or the IP (Imperial Units) system. Supported values are ‘SI’ and ‘IP’. Defaults to ‘SI’.

  • limit_inputs (bool, optional) – If True, limits the inputs to the standard applicability limits. Defaults to True.

    Note

    By default, if the inputs are outside the standard applicability limits the function returns nan. If False returns pmv and ppd values even if input values are outside the applicability limits of the model.

    The ISO 7730 2005 limits are 10 < tdb [°C] < 30, 10 < tr [°C] < 40, 0 < vr [m/s] < 1, 0.8 < met [met] < 4, 0 < clo [clo] < 2, and -2 < PMV < 2.

  • round_output (bool, optional) – If True, rounds output value. If False, it does not round it. Defaults to True.

Returns:

PMVPPD – A dataclass containing the Predicted Mean Vote and Predicted Percentage of Dissatisfied. See PMVPPD for more details. To access the pmv, ppd, tsv values, use the corresponding attributes of the returned PMVPPD instance, e.g., result.pmv.

Examples

from pythermalcomfort.models import pmv_ppd_iso
from pythermalcomfort.utilities import v_relative

tdb = 25
tr = 25
rh = 50
v = 0.1
met = 1.4
clo_dynamic = 0.5
# calculate relative air speed
v_r = v_relative(v=v, met=met)
results = pmv_ppd_iso(
    tdb=tdb,
    tr=tr,
    vr=v_r,
    rh=rh,
    met=met,
    clo=clo_dynamic,
    model="7730-2005",
)
print(results.pmv)  # 0.17
print(results.ppd)  # 5.6

result = pmv_ppd_iso(
    tdb=[22, 25],
    tr=25,
    vr=0.1,
    rh=50,
    met=1.4,
    clo=0.5,
    model="7730-2005",
)
print(result.pmv)  # [-0.  0.41]
print(result.ppd)  # [5.  8.5]
class pythermalcomfort.classes_return.PMVPPD(pmv, ppd, tsv)[source]#

Dataclass to represent the Predicted Mean Vote (PMV) and Predicted Percentage of Dissatisfied (PPD).

Variables:
  • pmv (float or list of floats) – Predicted Mean Vote.

  • ppd (float or list of floats) – Predicted Percentage of Dissatisfied.

  • tsv (str or list of strings) – Predicted thermal sensation vote.

ASHRAE 55 - PMV and PPD#

pythermalcomfort.models.pmv_ppd_ashrae.pmv_ppd_ashrae(tdb, tr, vr, rh, met, clo, wme=0, model='55-2023', units='SI', limit_inputs=True, airspeed_control=True, round_output=True)[source]#

Return Predicted Mean Vote (PMV) and Predicted Percentage of Dissatisfied (PPD) calculated in accordance with the ASHRAE 55 Standard.

While the PMV equation is the same for both the ISO and ASHRAE standards, in the ASHRAE 55 PMV equation, the SET is used to calculate the cooling effect first, this is then subtracted from both the air and mean radiant temperatures, and the differences are used as input to the PMV model, while the airspeed is set to 0.1m/s.

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C] in [°F] if units = ‘IP’

  • tr (float or list of floats) – Mean radiant temperature, [°C] in [°F] if units = ‘IP’

  • vr (float or list of floats) – Relative air speed, [m/s] in [fps] if units = ‘IP’

    Note

    vr is the relative air speed caused by body movement and not the air speed measured by the air speed sensor. The relative air speed is the sum of the average air speed measured by the sensor plus the activity-generated air speed (Vag). Where Vag is the activity-generated air speed caused by motion of individual body parts. vr can be calculated using the function pythermalcomfort.utilities.v_relative().

  • rh (float or list of floats) – Relative humidity, [%].

  • met (float or list of floats) – Metabolic rate, [met].

  • clo (float or list of floats) – Clothing insulation, [clo].

    Note

    this is the basic insulation also known as the intrinsic clothing insulation value of the clothing ensemble (Icl,r), this is the thermal insulation from the skin surface to the outer clothing surface, including enclosed air layers, under actual environmental conditions. This value is not the total insulation (IT,r). The dynamic clothing insulation, clo, can be calculated using the function pythermalcomfort.utilities.clo_dynamic_ashrae().

  • wme (float or list of floats, optional) – External work, [met]. Defaults to 0.

  • model (str, optional) – Select the version of the ASHRAE 55 Standard to use. Currently, the only option available is “55-2023”.

  • units (str, optional) – Select the SI (International System of Units) or the IP (Imperial Units) system. Supported values are ‘SI’ and ‘IP’. Defaults to ‘SI’.

  • limit_inputs (bool, optional) – If True, limits the inputs to the standard applicability limits. Defaults to True.

    Note

    By default, if the inputs are outside the standard applicability limits, the function returns NaN. If False returns pmv and ppd values even if input values are outside the applicability limits of the model.

    The ASHRAE 55 2020 limits are 10 < tdb [°C] < 40, 10 < tr [°C] < 40, 0 < vr [m/s] < 2, 1 < met [met] < 4, and 0 < clo [clo] < 1.5.

  • airspeed_control (bool, optional) – By default, this function assumes that the occupant has control over the airspeed. In this case, the ASHRAE 55 Standard does not impose any airspeed limits. On the other hand, if the occupant has no control over the airspeed, the ASHRAE 55 imposes an upper limit for v which varies as a function of the operative temperature, for more information please consult the Standard.

  • round_output (bool, optional) – If True, rounds output value. If False, it does not round it. Defaults to True.

Returns:

PMVPPDAshrae – A dataclass containing the Predicted Mean Vote and Predicted Percentage of Dissatisfied. See PMVPPDAshrae for more details. To access the pmv, ppd, tsv, and compliance values, use the corresponding attributes of the returned PMVPPDAshrae instance, e.g., result.pmv, result.compliance.

Examples

from pythermalcomfort.models import pmv_ppd_ashrae
from pythermalcomfort.utilities import v_relative, clo_dynamic_ashrae

tdb = 25
tr = 25
rh = 50
v = 0.1
met = 1.4
clo = 0.5
# calculate relative air speed
v_r = v_relative(v=v, met=met)
# calculate dynamic clothing
clo_d = clo_dynamic_ashrae(clo=clo, met=met)
results = pmv_ppd_ashrae(
    tdb=tdb, tr=tr, vr=v_r, rh=rh, met=met, clo=clo_d, model="55-2023"
)
print(results.pmv)  # 0.0
print(results.ppd)  # 5.0
print(results.compliance)  # True

result = pmv_ppd_ashrae(
    tdb=[22, 25, 28],
    tr=25,
    vr=0.1,
    rh=50,
    met=1.4,
    clo=0.5,
    model="55-2023",
)
print(result)
class pythermalcomfort.classes_return.PMVPPDAshrae(pmv, ppd, tsv, compliance)[source]#

Dataclass to represent the Predicted Mean Vote (PMV) and Predicted Percentage of Dissatisfied (PPD) calculated in accordance with ASHRAE Standard 55.

Variables:
  • pmv (float or list of floats) – Predicted Mean Vote.

  • ppd (float or list of floats) – Predicted Percentage of Dissatisfied.

  • tsv (str or list of strings) – Predicted thermal sensation vote.

  • compliance (bool or list of bools) – True if PMV is within the acceptable range (-0.5 < PMV < 0.5) according to ASHRAE Standard 55-2023. When limit_inputs=True and any input is outside the standard applicability limits, compliance will be nan.

Adaptive Predicted Mean Vote (aPMV)#

pythermalcomfort.models.pmv_a.pmv_a(tdb, tr, vr, rh, met, clo, a_coefficient, wme=0, units='SI', limit_inputs=True)[source]#

Return Adaptive Predicted Mean Vote (aPMV) [Yao2009]. This index was developed by Yao, R. et al. (2009). The model takes into account factors such as culture, climate, social, psychological, and behavioral adaptations, which have an impact on the senses used to detect thermal comfort. This model uses an adaptive coefficient (λ) representing the adaptive factors that affect the sense of thermal comfort.

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, default in [°C] or [°F] if units = ‘IP’.

  • tr (float or list of floats) – Mean radiant temperature, default in [°C] or [°F] if units = ‘IP’.

  • vr (float or list of floats) – Relative air speed, default in [m/s] or [fps] if units = ‘IP’.

    Note

    vr is the sum of the average air speed measured by the sensor and the activity-generated air speed (Vag). Calculate vr using pythermalcomfort.utilities.v_relative().

  • rh (float or list of floats) – Relative humidity, [%].

  • met (float or list of floats) – Metabolic rate, [met].

  • clo (float or list of floats) – Clothing insulation, [clo].

    Note

    Correct for body movement effects using pythermalcomfort.utilities.clo_dynamic_iso().

  • a_coefficient (float) – Adaptive coefficient.

  • wme (float or list of floats, optional) – External work, [met], default is 0.

  • units (str, optional) – Units system, ‘SI’ or ‘IP’. Defaults to ‘SI’.

  • limit_inputs (bool, optional) – If True, returns nan for inputs outside standard limits. Defaults to True.

    Note

    ISO 7730 2005 limits: 10 < tdb [°C] < 30, 10 < tr [°C] < 40, 0 < vr [m/s] < 1, 0.8 < met [met] < 4, 0 < clo [clo] < 2, -2 < PMV < 2.

Returns:

APMV – A dataclass containing the Predicted Mean Vote (a_pmv). See AdaptivePMV for more details. To access the a_pmv value, use the a_pmv attribute of the returned AdaptivePMV instance, e.g., result.a_pmv.

Examples

from pythermalcomfort.models import pmv_a
from pythermalcomfort.utilities import v_relative, clo_dynamic_iso

v = 0.1
met = 1.4
clo = 0.5

# Calculate relative air speed
v_r = v_relative(v=v, met=met)

# Calculate dynamic clothing
clo_d = clo_dynamic_iso(clo=clo, met=met, v=v)

results = pmv_a(
    tdb=28,
    tr=28,
    vr=v_r,
    rh=50,
    met=met,
    clo=clo_d,
    a_coefficient=0.293,
)
print(results)  # AdaptivePMV(a_pmv=0.74)
print(results.a_pmv)  # 0.71
class pythermalcomfort.classes_return.APMV(a_pmv)[source]#

A dataclass to store the results of the adaptive Predicted Mean Vote (aPMV) model.

Variables:

a_pmv (float or list of floats) – Predicted Mean Vote.

Adaptive Thermal Heat Balance (ATHB)#

pythermalcomfort.models.pmv_athb.pmv_athb(tdb, tr, vr, rh, met, t_running_mean, clo=False)[source]#

Return the PMV value calculated with the Adaptive Thermal Heat Balance Framework [Schweiker2022]. The adaptive thermal heat balance (ATHB) framework introduced a method to account for the three adaptive principals, namely physiological, behavioral, and psychological adaptation, individually within existing heat balance models. The objective is a predictive model of thermal sensation applicable during the design stage or in international standards without knowing characteristics of future occupants.

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, in [°C].

  • tr (float or list of floats) – Mean radiant temperature, in [°C].

  • vr (float or list of floats) – Relative air speed, in [m/s].

    Note

    vr is the relative air speed caused by body movement and not the air speed measured by the air speed sensor. The relative air speed is the sum of the average air speed measured by the sensor plus the activity-generated air speed (Vag). Where Vag is the activity-generated air speed caused by motion of individual body parts. vr can be calculated using the function pythermalcomfort.utilities.v_relative().

  • rh (float or list of floats) – Relative humidity, [%].

  • met (float or list of floats) – Metabolic rate, [met].

  • clo (bool, float or list of floats, optional) – Clothing insulation, in [clo]. If clo is set to False, the clothing insulation value will be calculated using the equation provided in the paper. Defaults to False.

  • t_running_mean (float or list of floats) – Running mean temperature, in [°C].

    Note

    The running mean temperature can be calculated using the function pythermalcomfort.utilities.running_mean_outdoor_temperature().

Returns:

ATHB – Dataclass containing the results of the ATHB calculation. See ATHB for more details.

Examples

from pythermalcomfort.models import pmv_athb

# calculate the predicted mean vote (PMV) using the Adaptive Thermal Heat Balance model
results = pmv_athb(tdb=25, tr=25, vr=0.1, rh=50, met=1.2, t_running_mean=20)
print(results.athb_pmv)  # returns the PMV value

# for multiple points
results = pmv_athb(
    tdb=[25, 25, 25],
    tr=[25, 25, 25],
    vr=[0.1, 0.1, 0.1],
    rh=[50, 50, 50],
    met=[1.2, 1.2, 1.2],
    t_running_mean=[20, 20, 20],
)
print(results.athb_pmv)
class pythermalcomfort.classes_return.ATHB(athb_pmv)[source]#

Dataclass to store the results of the Adaptive Thermal Heat Balance (ATHB) calculation.

Variables:

athb_pmv (float or list of floats) – Predicted Mean Vote calculated with the Adaptive Thermal Heat Balance framework.

Adjusted Predicted Mean Votes with Expectancy Factor (ePMV)#

pythermalcomfort.models.pmv_e.pmv_e(tdb, tr, vr, rh, met, clo, e_coefficient, wme=0, units='SI', limit_inputs=True)[source]#

Return Adjusted Predicted Mean Votes with Expectancy Factor (ePMV). This index was developed by Fanger, P. O. et al. (2002). In non-air- conditioned buildings in warm climates, occupants may sense the warmth as being less severe than the PMV predicts. The main reason is low expectations, but a metabolic rate that is estimated too high can also contribute to explaining the difference. An extension of the PMV model that includes an expectancy factor is introduced for use in non-air- conditioned buildings in warm climates [Fanger2002].

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, default in [°C] in [°F] if units = ‘IP’.

  • tr (float or list of floats) – Mean radiant temperature, default in [°C] in [°F] if units = ‘IP’.

  • vr (float or list of floats) – Relative air speed, default in [m/s] in [fps] if units = ‘IP’.

    Note

    vr is the relative air speed caused by body movement and not the air speed measured by the air speed sensor. The relative air speed is the sum of the average air speed measured by the sensor plus the activity-generated air speed (Vag). Where Vag is the activity-generated air speed caused by motion of individual body parts. vr can be calculated using the function pythermalcomfort.utilities.v_relative().

  • rh (float or list of floats) – Relative humidity, [%].

  • met (float or list of floats) – Metabolic rate, [met].

  • clo (float or list of floats) – Clothing insulation, [clo].

    Note

    this is the basic insulation also known as the intrinsic clothing insulation value of the clothing ensemble (Icl,r), this is the thermal insulation from the skin surface to the outer clothing surface, including enclosed air layers, under actual environmental conditions. This value is not the total insulation (IT,r). The dynamic clothing insulation, clo, can be calculated using the function pythermalcomfort.utilities.clo_dynamic_iso().

  • e_coefficient (float or list of floats) – Expectancy factor.

  • wme (float or list of floats, optional) – External work, [met]. Defaults to 0.

  • units ({‘SI’, ‘IP’}, optional) – Select the SI (International System of Units) or the IP (Imperial Units) system. Supported values are ‘SI’ and ‘IP’. Defaults to ‘SI’.

  • limit_inputs (bool, optional) – By default, if the inputs are outside the standard applicability limits the function returns nan. If False, returns pmv and ppd values even if input values are outside the applicability limits of the model. Defaults to True.

    Note

    The ISO 7730 2005 limits are 10 < tdb [°C] < 30, 10 < tr [°C] < 40, 0 < vr [m/s] < 1, 0.8 < met [met] < 4, 0 < clo [clo] < 2, and -2 < PMV < 2.

Returns:

EPMV – A dataclass containing the Adjusted Predicted Mean Votes with Expectancy Factor. See EPMV for more details. To access the e_pmv value, use the e_pmv attribute of the returned e_pmv instance, e.g., result.e_pmv.

Examples

from pythermalcomfort.models import pmv_e
from pythermalcomfort.utilities import v_relative, clo_dynamic_iso

tdb = 28
tr = 28
rh = 50
v = 0.1
met = 1.4
clo = 0.5
# calculate relative air speed
v_r = v_relative(v=v, met=met)
# Calculate dynamic clothing
clo_d = clo_dynamic_iso(clo=clo, met=met, v=v)
results = pmv_e(tdb, tr, v_r, rh, met, clo_d, e_coefficient=0.6)
print(results.e_pmv)  # 0.48
class pythermalcomfort.classes_return.EPMV(e_pmv)[source]#

Dataclass to represent the Adjusted Predicted Mean Votes with Expectancy Factor (ePMV).

Variables:

e_pmv (float or list of floats) – Adjusted Predicted Mean Votes with Expectancy Factor.

Ridge Regression to predict Rectal and Skin Temperatures#

pythermalcomfort.models.ridge_regression_predict_t_re_t_sk.ridge_regression_predict_t_re_t_sk(sex, age, height, weight, tdb, rh, duration, t_re=None, t_sk=None, limit_inputs=True, round_output=True)[source]#

Predicts the full history of core and skin temperature changes based on a ridge regression model.

This model simulates the rectal (t_re) and mean skin (t_sk) temperatures by first establishing a baseline in a thermoneutral environment (23°C, 50% RH for 120 mins) and then simulating exposure to the specified environmental conditions for duration. If a t_re and t_sk are provided, then the initial 120 min simulation is skipped. Coefficients are taken from fold one from the study [Forbes2025] (https://doi.org/10.1016/j.jtherbio.2025.104078). See notes documentation for limitations with this model.

Parameters:
  • sex (Sex or str or list of (Sex or str)) – Biological sex. Pass the enum instance (e.g., Sex.male), its string value (e.g., Sex.male.value or “male”), or a list of either for vectorized inputs.

  • age (float or list) – Age, in years.

  • height (float or list) – Body height in meters [m].

  • weight (float or list) – Body weight in kilograms [kg].

  • tdb (float or list) – Ambient (dry bulb) air temperature, in °C.

  • rh (float or list) – Relative humidity, in %.

  • duration (int) – Duration of the simulation in the specified environment, in minutes.

  • t_re (float or list, optional) – Initial rectal temperature (°C). If provided, the baseline simulation at 23°C, 50% RH for 120 minutes is skipped, and this value is used as the starting rectal temperature for the main simulation.

  • t_sk (float or list, optional) – Initial mean skin temperature (°C). If provided, the baseline simulation at 23°C, 50% RH for 120 minutes is skipped, and this value is used as the starting mean skin temperature for the main simulation.

  • limit_inputs (bool, optional) – If True, limits the inputs to the standard applicability limits. Defaults to True.

  • round_output (bool, optional) – If True, rounds outputs to 2 decimal places; otherwise leaves full precision. Defaults to True.

Returns:

PredictedBodyTemperatures – A dataclass containing the predicted rectal (t_re) and skin (t_sk) temperature history in °C. See PredictedBodyTemperatures for more details. The outputs are numpy arrays. For scalar inputs, the shape is (duration,). For vector inputs, the shape is (n_inputs, duration).

Raises:

ValueError – If input arrays have inconsistent shapes after broadcasting. If sex contains invalid values (must be “male”/”female” or Sex enum members). If only one of t_re or t_sk is provided. If t_re and t_sk are not broadcastable to the input shape.

Notes

The model is applicable for adults aged between 60 and 100 years. The ranges for the input parameters are:

  • Age: 60 to 100 years

  • Height: 1.30 to 2.30 m

  • Weight: 40 to 140 kg

  • Ambient Temperature: 0 to 60 °C

  • Relative Humidity: 0 to 100 %

If inputs fall outside applicability limits and limit_inputs=True, the corresponding output time series are filled with NaN for all time steps.

The model does not have inputs such as: air velocity, radiative heat transfer, clothing level and an individual’s activity level due to there being little variation in the dataset.

All individuals in the dataset were in minimal clothing while stationary in a heat chamber, therefore stationary individuals in minimal clothing are best represented by this model.

The function supports vectorized inputs for all parameters, allowing for batch predictions.

Examples

from pythermalcomfort.utilities import Sex
from pythermalcomfort.models.ridge_regression_predict_t_re_t_sk import (
    ridge_regression_predict_t_re_t_sk,
)

# Scalar example for a single person
results = ridge_regression_predict_t_re_t_sk(
    sex=Sex.male.value,
    age=60,
    height=1.8,
    weight=75,
    tdb=35,
    rh=60,
    duration=540,
)

print(f"Final Rectal temp: {results.t_re[-1]:.2f}°C")
print(f"Final Skin temp: {results.t_sk[-1]:.2f}°C")

# Vectorized example for multiple scenarios
results_vec = ridge_regression_predict_t_re_t_sk(
    sex=[Sex.male.value, Sex.female.value],
    age=[60, 65],
    height=[1.8, 1.65],
    weight=[75, 60],
    tdb=[35, 40],
    rh=[60, 50],
    duration=540,
)

print(f"Final rectal temps: {results_vec.t_re[:, -1]}")
print(f"Rectal temp history shape: {results_vec.t_re.shape}")
class pythermalcomfort.classes_return.PredictedBodyTemperatures(t_re, t_sk)[source]#

Dataclass for returning predicted temperature history.

The duration mentioned in the shapes below is the duration parameter passed to the prediction function.

Variables:
  • t_re (numpy.ndarray of float, in °C) – Predicted rectal temperature history. - If scalar inputs are provided, this is a 1D array of shape (duration,). - If vector inputs are provided, this is a 2D array of shape (n_inputs, duration).

  • t_sk (numpy.ndarray of float, in °C) – Predicted mean skin temperature history. - If scalar inputs are provided, this is a 1D array of shape (duration,). - If vector inputs are provided, this is a 2D array of shape (n_inputs, duration).

Solar gain on people#

pythermalcomfort.models.solar_gain.solar_gain(sol_altitude, sharp, sol_radiation_dir, sol_transmittance, f_svv, f_bes, asw=0.7, posture='sitting', floor_reflectance=0.6, round_output=True)[source]#

Calculate the solar gain to the human body using the Effective Radiant Field (ERF) [55ASHRAE2023]. The ERF is a measure of the net energy flux to or from the human body. ERF is expressed in W over human body surface area [W/m2]. In addition, it calculates the delta mean radiant temperature. Which is the amount by which the mean radiant temperature of the space should be increased if no solar radiation is present.

Parameters:
  • sol_altitude (float or list of floats) – Solar altitude, degrees from horizontal [deg]. Ranges between 0 and 90.

  • sharp (float or list of floats) – Solar horizontal angle relative to the front of the person (SHARP) [deg]. Ranges between 0 and 180 and is symmetrical on either side. Zero (0) degrees represents direct-beam radiation from the front, 90 degrees represents direct-beam radiation from the side, and 180 degrees represents direct-beam radiation from the back. SHARP is the angle between the sun and the person only. Orientation relative to compass or to room is not included in SHARP.

  • sol_radiation_dir (float or list of floats) – Direct-beam solar radiation, [W/m2]. Ranges between 200 and 1000. See Table C2-3 of ASHRAE 55 2020 [55ASHRAE2023].

  • sol_transmittance (float or list of floats) – Total solar transmittance, ranges from 0 to 1. The total solar transmittance of window systems, including glazing unit, blinds, and other façade treatments, shall be determined using one of the following methods: i) Provided by manufacturer or from the National Fenestration Rating Council approved Lawrence Berkeley National Lab International Glazing Database. ii) Glazing unit plus venetian blinds or other complex or unique shades shall be calculated using National Fenestration Rating Council approved software or Lawrence Berkeley National Lab Complex Glazing Database.

  • f_svv (float or list of floats) – Fraction of sky-vault view fraction exposed to body, ranges from 0 to 1. It can be calculated using the function pythermalcomfort.utilities.f_svv().

  • f_bes (float or list of floats) – Fraction of the possible body surface exposed to sun, ranges from 0 to 1. See Table C2-2 and equation C-7 ASHRAE 55 2020 [55ASHRAE2023].

  • asw (float or list of floats, optional) – The average short-wave absorptivity of the occupant. It will range widely, depending on the color of the skin of the occupant as well as the color and amount of clothing covering the body. Defaults to 0.7.

    Note

    Short-wave absorptivity typically ranges from 0.57 to 0.84, depending on skin and clothing color. More information is available in Blum (1945).

  • posture (str, optional) – Default ‘sitting’ list of available options ‘standing’, ‘supine’ or ‘sitting’.

  • floor_reflectance (float or list of floats, optional) – Floor reflectance. It is assumed to be constant and equal to 0.6. Defaults to 0.6.

  • round_output (bool, optional) – If True, rounds output value. If False, it does not round it. Defaults to True.

Returns:

SolarGain – A dataclass containing the solar gain to the human body and delta mean radiant temperature. See SolarGain for more details. To access the erf and delta_mrt values, use the corresponding attributes of the returned SolarGain instance, e.g., result.erf.

Raises:

ValueError – If the posture is not one of ‘standing’, ‘supine’, or ‘sitting’.

Examples

from pythermalcomfort.models import solar_gain

result = solar_gain(
    sol_altitude=0,
    sharp=120,
    sol_radiation_dir=800,
    sol_transmittance=0.5,
    f_svv=0.5,
    f_bes=0.5,
    asw=0.7,
    posture="sitting",
)
print(result.erf)  # 42.9
print(result.delta_mrt)  # 10.3

Applicability#

This model is applicable under the following conditions:

  • Solar altitude (sol_altitude) must be between 0° and 90°.

  • Solar horizontal angle (sharp) must be between 0° and 180°.

  • Direct-beam solar radiation (sol_radiation_dir) should typically range from 200 to 1000 W/m², as per ASHRAE 55 Table C2-3.

  • Solar transmittance (sol_transmittance) must be between 0 and 1.

  • Sky-vault view fraction (f_svv) and body surface exposure fraction (f_bes) must be between 0 and 1.

  • Average short-wave absorptivity (asw) typically ranges from 0.57 to 0.84, depending on skin and clothing color.

  • Posture must be one of ‘standing’, ‘supine’, or ‘sitting’.

  • Floor reflectance (floor_reflectance) is assumed constant at 0.6, but can vary.

  • All inputs are in SI units (e.g., angles in degrees, radiation in W/m²).

class pythermalcomfort.classes_return.SolarGain(erf, delta_mrt)[source]#

Dataclass to represent the solar gain to the human body.

Variables:
  • erf (float or list of floats) – Solar gain to the human body using the Effective Radiant Field [W/m2].

  • delta_mrt (float or list of floats) – Delta mean radiant temperature. The amount by which the mean radiant temperature of the space should be increased if no solar radiation is present.

Sports Heat Stress Risk#

pythermalcomfort.models.sports_heat_stress_risk.sports_heat_stress_risk(tdb, tr, rh, vr, sport)[source]#

Calculate sports heat stress risk levels based on environmental conditions and sport-specific parameters.

This function assesses heat stress risk for athletes during outdoor sports by combining environmental conditions with sport-specific metabolic rates and clothing insulation. It uses the Predicted Heat Strain (PHS) model to determine threshold temperatures for different risk categories (Low, Medium, High, Extreme). The method is based on the Sports Medicine Australia heat policy framework [SportsHeatStress2025], with detailed guidelines [SportsHeatPolicy2025] and an online implementation available at the Sports Heat Tool [SportsHeatTool].

Parameters:
  • tdb (float or list of float) – Dry bulb air temperature [°C].

  • tr (float or list of float) – Mean radiant temperature [°C].

  • rh (float or list of float) – Relative humidity [%].

  • vr (float or list of float) – Relative air speed [m/s]. Relative air speed [m/s]. If the input vr is lower than the minimum relative air speed defined for the selected sport (sport.vr), then sport.vr will be used for the calculation.

  • sport (_SportsValues) – Sport-specific activity dataclass with fields clo (clothing insulation), met (metabolic rate), vr (minimum relative air speed), and duration (activity duration). Use one of the predefined entries from the Sports class, e.g., Sports.RUNNING, Sports.SOCCER, Sports.TENNIS, etc.

Returns:

SportsHeatStressRisk – A dataclass containing the heat stress risk assessment results. See SportsHeatStressRisk for more details. To access individual values, use the corresponding attributes of the returned instance, e.g., result.risk_level_interpolated.

Raises:
  • ValueError – If the risk level could not be determined due to NaN thresholds or if the internal solver fails to produce thresholds that allow a risk determination.

  • TypeError – If sport is not a valid _SportsValues instance.

Examples

from pythermalcomfort.models.sports_heat_stress_risk import (
    sports_heat_stress_risk,
    Sports,
)

# Example 1: Single condition for running
result = sports_heat_stress_risk(
    tdb=35, tr=35, rh=40, vr=2.0, sport=Sports.RUNNING
)
print(result.risk_level_interpolated)  # 2.1 (Medium risk)
print(result.t_medium)  # 34.5 (Temperature threshold for medium risk)
print(result.t_high)  # 39.0 (Temperature threshold for high risk)
print(result.t_extreme)  # 41.6 (Temperature threshold for extreme risk)
print(
    result.recommendation
)  # "Increase frequency and/or duration of rest breaks"

# Example 2: Array inputs for multiple conditions
result = sports_heat_stress_risk(
    tdb=[30, 35, 40],
    tr=[30, 35, 40],
    rh=[50, 50, 50],
    vr=[1.0, 1.0, 1.5],
    sport=Sports.SOCCER,
)
print(result.risk_level_interpolated)  # Array of risk levels

# Example 3: vr clamping — input vr (0.5 m/s) is below Sports.RUNNING.vr
# (2.0 m/s), so the calculation uses sport.vr=2.0 m/s as the effective wind speed.
result_clamped = sports_heat_stress_risk(
    tdb=35, tr=35, rh=40, vr=0.5, sport=Sports.RUNNING
)
# Because vr is clamped to 2.0 m/s (same as Example 1), the output matches:
print(result_clamped.risk_level_interpolated)  # same as Example 1
print(result_clamped.t_medium)  # same as Example 1

# Example 4: Different sports
result_tennis = sports_heat_stress_risk(
    tdb=33, tr=70, rh=60, vr=0.75, sport=Sports.TENNIS
)
result_cycling = sports_heat_stress_risk(
    tdb=33, tr=70, rh=60, vr=3.0, sport=Sports.CYCLING
)
class pythermalcomfort.models.sports_heat_stress_risk.Sports[source]#

Namespace of predefined sport values.

Use attributes like Sports.RUNNING to obtain a _SportsValues instance. This class uses a frozen dataclass decorator to prevent modification of the namespace. Attributes are class-level constants, not instance fields.

class pythermalcomfort.classes_return.SportsHeatStressRisk(risk_level_interpolated, t_medium, t_high, t_extreme, recommendation)[source]#

Dataclass to represent the Sports Heat Stress Risk calculation results.

Variables:
  • risk_level_interpolated (float or list of floats) – Interpolated risk level (1.0-4.0), [dimensionless]. Risk levels: 1-<2 = low, 2-<3 = moderate, 3-<4 = high, 4 = extreme.

  • t_medium (float or list of floats) – Temperature threshold for medium risk level, [°C].

  • t_high (float or list of floats) – Temperature threshold for high risk level, [°C].

  • t_extreme (float or list of floats) – Temperature threshold for extreme risk level, [°C].

  • recommendation (str or list of str) – Heat stress management recommendations for the calculated risk level.

Standard Effective Temperature (SET)#

pythermalcomfort.models.set_tmp.set_tmp(tdb, tr, v, rh, met, clo, wme=0.0, body_surface_area=1.8258, p_atm=101325, position='standing', limit_inputs=True, round_output=True, calculate_ce=False)[source]#

Calculate the Standard Effective Temperature (SET).

The SET is the temperature of a hypothetical isothermal environment at 50% (rh), <0.1 m/s (20 fpm) average air speed (v), and tr = tdb, in which the total heat loss from the skin of an imaginary occupant wearing clothing, standardized for the activity concerned is the same as that from a person in the actual environment with actual clothing and activity level. [Gagge1986]

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C].

  • tr (float or list of floats) – Mean radiant temperature, [°C].

  • v (float or list of floats) – Air speed, [m/s].

  • rh (float or list of floats) – Relative humidity, [%].

  • met (float or list of floats) – Metabolic rate, [met].

  • clo (float or list of floats) – Clothing insulation, [clo].

  • wme (float or list of floats, optional) – External work, [met]. Defaults to 0.

  • body_surface_area (float or list of floats, optional) – Body surface area, default value 1.8258 [m2]

    Note

    The body surface area can be calculated using the function pythermalcomfort.utilities.body_surface_area().

  • p_atm (float or list of floats, optional) – Atmospheric pressure, default value 101325 [Pa]

  • position (str, optional) – Select either “sitting” or “standing”. Defaults to “standing”.

  • limit_inputs (bool, optional) – If True, limits the inputs to the standard applicability limits. Defaults to True.

    Note

    By default, if the inputs are outside the standard applicability limits the function returns nan. If False returns values regardless of the input values. The limits are 10 < tdb [°C] < 40, 10 < tr [°C] < 40, 0 < v [m/s] < 2, 1 < met [met] < 4, and 0 < clo [clo] < 1.5.

  • round_output (bool, optional) – If True, rounds output value. If False, it does not round it. Defaults to True.

Returns:

SET – A dataclass containing the Standard Effective Temperature. See SetTmp for more details. To access the set value, use the corresponding attribute of the returned SetTmp instance, e.g., result.set.

Examples

from pythermalcomfort.models import set_tmp

result = set_tmp(tdb=25, tr=25, v=0.1, rh=50, met=1.2, clo=0.5)
print(result.set)  # 24.3

result = set_tmp(tdb=[25, 25], tr=25, v=0.1, rh=50, met=1.2, clo=0.5)
print(result.set)  # [24.3, 24.3]
class pythermalcomfort.classes_return.SET(set)[source]#

Dataclass to represent the Standard Effective Temperature (SET).

Variables:

set (float or list of floats) – Standard effective temperature, [°C].

Universal Thermal Climate Index (UTCI)#

pythermalcomfort.models.utci.utci(tdb, tr, v, rh, units='SI', limit_inputs=True, round_output=True)[source]#

Calculate the Universal Thermal Climate Index (UTCI).

The UTCI is the equivalent temperature for the environment derived from a reference environment. It is defined as the air temperature of the reference environment which produces the same strain index value in comparison with the reference individual’s response to the real environment. It is regarded as one of the most comprehensive indices for calculating heat stress in outdoor spaces. The parameters that are taken into account for calculating UTCI involve dry bulb temperature, mean radiation temperature, the pressure of water vapor or relative humidity, and wind speed (at the elevation of 10 m above the ground). [Zare2018]

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature. Default in [°C] in [°F] if units = ‘IP’.

  • tr (float or list of floats) – Mean radiant temperature. Default in [°C] in [°F] if units = ‘IP’.

  • v (float or list of floats) – Wind speed 10m above ground level. Default in [m/s] in [fps] if units = ‘IP’.

  • rh (float or list of floats) – Relative humidity, [%].

  • units (str, optional) – Select the SI (International System of Units) or the IP (Imperial Units) system. Defaults to ‘SI’.

  • limit_inputs (bool, optional) – By default, if the inputs are outside the standard applicability limits the function returns nan. If False, returns UTCI values even if input values are outside the applicability limits of the model. The valid input ranges are -50 < tdb [°C] < 50, tdb - 30 < tr [°C] < tdb + 70, and for 0.5 < v [m/s] < 17.0. Defaults to True.

  • round_output (bool, optional) – If True, rounds output value. If False, it does not round it. Defaults to True.

Returns:

UTCI – A dataclass containing the Universal Thermal Climate Index and stress category. See UTCI for more details. To access the utci and stress_category values, use the corresponding attributes of the returned Utci instance, e.g., result.utci.

Examples

from pythermalcomfort.models import utci

result = utci(tdb=25, tr=25, v=1.0, rh=50)
print(result.utci)  # 24.6
print(result.stress_category)  # "no thermal stress"

result = utci(tdb=[25, 40], tr=25, v=1.0, rh=50)
print(result.utci)  # [24.6, 40.6]
class pythermalcomfort.classes_return.UTCI(utci, stress_category)[source]#

Dataclass to represent the Universal Thermal Climate Index (UTCI).

Variables:
  • utci (float or list of floats) – Universal Thermal Climate Index, [°C] or in [°F].

  • stress_category (str or list of strs) – UTCI categorized in terms of thermal stress [Blazejczyk2013].

Use Fans During Heatwaves#

pythermalcomfort.models.use_fans_heatwaves.use_fans_heatwaves(tdb, tr, v, rh, met, clo, wme=0, body_surface_area=1.8258, p_atm=101325, position='standing', max_skin_blood_flow=80, max_sweating=500, limit_inputs=True, round_output=True)[source]#

Estimate if the conditions you have selected would cause heat strain.

This occurs when either the following variables reaches its maximum value:

  • m_rsw Rate at which regulatory sweat is generated, [mL/h/m2]

  • w : Skin wettedness, adimensional. Ranges from 0 and 1.

  • m_bl : Skin blood flow [kg/h/m2]

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C].

  • tr (float or list of floats) – Mean radiant temperature, [°C].

  • v (float or list of floats) – Air speed, [m/s].

  • rh (float or list of floats) – Relative humidity, [%].

  • met (float or list of floats) – Metabolic rate, [met].

  • clo (float or list of floats) – Clothing insulation, [clo].

  • wme (float or list of floats, optional) – External work, [met]. Defaults to 0.

  • body_surface_area (float or list of floats, optional) – Body surface area, default value 1.8258 [m2]. Defaults to 1.8258.

    Note

    The body surface area can be calculated using the function pythermalcomfort.utilities.body_surface_area().

  • p_atm (float or list of floats, optional) – Atmospheric pressure, default value 101325 [Pa]. Defaults to 101325.

  • position (str, optional) – Select either “sitting” or “standing”. Defaults to “standing”.

  • max_skin_blood_flow (float) – Maximum blood flow from the core to the skin. Defaults to 80.

  • limit_inputs (bool, optional) – By default, if the inputs are outside the standard applicability limits the function returns nan. If False, returns values even if input values are outside the applicability limits of the model. Defaults to True. The applicability limits are 20 < tdb [°C] < 50, 20 < tr [°C] < 50, 0.1 < v [m/s] < 4.5, 0.7 < met [met] < 2, and 0 < clo [clo] < 1.

  • round_output (bool, optional) – If True, rounds output value. If False, it does not round it. Defaults to True.

  • max_sweating (float or list of floats, optional) – Maximum rate at which regulatory sweat is generated, [kg/h/m2]. Defaults to 500.

Returns:

UseFansHeatwaves – A dataclass containing the results of using fans during heatwaves. See UseFansHeatwaves for more details. To access the results, use the corresponding attributes of the returned UseFansHeatwaves instance, e.g., result.e_skin.

Examples

from pythermalcomfort.models import use_fans_heatwaves

result = use_fans_heatwaves(tdb=35, tr=35, v=1.0, rh=50, met=1.2, clo=0.5)
print(result.e_skin)  # 63.0
class pythermalcomfort.classes_return.UseFansHeatwaves(e_skin, e_rsw, e_max, q_sensible, q_skin, q_res, t_core, t_skin, m_bl, m_rsw, w, w_max, heat_strain, heat_strain_blood_flow, heat_strain_w, heat_strain_sweating)[source]#

Dataclass to represent the results of using fans during heatwaves.

Variables:
  • e_skin (float or list of floats) – Total rate of evaporative heat loss from skin, [W/m2]. Equal to e_rsw + e_diff.

  • e_rsw (float or list of floats) – Rate of evaporative heat loss from sweat evaporation, [W/m2].

  • e_max (float or list of floats) – Maximum rate of evaporative heat loss from skin, [W/m2].

  • q_sensible (float or list of floats) – Sensible heat loss from skin, [W/m2].

  • q_skin (float or list of floats) – Total rate of heat loss from skin, [W/m2]. Equal to q_sensible + e_skin.

  • q_res (float or list of floats) – Total rate of heat loss through respiration, [W/m2].

  • t_core (float or list of floats) – Core temperature, [°C].

  • t_skin (float or list of floats) – Skin temperature, [°C].

  • m_bl (float or list of floats) – Skin blood flow, [kg/h/m2].

  • m_rsw (float or list of floats) – Rate at which regulatory sweat is generated, [mL/h/m2].

  • w (float or list of floats) – Skin wettedness, adimensional. Ranges from 0 to 1.

  • w_max (float or list of floats) – Skin wettedness (w) practical upper limit, adimensional. Ranges from 0 to 1.

  • heat_strain (bool or list of bools) – True if the model predicts that the person may be experiencing heat strain.

  • heat_strain_blood_flow (bool or list of bools) – True if heat strain is caused by skin blood flow (m_bl) reaching its maximum value.

  • heat_strain_w (bool or list of bools) – True if heat strain is caused by skin wettedness (w) reaching its maximum value.

  • heat_strain_sweating (bool or list of bools) – True if heat strain is caused by regulatory sweating (m_rsw) reaching its maximum value.

Vertical air temperature gradient#

pythermalcomfort.models.vertical_tmp_grad_ppd.vertical_tmp_grad_ppd(tdb, tr, vr, rh, met, clo, vertical_tmp_grad, round_output=True, limit_inputs=True)[source]#

Calculate the percentage of thermally dissatisfied people with a vertical temperature gradient between feet and head [55ASHRAE2023]. This equation is only applicable for vr < 0.2 m/s (40 fps).

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C].

    Note

    The air temperature is the average value over two heights: 0.6 m (24 in.) and 1.1 m (43 in.) for seated occupants and 1.1 m (43 in.) and 1.7 m (67 in.) for standing occupants.

  • tr (float or list of floats) – Mean radiant temperature, [°C].

  • vr (float or list of floats) – Relative air speed, [m/s].

    Note

    vr is the relative air speed caused by body movement and not the air speed measured by the air speed sensor. The relative air speed is the sum of the average air speed measured by the sensor plus the activity-generated air speed (Vag). Where Vag is the activity-generated air speed caused by motion of individual body parts. vr can be calculated using the function pythermalcomfort.utilities.v_relative().

  • rh (float or list of floats) – Relative humidity, [%].

  • met (float or list of floats) – Metabolic rate, [met].

  • clo (float or list of floats) – Clothing insulation, [clo].

    Note

    this is the basic insulation also known as the intrinsic clothing insulation value of the clothing ensemble (Icl,r), this is the thermal insulation from the skin surface to the outer clothing surface, including enclosed air layers, under actual environmental conditions. This value is not the total insulation (IT,r). The dynamic clothing insulation, clo, can be calculated using the function pythermalcomfort.utilities.clo_dynamic_ashrae().

  • vertical_tmp_grad (float or list of floats) – Vertical temperature gradient between the feet and the head, [°C/m].

  • round_output (bool, optional) – If True, rounds output value. If False, it does not round it. Defaults to True.

  • limit_inputs (bool, optional) – By default, if the inputs are outside the standard applicability limits the function returns nan. If False, returns values even if input values are outside the applicability limits of the model. Defaults to True. The applicability limits are 10 < tdb [°C] < 40, 10 < tr [°C] < 40, 0 < vr [m/s] < 0.2, 1 < met [met] < 4, and 0 < clo [clo] < 1.5.

Returns:

VerticalTGradPPD – A dataclass containing the Predicted Percentage of Dissatisfied occupants with vertical temperature gradient and acceptability. See VerticalTmpGradPPD for more details. To access the ppd_vg and acceptability values, use the corresponding attributes of the returned VerticalTmpGradPPD instance, e.g., result.ppd_vg.

Examples

from pythermalcomfort.models import vertical_tmp_grad_ppd

result = vertical_tmp_grad_ppd(
    tdb=25, tr=25, vr=0.1, rh=50, met=1.2, clo=0.5, vertical_tmp_grad=7
)
print(result.ppd_vg)  # 12.6
print(result.acceptability)  # False
class pythermalcomfort.classes_return.VerticalTGradPPD(ppd_vg, acceptability)[source]#

Dataclass to represent the Predicted Percentage of Dissatisfied (PPD) with vertical temperature gradient.

Variables:
  • ppd_vg (float or list of floats) – Predicted Percentage of Dissatisfied occupants with vertical temperature gradient.

  • acceptability (bool or list of bools) – True if the value of air speed at the ankle level is acceptable (PPD_vg <= 5%).

Wet Bulb Globe Temperature Index (WBGT)#

pythermalcomfort.models.wbgt.wbgt(twb, tg, tdb=None, with_solar_load=False, round_output=True)[source]#

Calculate the Wet Bulb Globe Temperature (WBGT) index in compliance with the ISO 7243 Standard [7243ISO2017].

The WBGT is a heat stress index that measures the thermal environment to which a person is exposed. In most situations, this index is simple to calculate. It should be used as a screening tool to determine whether heat stress is present. The PHS model allows a more accurate estimation of stress. PHS can be calculated using the function pythermalcomfort.models.phs().

The WBGT determines the impact of heat on a person throughout the course of a working day (up to 8 hours). It does not apply to very brief heat exposures. It pertains to the evaluation of male and female people who are fit for work in both indoor and outdoor occupational environments, as well as other sorts of surroundings [7243ISO2017].

The WBGT is defined as a function of only twb and tg if the person is not exposed to direct radiant heat from the sun. When a person is exposed to direct radiant heat, tdb must also be specified.

Parameters:
  • twb (float or list of floats) – Natural (no forced air flow) wet bulb temperature, [°C].

  • tg (float or list of floats) – Globe temperature, [°C].

  • tdb (float or list of floats, optional) – Dry bulb air temperature, [°C]. This value is needed as input if the person is exposed to direct solar radiation.

  • with_solar_load (bool, optional) – True if the globe sensor is exposed to direct solar radiation. Defaults to False.

  • round_output (bool, optional) – If True, rounds output value. If False, it does not round it. Defaults to True.

Returns:

WBGT – A dataclass containing the Wet Bulb Globe Temperature Index. See WBGT for more details. To access the wbgt value, use the wbgt attribute of the returned wbgt instance, e.g., result.wbgt.

Examples

from pythermalcomfort.models import wbgt

result = wbgt(twb=25, tg=32)
print(result.wbgt)  # 27.1

result = wbgt(twb=25, tg=32, tdb=20, with_solar_load=True)
print(result.wbgt)  # 25.9
class pythermalcomfort.classes_return.WBGT(wbgt)[source]#

Dataclass to represent the Wet Bulb Globe Temperature (WBGT) index.

Variables:

wbgt (float or list of floats) – Wet Bulb Globe Temperature Index.

Wind chill index#

pythermalcomfort.models.wci.wci(tdb, v, round_output=True)[source]#

Calculate the Wind Chill Index (WCI) in accordance with the ASHRAE 2017 Handbook Fundamentals - Chapter 9 [ashrae2017].

The wind chill index (WCI) is an empirical index based on cooling measurements taken on a cylindrical flask partially filled with water in Antarctica (Siple and Passel 1945). For a surface temperature of 33°C, the index describes the rate of heat loss from the cylinder via radiation and convection as a function of ambient temperature and wind velocity.

This formulation has been met with some valid criticism. WCI is unlikely to be an accurate measure of heat loss from exposed flesh, which differs from plastic in terms of curvature, roughness, and radiation exchange qualities, and is always below 33°C in a cold environment. Furthermore, the equation’s values peak at 90 km/h and then decline as velocity increases. Nonetheless, this score reliably represents the combined effects of temperature and wind on subjective discomfort for velocities below 80 km/h [ashrae2017].

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C].

  • v (float or list of floats) – Wind speed 10m above ground level, [m/s].

  • round_output (bool, optional) – If True, rounds output value. If False, it does not round it. Defaults to True.

Returns:

WCI – A dataclass containing the Wind Chill Index. See WCI for more details. To access the wci value, use the wci attribute of the returned WCI instance, e.g., result.wci.

Examples

from pythermalcomfort.models import wci

result = wci(tdb=-5, v=5.5)
print(result.wci)  # 1255.2

result = wci(tdb=[-5, -10], v=[5.5, 10], round_output=True)
print(result.wci)  # [1255.2 1603.9]
class pythermalcomfort.classes_return.WCI(wci)[source]#

Dataclass to represent the Wind Chill Index (WCI).

Variables:

wci (float or list of floats) – Wind Chill Index, [W/m^2].

Wind chill temperature#

pythermalcomfort.models.wind_chill_temperature.wind_chill_temperature(tdb, v, round_output=True)[source]#

Calculate the Wind Chill Temperature (WCT).

We validated the implementation of this model by comparing the results with the Wind Chill Temperature Calculator on Calculator.net

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C].

  • v (float or list of floats) – Wind speed 10m above ground level, [km/h].

  • round_output (bool, optional) – If True, rounds output value. If False, it does not round it. Defaults to True.

Returns:

WCT – A dataclass containing the Wind Chill Temperature. See WCT for more details. To access the wct value, use the wct attribute of the returned WCT instance, e.g., result.wct.

Examples

from pythermalcomfort.models import wind_chill_temperature

result = wind_chill_temperature(tdb=-5, v=5.5)
print(result.wct)  # -7.5

result = wind_chill_temperature(tdb=[-5, -10], v=[5.5, 10], round_output=True)
print(result.wct)  # [-7.5, -15.3]
class pythermalcomfort.classes_return.WCT(wct)[source]#

Dataclass to represent the Wind Chill Temperature (WCT).

Variables:

wct (float or list of floats) – Wind Chill Temperature, [°C].

Work capacity (Dunne et al.)#

pythermalcomfort.models.work_capacity_dunne.work_capacity_dunne(wbgt, work_intensity='heavy')[source]#

Estimate work capacity due to heat based on Dunne et al [Dunne2013].

Estimates the amount of work that will be done at a given WBGT and intensity of work as a percent. 100% means work is unaffected by heat. 0% means no work is done.

This is based upon NIOSH safety standards. See: Dunne JP, Stouffer RJ, John JG. Reductions in labour capacity from heat stress under climate warming. Nature Climate Change. 2013 Jun;3(6):563 6.

Heavy intensity work is sometimes labelled as 400 W, but this is only nominal. Moderate work is assumed to be half as much as heavy intensity, and light half as much as moderate.

Parameters:
  • wbgt (float or list of floats) – Wet bulb globe temperature, [°C].

  • work_intensity (str) – Which work intensity to use for the calculation, choice of “heavy”, “moderate” or “light”. Default is “heavy”.

    Note

    Dunne et al [Dunne2013] suggests that heavy intensity work is 350-500 kcal/h, moderate is 200-350 kcal/h, and light is less than 100-200 kcal/h.

Returns:

WorkCapacity – A dataclass containing the work capacity. See WorkCapacity for more details. To access the capacity value, use the capacity attribute of the returned WorkCapacity instance, e.g., result.capacity.

class pythermalcomfort.classes_return.WorkCapacity(capacity)[source]#

Dataclass to represent work loss.

Variables:

capacity (float or list of floats) – Work capacity affected by heat.

Work capacity (Hothaps)#

pythermalcomfort.models.work_capacity_hothaps.work_capacity_hothaps(wbgt, work_intensity='heavy')[source]#

Estimate work capacity due to heat based on Kjellstrom et al. [Kjellstrom2018].

Estimates the amount of work that will be done at a given WBGT and intensity of work as a percent. 100% means work is unaffected by heat. 0% means no work is done. Note that in this version the functions do not reach 0% as it is assumed that it is always possible to work in short bursts for 10% of the time.

Note that for this function “the empirical evidence is from studies in heavyly distinct locations, including a gold mine (Wyndham, 1969), 124 rice harvesters in West Bengal in India (Sahu et al., 2013), and six women observed in a climatic chamber (Nag and Nag, 1992).” (https://adaptecca.es/sites/default/files/documentos/2018_jrc_pesetaiii_impact_labour_productivity.pdf). The shape of the function is just an assumption, and the fit of the sigmoid to the data it is analysing is not especially good.

Heavy intensity work is sometimes labelled as 400 W, moderate 300 W, light 200 W, but this is only nominal.

The correction citation is: Bröde P, Fiala D, Lemke B, Kjellstrom T. Estimated work ability in warm outdoor environments depends on the chosen heat stress assessment metric. International Journal of Biometeorology. 2018 Mar;62(3):331 45.

The relevant definitions of the functions can be found most clearly in: Orlov A, Sillmann J, Aunan K, Kjellstrom T, Aaheim A. Economic costs of heat-induced reductions in worker productivity due to global warming. Global Environmental Change [Internet]. 2020 Jul;63. Available from: https://doi.org/10.1016/j.gloenvcha.2020.102087

For a comparison of different functions see Fig 1 of Day E, Fankhauser S, Kingsmill N, Costa H, Mavrogianni A. Upholding labour productivity under climate change: an assessment of adaptation options. Climate Policy. 2019 Mar;19(3):367 85.

Parameters:
  • wbgt (float or list of floats) – Wet bulb globe temperature, [°C].

  • work_intensity (str) – Which work intensity to use for the calculation, choice of “heavy”, “moderate” or “light”.

Returns:

WorkCapacity – A dataclass containing the work capacity. See WorkCapacity for more details. To access the capacity value, use the capacity attribute of the returned WorkCapacity instance, e.g., result.capacity.

class pythermalcomfort.classes_return.WorkCapacity(capacity)[source]

Dataclass to represent work loss.

Variables:

capacity (float or list of floats) – Work capacity affected by heat.

Work capacity (ISO)#

pythermalcomfort.models.work_capacity_iso.work_capacity_iso(wbgt, met)[source]#

Estimate work capacity due to heat based on ISO standards as described by Brode et al.

Estimates the amount of work that will be done at a given WBGT and intensity of work as a percent. 100% means work is unaffected by heat. 0% means no work is done.

The function definitions / parameters can be found in: 1. Bröde P, Fiala D, Lemke B, Kjellstrom T. Estimated work ability in warm outdoor environments depends on the chosen heat stress assessment metric. International Journal of Biometeorology. 2018 Mar;62(3):331 45.

For a comparison of different functions see Fig 1 of Day E, Fankhauser S, Kingsmill N, Costa H, Mavrogianni A. Upholding labour productivity under climate change: an assessment of adaptation options. Climate Policy. 2019 Mar;19(3):367 85.

Parameters:
  • wbgt (float or list of floats) – Wet bulb globe temperature, [°C].

  • met (float or list of floats) – Metabolic heat production in Watts

Returns:

WorkCapacity – A dataclass containing the work capacity. See WorkCapacity for more details. To access the capacity value, use the capacity attribute of the returned WorkCapacity instance, e.g., result.capacity.

class pythermalcomfort.classes_return.WorkCapacity(capacity)[source]

Dataclass to represent work loss.

Variables:

capacity (float or list of floats) – Work capacity affected by heat.

Work capacity (NIOSH)#

pythermalcomfort.models.work_capacity_niosh.work_capacity_niosh(wbgt, met)[source]#

Estimate work capacity due to heat based on NIOSH standards as described by Brode et al.

Estimates the amount of work that will be done at a given WBGT and intensity of work as a percent. 100% means work is unaffected by heat. 0% means no work is done.

The function definitions / parameters can be found in: 1. Bröde P, Fiala D, Lemke B, Kjellstrom T. Estimated work ability in warm outdoor environments depends on the chosen heat stress assessment metric. International Journal of Biometeorology. 2018 Mar;62(3):331 45.

For a comparison of different functions see Fig 1 of Day E, Fankhauser S, Kingsmill N, Costa H, Mavrogianni A. Upholding labour productivity under climate change: an assessment of adaptation options. Climate Policy. 2019 Mar;19(3):367 85.

Parameters:
  • wbgt (float or list of floats) – Wet bulb globe temperature, [°C].

  • met (float or list of floats) – Metabolic heat production in Watts

Returns:

WorkCapacity – A dataclass containing the work capacity. See WorkCapacity for more details. To access the capacity value, use the capacity attribute of the returned WorkCapacity instance, e.g., result.capacity.

class pythermalcomfort.classes_return.WorkCapacity(capacity)[source]

Dataclass to represent work loss.

Variables:

capacity (float or list of floats) – Work capacity affected by heat.

Temperature Humidity Index (THI)#

pythermalcomfort.models.thi.thi(tdb, rh, round_output=True)[source]#

Calculate the Temperature-Humidity Index (THI) defined in [Yan2025], equivalent to the definition in [Schlatter1987], but uses Celsius instead of Fahrenheit.

Parameters:
  • tdb (float or list of floats) – Dry bulb air temperature, [°C].

  • rh (float or list of floats) – Relative humidity, [%].

  • round_output (bool, optional) – If True, rounds output value. If False, it does not round it. Defaults to True.

Returns:

THI – A dataclass containing the Temperature-Humidity Index. See THI for more details. To access the thi value, use the thi attribute of the returned THI instance, e.g., result.thi.

class pythermalcomfort.classes_return.THI(thi)[source]#

Dataclass to represent the Temperature-Humidity Index (THI).

Variables:

thi (float or list of floats) – Temperature-Humidity Index (THI).