# 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
unit_ventilators: Boolean to note whether individual zone ventilators are
included with the basic heating and cooling system (True) or the system only
performs heating and cooling without any ventilation (False). Unlike a DOAS,
unit ventilators do not pass the outdoor air through heating or cooling
coils to condition it for the space. However, heat recovery effectiveness
can be specified, which can temper the air supplied to the zone by the
ventilator with exhaust air. All air flow through this ventilator
is always balanced for each zone between supply and exhaust and the
total flow rate is determined by checking the ventilation and exhaust
requirements of the rooms to which the system is assigned. (Default: False).
sensible_heat_recovery: A number between 0 and 1 for the effectiveness of
sensible heat recovery within unit ventilators if specified. (Default: 0).
latent_heat_recovery: A number between 0 and 1 for the effectiveness of
latent heat recovery within unit ventilators if specified. (Default: 0).
vent_availability_schedule: An optional On/Off discrete schedule to specify when
unit ventilators shut off (assuming that the unit_ventilators is True).
This will not only reduce heating and cooling load by preventing
outdoor air from flowing but will also shut off the fans, which can
result in more energy savings when spaces served by the ventilators
are completely unoccupied. If None, the unit ventilators will be
always on. (Default: None).
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
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
* unit_ventilators
* sensible_heat_recovery
* latent_heat_recovery
* vent_availability_schedule
* radiant_type
* switch_over_time
* has_air_loop
* has_district_heating
* has_district_cooling
* balances_exhaust
* user_data
* properties
"""
__slots__ = ('_radiant_type', '_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,
unit_ventilators=False, sensible_heat_recovery=0, latent_heat_recovery=0,
vent_availability_schedule=None,
radiant_type='Floor', switch_over_time=24
):
"""Initialize HVACSystem."""
# initialize base HVAC system properties
_HeatCoolBase.__init__(
self, identifier, vintage, equipment_type, unit_ventilators,
sensible_heat_recovery, latent_heat_recovery, vent_availability_schedule
)
# set the main features of the HVAC system
self.radiant_type = radiant_type
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 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
"unit_ventilators": True, # boolean for unit ventilators
"sensible_heat_recovery": 0.75, # sensible heat recovery effectiveness
"latent_heat_recovery": 0.7, # latent heat recovery effectiveness
"vent_availability_schedule": {}, # Schedule for DOAS availability or None
"radiant_type": "CeilingMetalPanel", # type of radiant system
"switch_over_time": 24 # number of hours to switch between heating + cooling
}
"""
assert data['type'] == cls.__name__, \
'Expected {} dictionary. Got {}.'.format(cls.__name__, data['type'])
# extract the key features and properties of the HVAC
vent_avail = cls._get_schedule_from_dict(data['vent_availability_schedule']) if \
'vent_availability_schedule' in data and \
data['vent_availability_schedule'] is not None else None
erv, sensible, latent = cls._properties_from_dict(data)
f_type, sot = cls._radiant_properties_from_dict(data)
new_obj = cls(
data['identifier'], data['vintage'], data['equipment_type'],
erv, sensible, latent, vent_avail, radiant_type=f_type, switch_over_time=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
"unit_ventilators": True, # boolean for unit ventilators
"sensible_heat_recovery": 0.75, # sensible heat recovery effectiveness
"latent_heat_recovery": 0.7, # latent heat recovery effectiveness
"vent_availability_schedule": "", # Schedule id for ventilation availability
"radiant_type": "CeilingMetalPanel", # type of radiant system
"switch_over_time": 24 # number of hours to switch between heating + cooling
}
"""
assert cls.__name__ in data['type'], \
'Expected {} dictionary. Got {}.'.format(cls.__name__, data['type'])
# extract the schedule
vent_avail = None
if 'vent_availability_schedule' in data and \
data['vent_availability_schedule'] is not None:
try:
vent_avail = schedule_dict[data['vent_availability_schedule']]
except KeyError as e:
raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
erv, sensible, latent = cls._properties_from_dict(data)
f_type, sot = cls._radiant_properties_from_dict(data)
new_obj = cls(
data['identifier'], data['vintage'], data['equipment_type'],
erv, sensible, latent, vent_avail, radiant_type=f_type, switch_over_time=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]
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['switch_over_time'] = self.switch_over_time
if self.unit_ventilators:
base['unit_ventilators'] = self.unit_ventilators
if self.sensible_heat_recovery != 0:
base['sensible_heat_recovery'] = self.sensible_heat_recovery
if self.latent_heat_recovery != 0:
base['latent_heat_recovery'] = self.latent_heat_recovery
if self.vent_availability_schedule is not None:
base['vent_availability_schedule'] = \
self.vent_availability_schedule.identifier if \
abridged else self.vent_availability_schedule.to_dict()
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."""
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, sot
def __copy__(self):
new_obj = self.__class__(
self._identifier, self._vintage, self._equipment_type,
self._unit_ventilators,
self._sensible_heat_recovery, self._latent_heat_recovery,
self._vent_availability_schedule,
self._radiant_type, 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._unit_ventilators,
self._sensible_heat_recovery, self._latent_heat_recovery,
hash(self._vent_availability_schedule),
self._radiant_type, 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)