Source code for pythermalcomfort.models.adaptive_en

from typing import Literal

import numpy as np

from pythermalcomfort.classes_input import ENInputs
from pythermalcomfort.classes_return import AdaptiveEN
from pythermalcomfort.shared_functions import valid_range
from pythermalcomfort.utilities import Units, operative_tmp, units_converter


[docs] def adaptive_en( tdb: float | list[float], tr: float | list[float], t_running_mean: float | list[float], v: float | list[float], units: Literal["SI", "IP"] = Units.SI.value, limit_inputs: bool = True, round_output: bool = True, ) -> AdaptiveEN: """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 :py:meth:`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 :py:class:`~pythermalcomfort.classes_return.AdaptiveEN` for more details. Examples -------- .. code-block:: python 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. """ # Validate inputs using the ENInputs class ENInputs( tdb=tdb, tr=tr, t_running_mean=t_running_mean, v=v, units=units, round_output=round_output, ) tdb = np.asarray(tdb) tr = np.asarray(tr) t_running_mean = np.asarray(t_running_mean) v = np.asarray(v) standard = "iso" if units.upper() == Units.IP.value: tdb, tr, t_running_mean, v = units_converter( tdb=tdb, tr=tr, tmp_running_mean=t_running_mean, v=v, ) to = operative_tmp(tdb, tr, v, standard=standard) # Calculate cooling effect (ce) of elevated air speed when top > 25 degC. ce = np.where((v >= 0.6) & (to >= 25.0), 999, 0) ce = np.where((v < 0.9) & (ce == 999), 1.2, ce) ce = np.where((v < 1.2) & (ce == 999), 1.8, ce) ce = np.where(ce == 999, 2.2, ce) t_cmf = 0.33 * t_running_mean + 18.8 if limit_inputs: trm_valid = valid_range(t_running_mean, (10.0, 33.5)) all_valid = ~(np.isnan(trm_valid)) t_cmf = np.where(all_valid, t_cmf, np.nan) t_cmf_i_lower = t_cmf - 3.0 t_cmf_ii_lower = t_cmf - 4.0 t_cmf_iii_lower = t_cmf - 5.0 t_cmf_i_upper = t_cmf + 2.0 + ce t_cmf_ii_upper = t_cmf + 3.0 + ce t_cmf_iii_upper = t_cmf + 4.0 + ce acceptability_i = (t_cmf_i_lower <= to) & (to <= t_cmf_i_upper) acceptability_ii = (t_cmf_ii_lower <= to) & (to <= t_cmf_ii_upper) acceptability_iii = (t_cmf_iii_lower <= to) & (to <= t_cmf_iii_upper) if units.upper() == Units.IP.value: t_cmf, t_cmf_i_upper, t_cmf_ii_upper, t_cmf_iii_upper = units_converter( from_units=Units.SI.value.lower(), tmp_cmf=t_cmf, tmp_cmf_cat_i_up=t_cmf_i_upper, tmp_cmf_cat_ii_up=t_cmf_ii_upper, tmp_cmf_cat_iii_up=t_cmf_iii_upper, ) t_cmf_i_lower, t_cmf_ii_lower, t_cmf_iii_lower = units_converter( from_units=Units.SI.value.lower(), tmp_cmf_cat_i_low=t_cmf_i_lower, tmp_cmf_cat_ii_low=t_cmf_ii_lower, tmp_cmf_cat_iii_low=t_cmf_iii_lower, ) if round_output: t_cmf = np.around(t_cmf, 1) t_cmf_i_lower = np.around(t_cmf_i_lower, 1) t_cmf_ii_lower = np.around(t_cmf_ii_lower, 1) t_cmf_iii_lower = np.around(t_cmf_iii_lower, 1) t_cmf_i_upper = np.around(t_cmf_i_upper, 1) t_cmf_ii_upper = np.around(t_cmf_ii_upper, 1) t_cmf_iii_upper = np.around(t_cmf_iii_upper, 1) return AdaptiveEN( tmp_cmf=t_cmf, acceptability_cat_i=acceptability_i, acceptability_cat_ii=acceptability_ii, acceptability_cat_iii=acceptability_iii, tmp_cmf_cat_i_up=t_cmf_i_upper, tmp_cmf_cat_ii_up=t_cmf_ii_upper, tmp_cmf_cat_iii_up=t_cmf_iii_upper, tmp_cmf_cat_i_low=t_cmf_i_lower, tmp_cmf_cat_ii_low=t_cmf_ii_lower, tmp_cmf_cat_iii_low=t_cmf_iii_lower, )