# coding=utf-8
"""SteadyState THERM condition."""
from __future__ import division
import xml.etree.ElementTree as ET
from fairyfly._lockable import lockable
from fairyfly.typing import float_in_range, float_positive, uuid_from_therm_id
from ._base import _ThermConditionBase
[docs]
@lockable
class SteadyState(_ThermConditionBase):
"""Typical steady state condition.
Args:
temperature: A number for the temperature at the boundary in degrees Celsius.
For NFRC conditions, this temperature should be 21C for interior
boundary conditions and -18 C for winter exterior boundary conditions.
film_coefficient: A number in W/m2-K that represents the convective
resistance of the air film at the boundary condition. Typical film
coefficient values range from 36 W/m2-K (for an exterior condition
where outdoor wind strips away most convective resistance) to 2.5 W/m2-K
(for a vertically-oriented interior wood/vinyl surface). For NFRC
conditions, this should be 26 for exterior boundary conditions and
around 3 for interior boundary conditions.
emissivity: An optional number between 0 and 1 to set the emissivity
along the boundary, which represents the emissivity of the
environment to which the material in contact with the boundary is
radiating to. (Default: 1).
radiant_temperature: A number for the radiant temperature at the boundary
in degrees Celsius. If None, this will be the same as the specified
temperature. (Default: None).
heat_flux: An optional number in W/m2 that represents additional energy
flux across the boundary. This can be used to account for solar flux
among other forms of heat flux. (Default: 0).
relative_humidity: An optional value between 0 and 1 for the relative
humidity along the boundary. (Default: 0.5).
identifier: Text string for a unique object ID. Must be a UUID in the
format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. If None, a UUID will
automatically be generated. (Default: None).
Properties:
* identifier
* display_name
* temperature
* film_coefficient
* emissivity
* radiant_temperature
* heat_flux
* relative_humidity
* color
* protected
* project_tag
* user_data
"""
__slots__ = ('_temperature', '_film_coefficient', '_emissivity',
'_radiant_temperature', '_heat_flux', '_relative_humidity')
def __init__(
self, temperature, film_coefficient, emissivity=1.0, radiant_temperature=None,
heat_flux=0, relative_humidity=0.5, identifier=None
):
"""Initialize therm material."""
_ThermConditionBase.__init__(self, identifier)
self.temperature = temperature
self.film_coefficient = film_coefficient
self.emissivity = emissivity
self.radiant_temperature = radiant_temperature
self.heat_flux = heat_flux
self.relative_humidity = relative_humidity
@property
def temperature(self):
"""Get or set the temperature of the condition [C]."""
return self._temperature
@temperature.setter
def temperature(self, value):
self._temperature = \
float_in_range(value, mi=-273.15, input_name='condition temperature')
@property
def film_coefficient(self):
"""Get or set the film coefficient along the boundary [ W/m2-K]."""
return self._film_coefficient
@film_coefficient.setter
def film_coefficient(self, value):
self._film_coefficient = float_positive(value, 'film coefficient')
@property
def emissivity(self):
"""Get or set the hemispherical emissivity of the environment of the condition.
"""
return self._emissivity
@emissivity.setter
def emissivity(self, ir_e):
ir_e = float_in_range(ir_e, 0.0, 1.0, 'condition emissivity')
self._emissivity = ir_e
@property
def radiant_temperature(self):
"""Get or set the radiant temperature along the boundary."""
return self._radiant_temperature if self._radiant_temperature is not None \
else self._temperature
@radiant_temperature.setter
def radiant_temperature(self, ir_t):
if ir_t is not None:
ir_t = float_in_range(ir_t, mi=-273.15,
input_name='condition radiant temperature')
self._radiant_temperature = ir_t
@property
def heat_flux(self):
"""Get or set the additional energy flux across the boundary [W/m2]."""
return self._heat_flux
@heat_flux.setter
def heat_flux(self, value):
self._heat_flux = float_positive(value, 'condition heat flux')
@property
def relative_humidity(self):
"""Get or set a number between zero and one for the relative humidity."""
return self._relative_humidity
@relative_humidity.setter
def relative_humidity(self, value):
self._relative_humidity = \
float_in_range(value, 0.0, 1.0, 'condition relative humidity')
[docs]
@classmethod
def from_therm_xml(cls, xml_element):
"""Create SteadyState from an XML element of a THERM BoundaryCondition.
Args:
xml_element: An XML element of a THERM BoundaryCondition.
"""
# create the base material from the UUID, temperature, and film coefficient
xml_uuid = xml_element.find('UUID')
identifier = xml_uuid.text
if len(identifier) == 31:
identifier = uuid_from_therm_id(identifier)
xml_comp = xml_element.find('Comprehensive')
xml_conv = xml_comp.find('Convection')
xml_t = xml_conv.find('Temperature')
temperature = xml_t.text
xml_fc = xml_conv.find('FilmCoefficient')
film_coefficient = xml_fc.text
cond = SteadyState(temperature, film_coefficient, identifier=identifier)
# assign the other attributes if specified
xml_rh = xml_comp.find('RelativeHumidity')
if xml_rh is not None:
cond.relative_humidity = xml_rh.text
xml_flux = xml_comp.find('ConstantFlux')
if xml_flux is not None:
xml_fl = xml_flux.find('Flux')
if xml_fl is not None:
cond.heat_flux = xml_fl.text
# assign the radiation properties if specified
xml_rad = xml_comp.find('Radiation')
if xml_rad is not None:
xml_rm = xml_rad.find('AutomaticEnclosure')
if xml_rm is not None:
xml_rm = xml_rad.find('BlackBodyRadiation')
if xml_rm is not None:
xml_mrt = xml_rm.find('Temperature')
if xml_mrt is not None:
cond.radiant_temperature = xml_mrt.text
xml_emiss = xml_rm.find('Emissivity')
if xml_emiss is not None:
cond.emissivity = xml_emiss.text
# assign the name and color if they are specified
xml_name = xml_element.find('Name')
if xml_name is not None:
cond.display_name = xml_name.text
xml_col = xml_element.find('Color')
if xml_col is not None:
cond.color = xml_col.text
xml_protect = xml_element.find('Protected')
if xml_protect is not None:
cond.protected = True if xml_protect.text == 'true' else False
xml_tag = xml_element.find('ProjectNameTag')
if xml_tag is not None:
cond.project_tag = xml_tag.text
return cond
[docs]
@classmethod
def from_therm_xml_str(cls, xml_str):
"""Create SteadyState from an XML string of a THERM BoundaryCondition.
Args:
xml_str: An XML text string of a THERM BoundaryCondition.
"""
root = ET.fromstring(xml_str)
return cls.from_therm_xml(root)
[docs]
@classmethod
def from_dict(cls, data):
"""Create a SteadyState from a dictionary.
Args:
data: A python dictionary in the following format
.. code-block:: python
{
"type": 'SteadyState',
"identifier": 'f26f3597-e3ee-43fe-a0b3-ec673f993d86',
"display_name": 'NFRC 100-2010 Exterior',
"temperature": -18,
"film_coefficient": 26,
"emissivity": 1,
"radiant_temperature": -18,
"heat_flux": 0,
"relative_humidity": 0.5
}
"""
assert data['type'] == 'SteadyState', \
'Expected SteadyState. Got {}.'.format(data['type'])
emiss = data['emissivity'] if 'emissivity' in data and \
data['emissivity'] is not None else 1.0
rad_temp = data['radiant_temperature'] if 'radiant_temperature' in data else None
heat_flux = data['heat_flux'] if 'heat_flux' in data else 0
rh = data['relative_humidity'] if 'relative_humidity' in data else 0.5
new_cond = cls(
data['temperature'], data['film_coefficient'],
emiss, rad_temp, heat_flux, rh, data['identifier']
)
if 'display_name' in data and data['display_name'] is not None:
new_cond.display_name = data['display_name']
if 'color' in data and data['color'] is not None:
new_cond.color = data['color']
if 'protected' in data and data['protected'] is not None:
new_cond.protected = data['protected']
if 'project_tag' in data and data['project_tag']:
new_cond._project_tag = data['project_tag']
if 'user_data' in data and data['user_data'] is not None:
new_cond.user_data = data['user_data']
return new_cond
[docs]
def to_therm_xml(self, bcs_element=None):
"""Get an THERM XML element of the boundary condition.
Args:
bcs_element: An optional XML Element for the BoundaryConditions to
which the generated objects will be added. If None, a new XML
Element will be generated.
.. code-block:: xml
<BoundaryCondition>
<UUID>1810f37a-de4d-4e2d-95d5-09fe01157c34</UUID>
<Name>NFRC 100-2010 Exterior</Name>
<ProjectNameTag></ProjectNameTag>
<Protected>true</Protected>
<Color>0x0080C0</Color>
<IGUSurface>false</IGUSurface>
<Comprehensive>
<RelativeHumidity>0.5</RelativeHumidity>
<Convection>
<Temperature>-18</Temperature>
<FilmCoefficient>26</FilmCoefficient>
</Convection>
<ConstantFlux>
<Flux>0</Flux>
</ConstantFlux>
<Radiation>
<BlackBodyRadiation>
<Temperature>-18</Temperature>
<Emissivity>1</Emissivity>
<ViewFactor>1</ViewFactor>
</BlackBodyRadiation>
</Radiation>
</Comprehensive>
</BoundaryCondition>
"""
# create a new Materials element if one is not specified
if bcs_element is not None:
xml_cond = ET.SubElement(bcs_element, 'BoundaryCondition')
else:
xml_cond = ET.Element('BoundaryCondition')
# add all of the required basic attributes
xml_id = ET.SubElement(xml_cond, 'UUID')
xml_id.text = self.identifier
xml_name = ET.SubElement(xml_cond, 'Name')
xml_name.text = self.display_name
if self.project_tag:
xml_tag = ET.SubElement(xml_cond, 'ProjectNameTag')
xml_tag.text = self.project_tag
xml_protect = ET.SubElement(xml_cond, 'Protected')
xml_protect.text = 'true' if self.protected else 'false'
xml_color = ET.SubElement(xml_cond, 'Color')
xml_color.text = self.color.to_hex().replace('#', '0x')
xml_igu = ET.SubElement(xml_cond, 'IGUSurface')
xml_igu.text = 'false'
if self.film_coefficient == 0: # translate the condition as simple
xml_simple = ET.SubElement(xml_cond, 'Simplified')
xml_rh = ET.SubElement(xml_simple, 'RelativeHumidity')
xml_rh.text = str(self.relative_humidity)
xml_fc = ET.SubElement(xml_simple, 'FilmCoefficient')
xml_fc = '0'
xml_ct = ET.SubElement(xml_simple, 'Temperature')
xml_ct.text = str(self.temperature)
else: # add all of the comprehensive attributes
xml_comp = ET.SubElement(xml_cond, 'Comprehensive')
xml_rh = ET.SubElement(xml_comp, 'RelativeHumidity')
xml_rh.text = str(self.relative_humidity)
xml_conv = ET.SubElement(xml_comp, 'Convection')
xml_ct = ET.SubElement(xml_conv, 'Temperature')
xml_ct.text = str(self.temperature)
xml_fc = ET.SubElement(xml_conv, 'FilmCoefficient')
xml_fc.text = str(self.film_coefficient)
xml_heat = ET.SubElement(xml_comp, 'ConstantFlux')
xml_flux = ET.SubElement(xml_heat, 'Flux')
xml_flux.text = str(self.heat_flux)
xml_rad = ET.SubElement(xml_comp, 'Radiation')
xml_auto = ET.SubElement(xml_rad, 'AutomaticEnclosure')
xml_mrt = ET.SubElement(xml_auto, 'Temperature')
xml_mrt.text = str(self.radiant_temperature)
xml_emiss = ET.SubElement(xml_auto, 'Emissivity')
xml_emiss.text = str(self.emissivity)
return xml_cond
[docs]
def to_therm_xml_str(self):
"""Get an THERM XML string of the condition."""
xml_root = self.to_therm_xml()
try: # try to indent the XML to make it read-able
ET.indent(xml_root)
return ET.tostring(xml_root, encoding='unicode')
except AttributeError: # we are in Python 2 and no indent is available
return ET.tostring(xml_root)
[docs]
def to_dict(self):
"""SteadyState dictionary representation."""
base = {
'type': 'SteadyState',
'identifier': self.identifier,
'temperature': self.temperature,
'film_coefficient': self.film_coefficient
}
if self.emissivity != 1:
base['emissivity'] = self.emissivity
if self._radiant_temperature is not None:
base['radiant_temperature'] = self._radiant_temperature
if self.heat_flux != 0:
base['heat_flux'] = self.heat_flux
if self._relative_humidity != 0.5:
base['relative_humidity'] = self.relative_humidity
if self._display_name is not None:
base['display_name'] = self.display_name
base['protected'] = self._protected
base['color'] = self.color.to_hex()
if self._project_tag is not None:
base['project_tag'] = self.project_tag
if self._user_data is not None:
base['user_data'] = self.user_data
return base
def __key(self):
"""A tuple based on the object properties, useful for hashing."""
return (self.identifier, self.temperature, self.film_coefficient,
self.emissivity, self.radiant_temperature, self.heat_flux,
self.relative_humidity)
def __hash__(self):
return hash(self.__key())
def __eq__(self, other):
return isinstance(other, SteadyState) and self.__key() == other.__key()
def __ne__(self, other):
return not self.__eq__(other)
def __copy__(self):
new_cond = self.__class__(
self.temperature, self.film_coefficient,
self._emissivity, self._radiant_temperature, self._heat_flux,
self._relative_humidity, self.identifier
)
new_cond._display_name = self._display_name
new_cond._color = self._color
new_cond._protected = self._protected
new_cond._project_tag = self._project_tag
new_cond._user_data = None if self._user_data is None \
else self._user_data.copy()
return new_cond
def __repr__(self):
return 'Comprehensive THERM Condition: {}'.format(self.display_name)