# coding=utf-8
"""Complete definition of a zone program, including schedules and loads."""
from __future__ import division
import random
from .load.people import People
from .load.lighting import Lighting
from .load.equipment import ElectricEquipment, GasEquipment
from .load.hotwater import ServiceHotWater
from .load.infiltration import Infiltration
from .load.ventilation import Ventilation
from .load.setpoint import Setpoint
from honeybee._lockable import lockable
from honeybee.typing import valid_ep_string, tuple_with_length, clean_and_id_ep_string
[docs]
@lockable
class ProgramType(object):
"""Program Type object possessing all schedules and loads defining a program.
Args:
identifier: Text string for a unique ProgramType ID. 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.
people: A People object to describe the occupancy of the program. If None,
no occupancy will be assumed for the program. (Default: None).
lighting: A Lighting object to describe the lighting usage of the program.
If None, no lighting will be assumed for the program. (Default: None).
electric_equipment: An ElectricEquipment object to describe the usage
of electric equipment within the program. If None, no electric equipment
will be assumed for the program. (Default: None).
gas_equipment: A GasEquipment object to describe the usage of gas equipment
within the program. If None, no gas equipment will be assumed for
the program. (Default: None).
service_hot_water: A ServiceHotWater object to describe the usage of hot
water within the program. If None, no hot water will be assumed for
the program. (Default: None).
infiltration: An Infiltration object to describe the outdoor air leakage of
the program. If None, no infiltration will be assumed for the program.
(Default: None).
ventilation: A Ventilation object to describe the minimum outdoor air
requirement of the program. If None, no ventilation requirement will
be assumed for the program. Default: None
setpoint: A Setpoint object to describe the temperature and humidity
setpoints of the program. If None, the ProgramType cannot be assigned
to a Room that is conditioned. (Default: None).
Properties:
* identifier
* display_name
* people
* lighting
* electric_equipment
* gas_equipment
* service_hot_water
* infiltration
* ventilation
* setpoint
* schedules
* schedules_unique
* user_data
"""
__slots__ = ('_identifier', '_display_name', '_people', '_lighting',
'_electric_equipment', '_gas_equipment', '_service_hot_water',
'_infiltration', '_ventilation', '_setpoint', '_locked', '_user_data')
def __init__(self, identifier, people=None, lighting=None, electric_equipment=None,
gas_equipment=None, service_hot_water=None,
infiltration=None, ventilation=None, setpoint=None):
"""Initialize ProgramType"""
self._locked = False # unlocked by default
self.identifier = identifier
self._display_name = None
self.people = people
self.lighting = lighting
self.electric_equipment = electric_equipment
self.gas_equipment = gas_equipment
self.service_hot_water = service_hot_water
self.infiltration = infiltration
self.ventilation = ventilation
self.setpoint = setpoint
self._user_data = None
@property
def identifier(self):
"""Get or set the text string for a unique program type identifier."""
return self._identifier
@identifier.setter
def identifier(self, identifier):
self._identifier = valid_ep_string(identifier, 'program type identifier')
@property
def display_name(self):
"""Get or set a string for the object name without any character restrictions.
If not set, this will be equal to the identifier.
"""
if self._display_name is None:
return self._identifier
return self._display_name
@display_name.setter
def display_name(self, value):
if value is not None:
try:
value = str(value)
except UnicodeEncodeError: # Python 2 machine lacking the character set
pass # keep it as unicode
self._display_name = value
@property
def people(self):
"""Get or set a People object to describe the occupancy of the program."""
return self._people
@people.setter
def people(self, value):
if value is not None:
assert isinstance(value, People), 'Expected People object for ' \
'ProgramType.people. Got {}.'.format(type(value))
self._people = value
@property
def lighting(self):
"""Get or set a Lighting object to describe the lighting usage of the program."""
return self._lighting
@lighting.setter
def lighting(self, value):
if value is not None:
assert isinstance(value, Lighting), 'Expected Lighting object for ' \
'ProgramType.lighting. Got {}.'.format(type(value))
self._lighting = value
@property
def electric_equipment(self):
"""Get or set an ElectricEquipment object to describe the usage of equipment."""
return self._electric_equipment
@electric_equipment.setter
def electric_equipment(self, value):
if value is not None:
assert isinstance(value, ElectricEquipment), 'Expected ElectricEquipment ' \
'object for ProgramType.electric_equipment. Got {}.'.format(type(value))
self._electric_equipment = value
@property
def gas_equipment(self):
"""Get or set a GasEquipment object to describe the usage of equipment."""
return self._gas_equipment
@gas_equipment.setter
def gas_equipment(self, value):
if value is not None:
assert isinstance(value, GasEquipment), 'Expected GasEquipment ' \
'object for ProgramType.gas_equipment. Got {}.'.format(type(value))
self._gas_equipment = value
@property
def service_hot_water(self):
"""Get or set a ServiceHotWater object to describe the usage of hot water."""
return self._service_hot_water
@service_hot_water.setter
def service_hot_water(self, value):
if value is not None:
assert isinstance(value, ServiceHotWater), 'Expected ServiceHotWater ' \
'object for ProgramType.service_hot_water. Got {}.'.format(type(value))
self._service_hot_water = value
@property
def infiltration(self):
"""Get or set an Infiltration object to describe the outdoor air leakage."""
return self._infiltration
@infiltration.setter
def infiltration(self, value):
if value is not None:
assert isinstance(value, Infiltration), 'Expected Infiltration ' \
'object for ProgramType.infiltration. Got {}.'.format(type(value))
self._infiltration = value
@property
def ventilation(self):
"""Get or set a Ventilation object to describe the minimum outdoor air flow."""
return self._ventilation
@ventilation.setter
def ventilation(self, value):
if value is not None:
assert isinstance(value, Ventilation), 'Expected Ventilation ' \
'object for ProgramType.ventilation. Got {}.'.format(type(value))
self._ventilation = value
@property
def setpoint(self):
"""Get or set a Setpoint object to describe the temperature setpoints."""
return self._setpoint
@setpoint.setter
def setpoint(self, value):
if value is not None:
assert isinstance(value, Setpoint), 'Expected Setpoint ' \
'object for ProgramType.setpoint. Got {}.'.format(type(value))
self._setpoint = value
@property
def schedules(self):
"""List of all schedules contained within the ProgramType."""
sched = []
if self.people is not None:
sched.append(self.people.occupancy_schedule)
sched.append(self.people.activity_schedule)
if self.lighting is not None:
sched.append(self.lighting.schedule)
if self.electric_equipment is not None:
sched.append(self.electric_equipment.schedule)
if self.gas_equipment is not None:
sched.append(self.gas_equipment.schedule)
if self.service_hot_water is not None:
sched.append(self.service_hot_water.schedule)
if self.infiltration is not None:
sched.append(self.infiltration.schedule)
if self.ventilation is not None and self.ventilation.schedule is not None:
sched.append(self.ventilation.schedule)
if self.setpoint is not None:
sched.append(self.setpoint.heating_schedule)
sched.append(self.setpoint.cooling_schedule)
if self.setpoint.humidifying_schedule is not None:
sched.append(self.setpoint.humidifying_schedule)
sched.append(self.setpoint.dehumidifying_schedule)
return sched
@property
def schedules_unique(self):
"""List of all unique schedules contained within the ProgramType."""
return list(set(self.schedules))
@property
def user_data(self):
"""Get or set an optional dictionary for additional meta data for this object.
This will be None until it has been set. All keys and values of this
dictionary should be of a standard Python type to ensure correct
serialization of the object to/from JSON (eg. str, float, int, list, dict)
"""
return self._user_data
@user_data.setter
def user_data(self, value):
if value is not None:
assert isinstance(value, dict), 'Expected dictionary for honeybee_energy' \
'object user_data. Got {}.'.format(type(value))
self._user_data = value
[docs]
@classmethod
def from_dict(cls, data):
"""Create a ProgramType from a dictionary.
Note that the dictionary must be a non-abridged version for this
classmethod to work.
Args:
data: Dictionary describing the ProgramType with the format below.
.. code-block:: python
{
"type": 'ProgramType',
"identifier": str, # ProgramType identifier
"display_name": str, # ProgramType display name
'people': {}, # A People dictionary
'lighting': {}, # A Lighting dictionary
'electric_equipment': {}, # A ElectricEquipment dictionary
'gas_equipment': {}, # A GasEquipment dictionary
'service_hot_water': {}, # A ServiceHotWater dictionary
'infiltration': {}, # A Infliltration dictionary
'ventilation': {}, # A Ventilation dictionary
'setpoint': {} # A Setpoint dictionary
}
"""
assert data['type'] == 'ProgramType', \
'Expected ProgramType. Got {}.'.format(data['type'])
# build each of the load objects
people = People.from_dict(data['people']) if 'people' in data and \
data['people'] is not None else None
lighting = Lighting.from_dict(data['lighting']) if 'lighting' in data and \
data['lighting'] is not None else None
electric_equipment = ElectricEquipment.from_dict(data['electric_equipment']) \
if 'electric_equipment' in data and \
data['electric_equipment'] is not None else None
gas_equipment = GasEquipment.from_dict(data['gas_equipment']) \
if 'gas_equipment' in data and \
data['gas_equipment'] is not None else None
shw = ServiceHotWater.from_dict(data['service_hot_water']) \
if 'service_hot_water' in data and \
data['service_hot_water'] is not None else None
infiltration = Infiltration.from_dict(data['infiltration']) if 'infiltration' \
in data and data['infiltration'] is not None else None
ventilation = Ventilation.from_dict(data['ventilation']) if 'ventilation' \
in data and data['ventilation'] is not None else None
setpoint = Setpoint.from_dict(data['setpoint']) if 'setpoint' in data and \
data['setpoint'] is not None else None
new_obj = cls(data['identifier'], people, lighting, electric_equipment,
gas_equipment, shw, infiltration, ventilation, setpoint)
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 ProgramType object from an abridged dictionary.
Args:
data: A ProgramTypeAbridged dictionary.
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 ProgramType object.
.. code-block:: python
{
"type": 'ProgramTypeAbridged',
"identifier": str, # ProgramType identifier
"display_name": str, # ProgramType display name
'people': {}, # A PeopleAbridged dictionary
'lighting': {}, # A LightingAbridged dictionary
'electric_equipment': {}, # A ElectricEquipmentAbridged dictionary
'gas_equipment': {}, # A GasEquipmentAbridged dictionary
'service_hot_water': {}, # A ServiceHotWaterAbridged dictionary
'infiltration': {}, # A InfiltrationAbridged dictionary
'ventilation': {}, # A VentilationAbridged dictionary
'setpoint': {} # A SetpointAbridged dictionary
}
"""
assert data['type'] == 'ProgramTypeAbridged', \
'Expected ProgramTypeAbridged dictionary. Got {}.'.format(data['type'])
# build each of the load objects
try:
people, lighting, electric_equipment, gas_equipment, shw, infiltration, \
ventilation, setpoint = cls._get_loads_from_abridged(data, schedule_dict)
except KeyError as e:
raise ValueError(
'The following schedule is missing from the model: {}'.format(e)
)
new_obj = cls(data['identifier'], people, lighting, electric_equipment,
gas_equipment, shw, infiltration, ventilation, setpoint)
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):
"""Get ProgramType as a dictionary.
Args:
abridged: Boolean noting whether detailed schedule objects should be
written into the ProgramType (False) or just an abridged version (True)
that references the schedules by identifier. Default: False.
"""
base = {'type': 'ProgramType'} if not \
abridged else {'type': 'ProgramTypeAbridged'}
base['identifier'] = self.identifier
if self.people is not None:
base['people'] = self.people.to_dict(abridged)
if self.lighting is not None:
base['lighting'] = self.lighting.to_dict(abridged)
if self.electric_equipment is not None:
base['electric_equipment'] = self.electric_equipment.to_dict(abridged)
if self.gas_equipment is not None:
base['gas_equipment'] = self.gas_equipment.to_dict(abridged)
if self.service_hot_water is not None:
base['service_hot_water'] = self.service_hot_water.to_dict(abridged)
if self.infiltration is not None:
base['infiltration'] = self.infiltration.to_dict(abridged)
if self.ventilation is not None:
base['ventilation'] = self.ventilation.to_dict(abridged)
if self.setpoint is not None:
base['setpoint'] = self.setpoint.to_dict(abridged)
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
[docs]
def diversify(self, program_count, occupancy_stdev=20, lighting_stdev=20,
electric_equip_stdev=20, gas_equip_stdev=20, hot_water_stdev=20,
infiltration_stdev=20, schedule_offset=1, timestep=1):
"""Get an array of diversified ProgramTypes derived from this "average" one.
This method is useful when attempting to account for the fact that not
all rooms within a building will be used by occupants according to a
strict regimen. Some rooms will be used more than expected and others less.
This method uses a random number generator and gaussian distribution to
generate loads that vary about the mean program. Note that the randomly
generated values can be set to something predictable by using the native
Python random.seed() method before running this method.
In addition to diversifying load values, approximately 2/3 of the schedules
in the output programs will be offset from the mean by the input
schedule_offset (1/3 ahead and another 1/3 behind).
Args:
program_count: An positive integer for the number of diversified programs
to generate from this mean program.
occupancy_stdev: A number between 0 and 100 for the percent of the
occupancy people_per_area representing one standard deviation
of diversification from the mean. (Default 20 percent).
lighting_stdev: A number between 0 and 100 for the percent of the
lighting watts_per_area representing one standard deviation
of diversification from the mean. (Default 20 percent).
electric_equip_stdev: A number between 0 and 100 for the percent of the
electric equipment watts_per_area representing one standard deviation
of diversification from the mean. (Default 20 percent).
gas_equip_stdev: A number between 0 and 100 for the percent of the
gas equipment watts_per_area representing one standard deviation
of diversification from the mean. (Default 20 percent).
hot_water_stdev: A number between 0 and 100 for the percent of the
service hot water flow_per_area representing one standard deviation
of diversification from the mean. (Default 20 percent).
infiltration_stdev: A number between 0 and 100 for the percent of the
infiltration flow_per_exterior_area representing one standard deviation
of diversification from the mean. (Default 20 percent).
schedule_offset: A positive integer for the number of timesteps at which all
schedules of the resulting programs will be shifted - roughly 1/3 of
the programs ahead and another 1/3 behind. (Default: 1).
timestep: An integer for the number of timesteps per hour at which the
shifting is occurring. This must be a value between 1 and 60, which
is evenly divisible by 60. 1 indicates that each step is an hour
while 60 indicates that each step is a minute. (Default: 1).
"""
# duplicate the input programs so that they can be diversified
div_programs = [self.duplicate() for i in range(program_count)]
for program in div_programs:
program.identifier = clean_and_id_ep_string(self.identifier)
sch_int = [random.randint(0, 2) for i in range(program_count)]
# go through each load and generate diversified versions for the div_programs
if self.people is not None and occupancy_stdev != 0:
div_people = self.people.diversify(
program_count, occupancy_stdev, schedule_offset, timestep, sch_int)
for i, ppl in enumerate(div_people):
div_programs[i].people = ppl
if self.lighting is not None and lighting_stdev != 0:
div_lighting = self.lighting.diversify(
program_count, lighting_stdev, schedule_offset, timestep, sch_int)
for i, light in enumerate(div_lighting):
div_programs[i].lighting = light
if self.electric_equipment is not None and electric_equip_stdev != 0:
div_e_equipment = self.electric_equipment.diversify(
program_count, electric_equip_stdev, schedule_offset, timestep, sch_int)
for i, e_equip in enumerate(div_e_equipment):
div_programs[i].electric_equipment = e_equip
if self.gas_equipment is not None and gas_equip_stdev != 0:
div_g_equipment = self.gas_equipment.diversify(
program_count, gas_equip_stdev, schedule_offset, timestep, sch_int)
for i, g_equip in enumerate(div_g_equipment):
div_programs[i].gas_equipment = g_equip
if self.service_hot_water is not None and hot_water_stdev != 0:
div_hot_water = self.service_hot_water.diversify(
program_count, hot_water_stdev, schedule_offset, timestep, sch_int)
for i, shw in enumerate(div_hot_water):
div_programs[i].service_hot_water = shw
if self.infiltration is not None and infiltration_stdev != 0:
div_infiltration = self.infiltration.diversify(
program_count, infiltration_stdev, schedule_offset, timestep, sch_int)
for i, inf in enumerate(div_infiltration):
div_programs[i].infiltration = inf
if self.setpoint is not None and schedule_offset != 0:
div_setpoint = self.setpoint.diversify(
program_count, schedule_offset, timestep, sch_int)
for i, setpt in enumerate(div_setpoint):
div_programs[i].setpoint = setpt
return div_programs
[docs]
@staticmethod
def average(identifier, program_types, weights=None, timestep_resolution=1):
"""Get a ProgramType object that's a weighted average between other objects.
Args:
identifier: A unique ID text string for the new averaged ProgramType.
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.
program_types: A list of ProgramType objects that will be averaged
together to make a new ProgramType.
weights: An optional list of fractional numbers with the same length
as the input program_types that sum to 1. These will be used to weight
each of the ProgramType objects in the resulting average. If None, the
individual objects will be weighted equally. (Default: None).
timestep_resolution: An optional integer for the timestep resolution
at which the schedules will be averaged. Any schedule details
smaller than this timestep will be lost in the averaging process.
(Default: 1).
"""
# check the weights input
if weights is None:
weights = [1 / len(program_types)] * len(program_types) if \
len(program_types) > 0 else []
else:
weights = tuple_with_length(weights, len(program_types), float,
'average ProgramType weights')
assert abs(sum(weights) - 1.0) <= 1e-3, 'Average ProgramType weights ' \
'must be equal to 1. Got {}.'.format(sum(weights))
# gather all of the load objects across all of the programs
people_mtx = [[pr.people, w] for pr, w in zip(program_types, weights)
if pr.people is not None]
lighting_mtx = [[pr.lighting, w] for pr, w in zip(program_types, weights)
if pr.lighting is not None]
e_equip_mtx = [[p.electric_equipment, w] for p, w in zip(program_types, weights)
if p.electric_equipment is not None]
g_equip_mtx = [[pr.gas_equipment, w] for pr, w in zip(program_types, weights)
if pr.gas_equipment is not None]
shw_mtx = [[pr.service_hot_water, w] for pr, w in zip(program_types, weights)
if pr.service_hot_water is not None]
inf_mtx = [[pr.infiltration, w] for pr, w in zip(program_types, weights)
if pr.infiltration is not None]
vent_mtx = [[pr.ventilation, w] for pr, w in zip(program_types, weights)
if pr.ventilation is not None]
setp_mtx = [[pr.setpoint, w] for pr, w in zip(program_types, weights)
if pr.setpoint is not None]
# compute the average loads
people = None
if len(people_mtx) != 0:
t_people_mtx = tuple(zip(*people_mtx))
people = People.average('{}_People'.format(identifier), t_people_mtx[0],
t_people_mtx[1], timestep_resolution)
lighting = None
if len(lighting_mtx) != 0:
t_lighting_mtx = tuple(zip(*lighting_mtx))
lighting = Lighting.average(
'{}_Lighting'.format(identifier), t_lighting_mtx[0], t_lighting_mtx[1],
timestep_resolution)
electric_equipment = None
if len(e_equip_mtx) != 0:
t_e_equip_mtx = tuple(zip(*e_equip_mtx))
electric_equipment = ElectricEquipment.average(
'{}_Electric Equipment'.format(identifier), t_e_equip_mtx[0],
t_e_equip_mtx[1], timestep_resolution)
gas_equipment = None
if len(g_equip_mtx) != 0:
t_g_equip_mtx = tuple(zip(*g_equip_mtx))
gas_equipment = GasEquipment.average(
'{}_Gas Equipment'.format(identifier), t_g_equip_mtx[0],
t_g_equip_mtx[1], timestep_resolution)
shw = None
if len(shw_mtx) != 0:
t_shw_mtx = tuple(zip(*shw_mtx))
shw = ServiceHotWater.average(
'{}_Service Hot Water'.format(identifier), t_shw_mtx[0],
t_shw_mtx[1], timestep_resolution)
infiltration = None
if len(inf_mtx) != 0:
t_inf_mtx = tuple(zip(*inf_mtx))
infiltration = Infiltration.average(
'{}_Infiltration'.format(identifier), t_inf_mtx[0],
t_inf_mtx[1], timestep_resolution)
ventilation = None
if len(vent_mtx) != 0:
t_vent_mtx = tuple(zip(*vent_mtx))
ventilation = Ventilation.average(
'{}_Ventilation'.format(identifier), t_vent_mtx[0],
t_vent_mtx[1], timestep_resolution)
setpoint = None
if len(setp_mtx) != 0:
t_setp_mtx = tuple(zip(*setp_mtx))
setpoint = Setpoint.average('{}_Setpoint'.format(identifier), t_setp_mtx[0],
t_setp_mtx[1], timestep_resolution)
# return the averaged object
return ProgramType(
identifier, people, lighting, electric_equipment, gas_equipment, shw,
infiltration, ventilation, setpoint)
[docs]
def duplicate(self):
"""Get a copy of this object."""
return self.__copy__()
[docs]
def lock(self):
"""The lock() method to will also lock the loads."""
self._locked = True
if self.people is not None:
self.people.lock()
if self.lighting is not None:
self.lighting.lock()
if self.electric_equipment is not None:
self.electric_equipment.lock()
if self.gas_equipment is not None:
self.gas_equipment.lock()
if self.service_hot_water is not None:
self.service_hot_water.lock()
if self.infiltration is not None:
self.infiltration.lock()
if self.ventilation is not None:
self.ventilation.lock()
if self.setpoint is not None:
self.setpoint.lock()
[docs]
def unlock(self):
"""The unlock() method will also unlock the loads."""
self._locked = False
if self.people is not None:
self.people.unlock()
if self.lighting is not None:
self.lighting.unlock()
if self.electric_equipment is not None:
self.electric_equipment.unlock()
if self.gas_equipment is not None:
self.gas_equipment.unlock()
if self.service_hot_water is not None:
self.service_hot_water.unlock()
if self.infiltration is not None:
self.infiltration.unlock()
if self.ventilation is not None:
self.ventilation.unlock()
if self.setpoint is not None:
self.setpoint.unlock()
[docs]
def ToString(self):
"""Overwrite .NET ToString."""
return self.__repr__()
@staticmethod
def _get_loads_from_abridged(data, schedule_dict):
"""Get re-built load objects from abridged dictionaries."""
people = None
lighting = None
electric_equipment = None
gas_equipment = None
shw = None
infiltration = None
ventilation = None
setpoint = None
if 'people' in data and data['people'] is not None:
people = People.from_dict_abridged(data['people'], schedule_dict)
if 'lighting' in data and data['lighting'] is not None:
lighting = Lighting.from_dict_abridged(data['lighting'], schedule_dict)
if 'electric_equipment' in data and data['electric_equipment'] is not None:
electric_equipment = ElectricEquipment.from_dict_abridged(
data['electric_equipment'], schedule_dict)
if 'gas_equipment' in data and data['gas_equipment'] is not None:
gas_equipment = GasEquipment.from_dict_abridged(
data['gas_equipment'], schedule_dict)
if 'service_hot_water' in data and data['service_hot_water'] is not None:
shw = ServiceHotWater.from_dict_abridged(
data['service_hot_water'], schedule_dict)
if 'infiltration' in data and data['infiltration'] is not None:
infiltration = Infiltration.from_dict_abridged(
data['infiltration'], schedule_dict)
if 'ventilation' in data and data['ventilation'] is not None:
ventilation = Ventilation.from_dict_abridged(
data['ventilation'], schedule_dict)
if 'setpoint' in data and data['setpoint'] is not None:
setpoint = Setpoint.from_dict_abridged(data['setpoint'], schedule_dict)
return people, lighting, electric_equipment, gas_equipment, shw, \
infiltration, ventilation, setpoint
@staticmethod
def _instance_in_array(object_instance, object_array):
"""Check if a specific object instance is already in an array.
This can be much faster than `if object_instance in object_array`
when you expect to be testing a lot of the same instance of an object for
inclusion in an array since the builtin method uses an == operator to
test inclusion.
"""
for val in object_array:
if val is object_instance:
return True
return False
def __copy__(self):
people = self.people.duplicate() if self.people is not None else None
lighting = self.lighting.duplicate() if self.lighting is not None else None
electric_equipment = self.electric_equipment.duplicate() if \
self.electric_equipment is not None else None
gas_equipment = self.gas_equipment.duplicate() if \
self.gas_equipment is not None else None
shw = self.service_hot_water.duplicate() if \
self.service_hot_water is not None else None
infiltration = self.infiltration.duplicate() if \
self.infiltration is not None else None
ventilation = self.ventilation.duplicate() if \
self.ventilation is not None else None
setpoint = self.setpoint.duplicate() if self.setpoint is not None else None
new_obj = ProgramType(self.identifier, people, lighting, electric_equipment,
gas_equipment, shw, infiltration, ventilation, setpoint)
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, hash(self.people), hash(self.lighting),
hash(self.electric_equipment), hash(self.gas_equipment),
hash(self.service_hot_water), hash(self.infiltration),
hash(self.ventilation), hash(self.setpoint))
def __hash__(self):
return hash(self.__key())
def __eq__(self, other):
return isinstance(other, ProgramType) and self.__key() == other.__key()
def __ne__(self, other):
return not self.__eq__(other)
def __repr__(self):
return 'Program Type: {}'.format(self.display_name)