Source code for ladybug_comfort.collection.utci

# coding=utf-8
"""Object for calculating UTCI comfort from DataCollections."""
from __future__ import division

from ..utci import universal_thermal_climate_index
from ..parameter.utci import UTCIParameter
from .base import ComfortCollection
from .solarcal import OutdoorSolarCal

from ladybug._datacollectionbase import BaseCollection

from ladybug.datatype.temperature import Temperature, MeanRadiantTemperature, \
    AirTemperature, UniversalThermalClimateIndex
from ladybug.datatype.fraction import Fraction, RelativeHumidity
from ladybug.datatype.speed import Speed, WindSpeed
from ladybug.datatype.thermalcondition import ThermalComfort, ThermalCondition, \
    ThermalConditionFivePoint, ThermalConditionSevenPoint, \
    ThermalConditionNinePoint, ThermalConditionElevenPoint, UTCICategory


[docs]class UTCI(ComfortCollection): """UTCI comfort DataCollection object. Args: air_temperature: Data Collection of air temperature values in Celsius. rel_humidity: Data Collection of relative humidity values in % or a single relative humidity value to be used for the whole analysis. rad_temperature: Data Collection of mean radiant temperature (MRT) values in degrees Celsius or a single MRT value to be used for the whole analysis. If None, this will be the same as the air_temperature. wind_speed: Data Collection of meteorological wind speed values in m/s (measured 10 m above the ground) or a single wind speed value to be used for the whole analysis. If None, this will default to a low wind speed of 0.5 m/s, which is the lowest input speed that is recommended for the UTCI model. comfort_parameter: Optional UTCIParameter object to specify parameters under which conditions are considered acceptable. If None, default will assume comfort thresholds consistent with those used by meteorologists to categorize outdoor conditions. Properties: * air_temperature * rad_temperature * air_speed * rel_humidity * comfort_parameter * universal_thermal_climate_index * is_comfortable * thermal_condition * thermal_condition_five_point * thermal_condition_seven_point * thermal_condition_nine_point * thermal_condition_eleven_point * original_utci_category * percent_comfortable * percent_uncomfortable * percent_neutral * percent_hot * percent_cold * percent_extreme_cold_stress * percent_very_strong_cold_stress * percent_strong_cold_stress * percent_moderate_cold_stress * percent_slight_cold_stress * percent_slight_heat_stress * percent_moderate_heat_stress * percent_strong_heat_stress * percent_very_strong_heat_stress * percent_extreme_heat_stress """ _model = 'Universal Thermal Climate Index' __slots__ = ('_air_temperature', '_rel_humidity', '_rad_temperature', '_wind_speed', '_comfort_par', '_utci', '_thermal_category', '_air_temperature_coll', '_rel_humidity_coll', '_rad_temperature_coll', '_wind_speed_coll', '_utci_coll', '_is_comfortable_coll', '_thermal_condition_coll', '_five_point_coll', '_seven_point_coll', '_nine_point_coll', '_eleven_point_coll', '_original_category_coll') def __init__(self, air_temperature, rel_humidity, rad_temperature=None, wind_speed=None, comfort_parameter=None): """Initialize a UTCI comfort object from DataCollections of UTCI inputs. """ # set up the object using air temperature as a base self._check_datacoll(air_temperature, Temperature, 'C', 'air_temperature') self._input_collections = [air_temperature] self._calc_length = len(air_temperature.values) self._base_collection = air_temperature # check required inputs self._air_temperature = air_temperature.values self._rel_humidity = self._check_input( rel_humidity, Fraction, '%', 'rel_humidity') # check inputs with defaults if rad_temperature is not None: self._rad_temperature = self._check_input( rad_temperature, Temperature, 'C', 'rad_temperature') else: self._rad_temperature = self._air_temperature if wind_speed is not None: self._wind_speed = self._check_input( wind_speed, Speed, 'm/s', 'air_speed') else: self._wind_speed = [0.5] * self.calc_length # check that all input data collections are aligned. BaseCollection.are_collections_aligned(self._input_collections) # check comfort parameters if comfort_parameter is None: self._comfort_par = UTCIParameter() else: assert isinstance(comfort_parameter, UTCIParameter), 'comfort_parameter '\ 'must be a UTCIParameter object. Got {}'.format(type(comfort_parameter)) self._comfort_par = comfort_parameter # compute UTCI self._calculate_utci()
[docs] @classmethod def from_epw(cls, epw, include_wind=True, include_sun=True, utci_parameter=None): """Get a UTCI comfort object from the conditions within an EPW file. Args: epw: A ladybug EPW object from which the UTCI object will be created. include_wind: Set to True to include the EPW wind speed in the calculation. Setting to False will assume a condition that is shielded from wind where the human subject experiences a low wind speed of 0.5 m/s, which is the lowest input speed that is recommended for the UTCI model. Default: True to include wind. include_sun: Set to True to include the mean radiant temperature (MRT) delta from both shortwave solar falling directly on people and long wave radiant exchange with the sky. Setting to False will assume a shaded condition with MRT being equal to the EPW dry bulb temperature. When set to True, this calculation will assume no surrounding shade context, standing human geometry, and a solar horizontal angle relative to front of person (SHARP) of 135 degrees. A SHARP of 135 essentially assumes that a person typically faces their side or back to the sun to avoid glare. Default: True to include sun. utci_parameter: Optional UTCIParameter object to specify parameters under which conditions are considered acceptable. If None, default will assume comfort thresholds consistent with those used by meterologists to categorize outdoor conditions. Returns: A UTCI object with data collections of the results as properties. Usage: .. code-block:: python from ladybug.epw import EPW from ladybug_comfort.collection.utci import UTCI epw_file_path = './tests/epw/chicago.epw' epw = EPW(epw_file_path) utci_exposed = UTCI.from_epw(epw, include_wind=True, include_sun=True) utci_protected = UTCI.from_epw(epw, include_wind=False, include_sun=False) print(utci_exposed.percent_neutral) # comfortable % with sun + wind print(utci_protected.percent_neutral) # comfortable % without sun + wind """ # Get wind and mrt inputs wind_speed = epw.wind_speed if include_wind is True else 0.5 if include_sun is True: solarcal_obj = OutdoorSolarCal(epw.location, epw.direct_normal_radiation, epw.diffuse_horizontal_radiation, epw.horizontal_infrared_radiation_intensity, epw.dry_bulb_temperature) mrt = solarcal_obj.mean_radiant_temperature else: mrt = epw.dry_bulb_temperature # return the comfort object return cls(epw.dry_bulb_temperature, epw.relative_humidity, mrt, wind_speed, utci_parameter)
def _calculate_utci(self): """Compute UTCI for each step of the Data Collection.""" self._utci = [] self._thermal_category = [] for ta, tr, vel, rh in \ zip(self._air_temperature, self._rad_temperature, self._wind_speed, self._rel_humidity): result = universal_thermal_climate_index(ta, tr, vel, rh) self._utci.append(result) self._thermal_category.append( self._comfort_par.thermal_condition_eleven_point(result)) @property def air_temperature(self): """Data Collection of air temperature values in degrees C.""" return self._get_coll('_air_temperature_coll', self._air_temperature, AirTemperature, 'C') @property def rad_temperature(self): """Data Collection of mean radiant temperature (MRT) values in degrees C.""" return self._get_coll('_rad_temperature_coll', self._rad_temperature, MeanRadiantTemperature, 'C') @property def wind_speed(self): """Data Collection of air speed values in m/s.""" return self._get_coll('_wind_speed_coll', self._wind_speed, WindSpeed, 'm/s') @property def rel_humidity(self): """Data Collection of relative humidity values in %.""" return self._get_coll('_rel_humidity_coll', self._rel_humidity, RelativeHumidity, '%') @property def comfort_parameter(self): """UTCI comfort parameters that are assigned to this object.""" return self._comfort_par @property def universal_thermal_climate_index(self): """A Data Collection of Universal Thermal Climate Index (UTCI) in C.""" return self._get_coll('_utci_coll', self._utci, UniversalThermalClimateIndex, 'C') @property def is_comfortable(self): """Data Collection of integers noting whether the input conditions are acceptable according to the assigned comfort_parameter. Values are one of the following: * 0 = uncomfortable * 1 = comfortable """ return self._get_coll('_is_comfortable_coll', self._comf_val_funct, ThermalComfort, 'condition') @property def thermal_condition(self): """Data Collection of integers noting the thermal status of a subject according to the assigned comfort_parameter. Values are one of the following: * -1 = cold * 0 = netural * +1 = hot """ return self._get_coll('_thermal_condition_coll', self._condit_val_funct, ThermalCondition, 'condition') @property def thermal_condition_five_point(self): """Data Collection of integers noting the thermal status on a five-point scale. Values are one of the following: * -2 = strong/extreme cold stress * -1 = moderate cold stress * 0 = no thermal stress * +1 = moderate heat stress * +2 = strong/extreme heat stress """ return self._get_coll('_five_point_coll', self._five_pt_funct, ThermalConditionFivePoint, 'condition') @property def thermal_condition_seven_point(self): """Data Collection of integers noting the thermal status on a seven-point scale. Values are one of the following: * -3 = very strong/extreme cold stress * -2 = strong cold stress * -1 = moderate cold stress * 0 = no thermal stress * +1 = moderate heat stress * +2 = strong heat stress * +3 = very strong/extreme heat stress """ return self._get_coll('_seven_point_coll', self._seven_pt_funct, ThermalConditionSevenPoint, 'condition') @property def thermal_condition_nine_point(self): """Data Collection of integers noting the thermal status on a nine-point scale. Values are one of the following: * -4 = very strong/extreme cold stress * -3 = strong cold stress * -2 = moderate cold stress * -1 = slight cold stress * 0 = no thermal stress * +1 = slight heat stress * +2 = moderate heat stress * +3 = strong heat stress * +4 = very strong/extreme heat stress """ return self._get_coll('_nine_point_coll', self._nine_pt_funct, ThermalConditionNinePoint, 'condition') @property def thermal_condition_eleven_point(self): """Data Collection of integers noting the thermal status on an eleven-point scale. Values are one of the following: * -5 = extreme cold stress * -4 = very strong cold stress * -3 = strong cold stress * -2 = moderate cold stress * -1 = slight cold stress * 0 = no thermal stress * +1 = slight heat stress * +2 = moderate heat stress * +3 = strong heat stress * +4 = very strong heat stress * +5 = extreme heat stress """ return self._get_coll('_eleven_point_coll', self._thermal_category, ThermalConditionElevenPoint, 'condition') @property def original_utci_category(self): """Data Collection of integers noting the original UTCI assessment scale. Glossary of Terms for Thermal Physiology (2003). Journal of Thermal Biology 28, 75-106 Values are one of the following: * 0 = extreme cold stress * 1 = very strong cold stress * 2 = strong cold stress * 3 = moderate cold stress * 4 = slight cold stress * 5 = no thermal stress * 6 = moderate heat stress * 7 = strong heat stress * 8 = strong heat stress * 9 = extreme heat stress """ return self._get_coll('_original_category_coll', self._original_category_funct, UTCICategory, 'condition') @property def percent_comfortable(self): """The percent of time comfortabe given by the assigned comfort_parameter.""" _vals = [1 for t in self._thermal_category if t == 0] return (sum(_vals) / self._calc_length) * 100 @property def percent_uncomfortable(self): """The percent of time uncomfortable given by the assigned comfort_parameter.""" return 100 - self.percent_comfortable @property def percent_neutral(self): """The percent of time that the thermal_condition is neutral.""" return self.percent_comfortable @property def percent_cold(self): """The percent of time that the thermal_condition is cold.""" _vals = [1 for x in self._thermal_category if x < 0] return (sum(_vals) / self._calc_length) * 100 @property def percent_hot(self): """The percent of time that the thermal_condition is hot.""" _vals = [1 for x in self._thermal_category if x > 0] return (sum(_vals) / self._calc_length) * 100 @property def percent_slight_cold_stress(self): """The percent of time that conditions have slight cold stress.""" _vals = [1 for x in self._thermal_category if x == -1] return (sum(_vals) / self._calc_length) * 100 @property def percent_moderate_cold_stress(self): """The percent of time that conditions have moderate cold stress.""" _vals = [1 for x in self._thermal_category if x == -2] return (sum(_vals) / self._calc_length) * 100 @property def percent_strong_cold_stress(self): """The percent of time that conditions have strong cold stress.""" _vals = [1 for x in self._thermal_category if x == -3] return (sum(_vals) / self._calc_length) * 100 @property def percent_very_strong_cold_stress(self): """The percent of time that conditions have very strong cold stress.""" _vals = [1 for x in self._thermal_category if x == -4] return (sum(_vals) / self._calc_length) * 100 @property def percent_extreme_cold_stress(self): """The percent of time that conditions have very strong cold stress.""" _vals = [1 for x in self._thermal_category if x == -5] return (sum(_vals) / self._calc_length) * 100 @property def percent_slight_heat_stress(self): """The percent of time that conditions have slight heat stress.""" _vals = [1 for x in self._thermal_category if x == 1] return (sum(_vals) / self._calc_length) * 100 @property def percent_moderate_heat_stress(self): """The percent of time that conditions have moderate heat stress.""" _vals = [1 for x in self._thermal_category if x == 2] return (sum(_vals) / self._calc_length) * 100 @property def percent_strong_heat_stress(self): """The percent of time that conditions have strong heat stress.""" _vals = [1 for x in self._thermal_category if x == 3] return (sum(_vals) / self._calc_length) * 100 @property def percent_very_strong_heat_stress(self): """The percent of time that conditions have very strong heat stress.""" _vals = [1 for x in self._thermal_category if x == 4] return (sum(_vals) / self._calc_length) * 100 @property def percent_extreme_heat_stress(self): """The percent of time that conditions have very strong heat stress.""" _vals = [1 for x in self._thermal_category if x == 5] return (sum(_vals) / self._calc_length) * 100 def _comf_val_funct(self): return [self._comfort_par.is_comfortable(t) for t in self._utci] def _condit_val_funct(self): return [self._comfort_par.thermal_condition(t) for t in self._utci] def _five_pt_funct(self): return [self._comfort_par.thermal_condition_five_point(t) for t in self._utci] def _seven_pt_funct(self): return [self._comfort_par.thermal_condition_seven_point(t) for t in self._utci] def _nine_pt_funct(self): return [self._comfort_par.thermal_condition_nine_point(t) for t in self._utci] def _original_category_funct(self): return [self._comfort_par.original_utci_category(t) for t in self._utci]