# coding=utf-8
"""Low temperature radiant system."""
from __future__ import division
from ._base import _HeatCoolBase
from honeybee._lockable import lockable
from honeybee.typing import float_positive, valid_string
[docs]
@lockable
class Radiant(_HeatCoolBase):
"""Low temperature radiant HVAC system.
This HVAC template will change the floor and/or ceiling constructions
of the Rooms that it is applied to, replacing them with a construction that
aligns with the radiant_type property (eg. CeilingMetalPanel).
The heating and cooling needs of the space are met with the radiant constructions,
which use chilled water at 12.8C (55F) and a hot water temperature somewhere
between 32.2C (90F) and 49C (120F) (warmer temperatures are used in colder
climate zones).
Note that radiant systems are particularly limited in cooling capacity and
using them may result in many unmet hours. To reduce unmet hours, one can
remove carpets, reduce internal loads, reduce solar and envelope gains during
peak times, add thermal mass, and use an expanded comfort range.
Args:
identifier: Text string for system identifier. Must be < 100 characters
and not contain any EnergyPlus special characters. This will be used to
identify the object across a model and in the exported IDF.
vintage: Text for the vintage of the template system. This will be used
to set efficiencies for various pieces of equipment within the system.
Choose from the following.
* DOE_Ref_Pre_1980
* DOE_Ref_1980_2004
* ASHRAE_2004
* ASHRAE_2007
* ASHRAE_2010
* ASHRAE_2013
* ASHRAE_2016
* ASHRAE_2019
equipment_type: Text for the specific type of the system and equipment. (Default:
the first option below) Choose from.
* Radiant_Chiller_Boiler
* Radiant_Chiller_ASHP
* Radiant_Chiller_DHW
* Radiant_ACChiller_Boiler
* Radiant_ACChiller_ASHP
* Radiant_ACChiller_DHW
* Radiant_DCW_Boiler
* Radiant_DCW_ASHP
* Radiant_DCW_DHW
radiant_type: Text to indicate which faces are thermally active. Note
that systems are assumed to be embedded in concrete slabs unless
CeilingMetalPanel or FloorWithHardwood is specified. Choose from the
following. (Default: Floor).
* Floor
* Ceiling
* FloorWithCarpet
* CeilingMetalPanel
* FloorWithHardwood
minimum_operation_time: A number for the minimum number of hours of operation
for the radiant system before it shuts off. Note that this has no effect
if the radiant_type is not in a slab. (Default: 1).
switch_over_time: A number for the minimum number of hours for when the system
can switch between heating and cooling. Note that this has no effect
if the radiant_type is not in a slab. (Default: 24).
Properties:
* identifier
* display_name
* vintage
* equipment_type
* radiant_type
* minimum_operation_time
* switch_over_time
* schedules
* has_district_heating
* has_district_cooling
* user_data
* properties
"""
__slots__ = ('_radiant_type', '_minimum_operation_time', '_switch_over_time')
EQUIPMENT_TYPES = (
'Radiant_Chiller_Boiler',
'Radiant_Chiller_ASHP',
'Radiant_Chiller_DHW',
'Radiant_ACChiller_Boiler',
'Radiant_ACChiller_ASHP',
'Radiant_ACChiller_DHW',
'Radiant_DCW_Boiler',
'Radiant_DCW_ASHP',
'Radiant_DCW_DHW'
)
radiant_typeS = ('Floor', 'Ceiling', 'FloorWithCarpet',
'CeilingMetalPanel', 'FloorWithHardwood')
def __init__(self, identifier, vintage='ASHRAE_2019', equipment_type=None,
radiant_type='Floor', minimum_operation_time=1,
switch_over_time=24):
"""Initialize HVACSystem."""
# initialize base HVAC system properties
_HeatCoolBase.__init__(self, identifier, vintage, equipment_type)
# set the main features of the HVAC system
self.radiant_type = radiant_type
self.minimum_operation_time = minimum_operation_time
self.switch_over_time = switch_over_time
@property
def radiant_type(self):
"""Get or set text to indicate the type of radiant system."""
return self._radiant_type
@radiant_type.setter
def radiant_type(self, value):
clean_input = valid_string(value).lower()
for key in self.radiant_typeS:
if key.lower() == clean_input:
value = key
break
else:
raise ValueError(
'radiant_type {} is not recognized.\nChoose from the '
'following:\n{}'.format(value, self.radiant_typeS))
self._radiant_type = value
@property
def minimum_operation_time(self):
"""Get or set a the minimum hours of operation before the system shuts off."""
return self._minimum_operation_time
@minimum_operation_time.setter
def minimum_operation_time(self, value):
self._minimum_operation_time = \
float_positive(value, 'hvac minimum operation time')
@property
def switch_over_time(self):
"""Get or set the minimum hours the system can switch between heating/cooling."""
return self._switch_over_time
@switch_over_time.setter
def switch_over_time(self, value):
self._switch_over_time = float_positive(value, 'hvac switch over time')
[docs]
@classmethod
def from_dict(cls, data):
"""Create a HVAC object from a dictionary.
Args:
data: A dictionary in following the format below.
.. code-block:: python
{
"type": "", # text for the class name of the HVAC
"identifier": "Classroom1_System", # identifier for the HVAC
"display_name": "Standard System", # name for the HVAC
"vintage": "ASHRAE_2019", # text for the vintage of the template
"equipment_type": "", # text for the HVAC equipment type
"radiant_type": "Ceiling",
"minimum_operation_time": 1,
"switch_over_time": 24
}
"""
assert data['type'] == cls.__name__, \
'Expected {} dictionary. Got {}.'.format(cls.__name__, data['type'])
# extract the key features and properties of the HVAC
f_type, mot, sot = cls._radiant_properties_from_dict(data)
new_obj = cls(data['identifier'], data['vintage'], data['equipment_type'],
f_type, mot, sot)
if 'display_name' in data and data['display_name'] is not None:
new_obj.display_name = data['display_name']
if 'user_data' in data and data['user_data'] is not None:
new_obj.user_data = data['user_data']
return new_obj
[docs]
@classmethod
def from_dict_abridged(cls, data, schedule_dict):
"""Create a HVAC object from an abridged dictionary.
Args:
data: An abridged dictionary in following the format below.
schedule_dict: A dictionary with schedule identifiers as keys and honeybee
schedule objects as values (either ScheduleRuleset or
ScheduleFixedInterval). These will be used to assign the schedules
to the Setpoint object.
.. code-block:: python
{
"type": "", # text for the class name of the HVAC
"identifier": "Classroom1_System", # identifier for the HVAC
"display_name": "Standard System", # name for the HVAC
"vintage": "ASHRAE_2019", # text for the vintage of the template
"equipment_type": "", # text for the HVAC equipment type
"radiant_type": "Ceiling",
"minimum_operation_time": 1,
"switch_over_time": 24
}
"""
# this is the same as the from_dict method for as long as there are not schedules
return cls.from_dict(data)
[docs]
def to_dict(self, abridged=False):
"""Radiant system dictionary representation.
Args:
abridged: Boolean to note whether the full dictionary describing the
object should be returned (False) or just an abridged version (True).
This input currently has no effect but may eventually have one if
schedule-type properties are exposed on this template.
"""
base = {'type': self.__class__.__name__}
base['identifier'] = self.identifier
base['vintage'] = self.vintage
base['equipment_type'] = self.equipment_type
base['radiant_type'] = self.radiant_type
base['minimum_operation_time'] = self.minimum_operation_time
base['switch_over_time'] = self.switch_over_time
if self._display_name is not None:
base['display_name'] = self.display_name
if self._user_data is not None:
base['user_data'] = self.user_data
return base
@staticmethod
def _radiant_properties_from_dict(data):
"""Extract basic radiant properties from a dictionary and assign defaults."""
mot = data['minimum_operation_time'] if 'minimum_operation_time' in data else 1
sot = data['switch_over_time'] if 'switch_over_time' in data else 24
rad_type = data['radiant_type'] if 'radiant_type' in data and \
data['radiant_type'] is not None else 'Floor'
return rad_type, mot, sot
def __copy__(self):
new_obj = self.__class__(
self._identifier, self._vintage, self._equipment_type,
self._radiant_type, self._minimum_operation_time,
self._switch_over_time)
new_obj._display_name = self._display_name
new_obj._user_data = None if self._user_data is None else self._user_data.copy()
return new_obj
def __key(self):
"""A tuple based on the object properties, useful for hashing."""
return (self._identifier, self._vintage, self._equipment_type,
self._radiant_type, self._minimum_operation_time,
self._switch_over_time)
def __hash__(self):
return hash(self.__key())
def __eq__(self, other):
return isinstance(other, self.__class__) and self.__key() == other.__key()
def __ne__(self, other):
return not self.__eq__(other)