honeybee-energy 1.116.106__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. honeybee_energy/__init__.py +24 -0
  2. honeybee_energy/__main__.py +4 -0
  3. honeybee_energy/_extend_honeybee.py +145 -0
  4. honeybee_energy/altnumber.py +21 -0
  5. honeybee_energy/baseline/__init__.py +2 -0
  6. honeybee_energy/baseline/create.py +608 -0
  7. honeybee_energy/baseline/data/__init__.py +1 -0
  8. honeybee_energy/baseline/data/constructions.csv +64 -0
  9. honeybee_energy/baseline/data/fen_ratios.csv +15 -0
  10. honeybee_energy/baseline/data/lpd_building.csv +21 -0
  11. honeybee_energy/baseline/data/pci_2016.csv +22 -0
  12. honeybee_energy/baseline/data/pci_2019.csv +22 -0
  13. honeybee_energy/baseline/data/pci_2022.csv +22 -0
  14. honeybee_energy/baseline/data/shw.csv +21 -0
  15. honeybee_energy/baseline/pci.py +512 -0
  16. honeybee_energy/baseline/result.py +371 -0
  17. honeybee_energy/boundarycondition.py +128 -0
  18. honeybee_energy/cli/__init__.py +69 -0
  19. honeybee_energy/cli/baseline.py +475 -0
  20. honeybee_energy/cli/edit.py +327 -0
  21. honeybee_energy/cli/lib.py +1154 -0
  22. honeybee_energy/cli/result.py +810 -0
  23. honeybee_energy/cli/setconfig.py +124 -0
  24. honeybee_energy/cli/settings.py +569 -0
  25. honeybee_energy/cli/simulate.py +380 -0
  26. honeybee_energy/cli/translate.py +1714 -0
  27. honeybee_energy/cli/validate.py +224 -0
  28. honeybee_energy/config.json +11 -0
  29. honeybee_energy/config.py +842 -0
  30. honeybee_energy/construction/__init__.py +1 -0
  31. honeybee_energy/construction/_base.py +374 -0
  32. honeybee_energy/construction/air.py +325 -0
  33. honeybee_energy/construction/dictutil.py +89 -0
  34. honeybee_energy/construction/dynamic.py +607 -0
  35. honeybee_energy/construction/opaque.py +460 -0
  36. honeybee_energy/construction/shade.py +319 -0
  37. honeybee_energy/construction/window.py +1096 -0
  38. honeybee_energy/construction/windowshade.py +847 -0
  39. honeybee_energy/constructionset.py +1655 -0
  40. honeybee_energy/dictutil.py +56 -0
  41. honeybee_energy/generator/__init__.py +5 -0
  42. honeybee_energy/generator/loadcenter.py +204 -0
  43. honeybee_energy/generator/pv.py +535 -0
  44. honeybee_energy/hvac/__init__.py +21 -0
  45. honeybee_energy/hvac/_base.py +124 -0
  46. honeybee_energy/hvac/_template.py +270 -0
  47. honeybee_energy/hvac/allair/__init__.py +22 -0
  48. honeybee_energy/hvac/allair/_base.py +349 -0
  49. honeybee_energy/hvac/allair/furnace.py +168 -0
  50. honeybee_energy/hvac/allair/psz.py +131 -0
  51. honeybee_energy/hvac/allair/ptac.py +163 -0
  52. honeybee_energy/hvac/allair/pvav.py +109 -0
  53. honeybee_energy/hvac/allair/vav.py +128 -0
  54. honeybee_energy/hvac/detailed.py +337 -0
  55. honeybee_energy/hvac/doas/__init__.py +28 -0
  56. honeybee_energy/hvac/doas/_base.py +345 -0
  57. honeybee_energy/hvac/doas/fcu.py +127 -0
  58. honeybee_energy/hvac/doas/radiant.py +329 -0
  59. honeybee_energy/hvac/doas/vrf.py +81 -0
  60. honeybee_energy/hvac/doas/wshp.py +91 -0
  61. honeybee_energy/hvac/heatcool/__init__.py +23 -0
  62. honeybee_energy/hvac/heatcool/_base.py +177 -0
  63. honeybee_energy/hvac/heatcool/baseboard.py +61 -0
  64. honeybee_energy/hvac/heatcool/evapcool.py +72 -0
  65. honeybee_energy/hvac/heatcool/fcu.py +92 -0
  66. honeybee_energy/hvac/heatcool/gasunit.py +53 -0
  67. honeybee_energy/hvac/heatcool/radiant.py +269 -0
  68. honeybee_energy/hvac/heatcool/residential.py +77 -0
  69. honeybee_energy/hvac/heatcool/vrf.py +54 -0
  70. honeybee_energy/hvac/heatcool/windowac.py +70 -0
  71. honeybee_energy/hvac/heatcool/wshp.py +62 -0
  72. honeybee_energy/hvac/idealair.py +699 -0
  73. honeybee_energy/internalmass.py +310 -0
  74. honeybee_energy/lib/__init__.py +1 -0
  75. honeybee_energy/lib/_loadconstructions.py +194 -0
  76. honeybee_energy/lib/_loadconstructionsets.py +117 -0
  77. honeybee_energy/lib/_loadmaterials.py +83 -0
  78. honeybee_energy/lib/_loadprogramtypes.py +125 -0
  79. honeybee_energy/lib/_loadschedules.py +87 -0
  80. honeybee_energy/lib/_loadtypelimits.py +64 -0
  81. honeybee_energy/lib/constructions.py +207 -0
  82. honeybee_energy/lib/constructionsets.py +95 -0
  83. honeybee_energy/lib/materials.py +67 -0
  84. honeybee_energy/lib/programtypes.py +125 -0
  85. honeybee_energy/lib/schedules.py +61 -0
  86. honeybee_energy/lib/scheduletypelimits.py +31 -0
  87. honeybee_energy/load/__init__.py +1 -0
  88. honeybee_energy/load/_base.py +190 -0
  89. honeybee_energy/load/daylight.py +397 -0
  90. honeybee_energy/load/dictutil.py +47 -0
  91. honeybee_energy/load/equipment.py +771 -0
  92. honeybee_energy/load/hotwater.py +543 -0
  93. honeybee_energy/load/infiltration.py +460 -0
  94. honeybee_energy/load/lighting.py +480 -0
  95. honeybee_energy/load/people.py +497 -0
  96. honeybee_energy/load/process.py +472 -0
  97. honeybee_energy/load/setpoint.py +816 -0
  98. honeybee_energy/load/ventilation.py +550 -0
  99. honeybee_energy/material/__init__.py +1 -0
  100. honeybee_energy/material/_base.py +166 -0
  101. honeybee_energy/material/dictutil.py +59 -0
  102. honeybee_energy/material/frame.py +367 -0
  103. honeybee_energy/material/gas.py +1087 -0
  104. honeybee_energy/material/glazing.py +854 -0
  105. honeybee_energy/material/opaque.py +1351 -0
  106. honeybee_energy/material/shade.py +1360 -0
  107. honeybee_energy/measure.py +472 -0
  108. honeybee_energy/programtype.py +723 -0
  109. honeybee_energy/properties/__init__.py +1 -0
  110. honeybee_energy/properties/aperture.py +333 -0
  111. honeybee_energy/properties/door.py +342 -0
  112. honeybee_energy/properties/extension.py +244 -0
  113. honeybee_energy/properties/face.py +274 -0
  114. honeybee_energy/properties/model.py +2640 -0
  115. honeybee_energy/properties/room.py +1747 -0
  116. honeybee_energy/properties/shade.py +314 -0
  117. honeybee_energy/properties/shademesh.py +262 -0
  118. honeybee_energy/reader.py +48 -0
  119. honeybee_energy/result/__init__.py +1 -0
  120. honeybee_energy/result/colorobj.py +648 -0
  121. honeybee_energy/result/emissions.py +290 -0
  122. honeybee_energy/result/err.py +101 -0
  123. honeybee_energy/result/eui.py +100 -0
  124. honeybee_energy/result/generation.py +160 -0
  125. honeybee_energy/result/loadbalance.py +890 -0
  126. honeybee_energy/result/match.py +202 -0
  127. honeybee_energy/result/osw.py +90 -0
  128. honeybee_energy/result/rdd.py +59 -0
  129. honeybee_energy/result/zsz.py +190 -0
  130. honeybee_energy/run.py +1577 -0
  131. honeybee_energy/schedule/__init__.py +1 -0
  132. honeybee_energy/schedule/day.py +626 -0
  133. honeybee_energy/schedule/dictutil.py +59 -0
  134. honeybee_energy/schedule/fixedinterval.py +1012 -0
  135. honeybee_energy/schedule/rule.py +619 -0
  136. honeybee_energy/schedule/ruleset.py +1867 -0
  137. honeybee_energy/schedule/typelimit.py +310 -0
  138. honeybee_energy/shw.py +315 -0
  139. honeybee_energy/simulation/__init__.py +1 -0
  140. honeybee_energy/simulation/control.py +214 -0
  141. honeybee_energy/simulation/daylightsaving.py +185 -0
  142. honeybee_energy/simulation/dictutil.py +51 -0
  143. honeybee_energy/simulation/output.py +646 -0
  144. honeybee_energy/simulation/parameter.py +606 -0
  145. honeybee_energy/simulation/runperiod.py +443 -0
  146. honeybee_energy/simulation/shadowcalculation.py +295 -0
  147. honeybee_energy/simulation/sizing.py +546 -0
  148. honeybee_energy/ventcool/__init__.py +5 -0
  149. honeybee_energy/ventcool/_crack_data.py +91 -0
  150. honeybee_energy/ventcool/afn.py +289 -0
  151. honeybee_energy/ventcool/control.py +269 -0
  152. honeybee_energy/ventcool/crack.py +126 -0
  153. honeybee_energy/ventcool/fan.py +493 -0
  154. honeybee_energy/ventcool/opening.py +365 -0
  155. honeybee_energy/ventcool/simulation.py +314 -0
  156. honeybee_energy/writer.py +1078 -0
  157. honeybee_energy-1.116.106.dist-info/METADATA +113 -0
  158. honeybee_energy-1.116.106.dist-info/RECORD +162 -0
  159. honeybee_energy-1.116.106.dist-info/WHEEL +5 -0
  160. honeybee_energy-1.116.106.dist-info/entry_points.txt +2 -0
  161. honeybee_energy-1.116.106.dist-info/licenses/LICENSE +661 -0
  162. honeybee_energy-1.116.106.dist-info/top_level.txt +1 -0
@@ -0,0 +1,771 @@
1
+ # coding=utf-8
2
+ """Complete definition of equipment in a simulation, including schedule and load."""
3
+ from __future__ import division
4
+
5
+ from honeybee._lockable import lockable
6
+ from honeybee.typing import float_in_range, float_positive, clean_and_id_ep_string
7
+
8
+ from ._base import _LoadBase
9
+ from ..schedule.ruleset import ScheduleRuleset
10
+ from ..schedule.fixedinterval import ScheduleFixedInterval
11
+ from ..reader import parse_idf_string
12
+ from ..writer import generate_idf_string
13
+ from ..properties.extension import ElectricEquipmentProperties, GasEquipmentProperties
14
+
15
+
16
+ @lockable
17
+ class _EquipmentBase(_LoadBase):
18
+ """A complete definition of equipment, including schedules and load.
19
+
20
+ Args:
21
+ identifier: Text string for a unique Equipment ID. Must be < 100 characters
22
+ and not contain any EnergyPlus special characters. This will be used to
23
+ identify the object across a model and in the exported IDF.
24
+ watts_per_area: A numerical value for the equipment power density in
25
+ Watts per square meter of floor area.
26
+ schedule: A ScheduleRuleset or ScheduleFixedInterval for the use of equipment
27
+ over the course of the year. The type of this schedule should be
28
+ Fractional and the fractional values will get multiplied by the
29
+ watts_per_area to yield a complete equipment profile.
30
+ radiant_fraction: A number between 0 and 1 for the fraction of the total
31
+ equipment load given off as long wave radiant heat. (Default: 0).
32
+ latent_fraction: A number between 0 and 1 for the fraction of the total
33
+ equipment load that is latent (as opposed to sensible). (Default: 0).
34
+ lost_fraction: A number between 0 and 1 for the fraction of the total
35
+ equipment load that is lost outside of the zone and the HVAC system.
36
+ Typically, this is used to represent heat that is exhausted directly
37
+ out of a zone (as you would for a stove). (Default: 0).
38
+
39
+ Properties:
40
+ * identifier
41
+ * display_name
42
+ * watts_per_area
43
+ * schedule
44
+ * radiant_fraction
45
+ * latent_fraction
46
+ * lost_fraction
47
+ * convected_fraction
48
+ * user_data
49
+ """
50
+ __slots__ = ('_watts_per_area', '_schedule', '_radiant_fraction',
51
+ '_latent_fraction', '_lost_fraction')
52
+ _idf_comments = ('name', 'zone name', 'schedule name', 'equipment level method',
53
+ 'equipment power level {W}', 'equipment per floor area {W/m2}',
54
+ 'equipment per person {W/ppl}', 'latent fraction',
55
+ 'radiant fraction', 'lost fraction')
56
+
57
+ def __init__(self, identifier, watts_per_area, schedule, radiant_fraction=0,
58
+ latent_fraction=0, lost_fraction=0):
59
+ """Initialize Equipment."""
60
+ _LoadBase.__init__(self, identifier)
61
+ self._latent_fraction = 0 # starting value so that check runs correctly
62
+ self._lost_fraction = 0 # starting value so that check runs correctly
63
+
64
+ self.watts_per_area = watts_per_area
65
+ self.schedule = schedule
66
+ self.radiant_fraction = radiant_fraction
67
+ self.latent_fraction = latent_fraction
68
+ self.lost_fraction = lost_fraction
69
+
70
+ @property
71
+ def watts_per_area(self):
72
+ """Get or set the equipment power density in Watts/square meter of floor area."""
73
+ return self._watts_per_area
74
+
75
+ @watts_per_area.setter
76
+ def watts_per_area(self, value):
77
+ self._watts_per_area = float_positive(value, 'equipment watts per area')
78
+
79
+ @property
80
+ def schedule(self):
81
+ """Get or set a ScheduleRuleset or ScheduleFixedInterval for equipment usage."""
82
+ return self._schedule
83
+
84
+ @schedule.setter
85
+ def schedule(self, value):
86
+ assert isinstance(value, (ScheduleRuleset, ScheduleFixedInterval)), \
87
+ 'Expected ScheduleRuleset or ScheduleFixedInterval for equipment ' \
88
+ 'schedule. Got {}.'.format(type(value))
89
+ self._check_fractional_schedule_type(value, 'Equipment')
90
+ value.lock() # lock editing in case schedule has multiple references
91
+ self._schedule = value
92
+
93
+ @property
94
+ def radiant_fraction(self):
95
+ """Get or set the fraction of equipment heat given off as long wave radiation."""
96
+ return self._radiant_fraction
97
+
98
+ @radiant_fraction.setter
99
+ def radiant_fraction(self, value):
100
+ self._radiant_fraction = float_in_range(
101
+ value, 0.0, 1.0, 'equipment radiant fraction')
102
+ self._check_fractions()
103
+
104
+ @property
105
+ def latent_fraction(self):
106
+ """Get or set the fraction of equipment heat that is latent."""
107
+ return self._latent_fraction
108
+
109
+ @latent_fraction.setter
110
+ def latent_fraction(self, value):
111
+ self._latent_fraction = float_in_range(
112
+ value, 0.0, 1.0, 'equipment latent fraction')
113
+ self._check_fractions()
114
+
115
+ @property
116
+ def lost_fraction(self):
117
+ """Get or set the fraction of equipment heat that is lost out of the zone."""
118
+ return self._lost_fraction
119
+
120
+ @lost_fraction.setter
121
+ def lost_fraction(self, value):
122
+ self._lost_fraction = float_in_range(
123
+ value, 0.0, 1.0, 'equipment lost fraction')
124
+ self._check_fractions()
125
+
126
+ @property
127
+ def convected_fraction(self):
128
+ """Get the fraction of equipment heat that convects to the zone air."""
129
+ return 1 - sum((self._radiant_fraction, self._latent_fraction,
130
+ self._lost_fraction))
131
+
132
+ def diversify(self, count, watts_stdev=20, schedule_offset=1, timestep=1,
133
+ schedule_indices=None):
134
+ """Get an array of diversified Equipment derived from this "average" one.
135
+
136
+ Approximately 2/3 of the schedules in the output objects will be offset
137
+ from the mean by the input schedule_offset (1/3 ahead and another 1/3 behind).
138
+
139
+ Args:
140
+ count: An positive integer for the number of diversified objects to
141
+ generate from this mean object.
142
+ watts_stdev: A number between 0 and 100 for the percent of the watts_per_area
143
+ representing one standard deviation of diversification from
144
+ the mean. (Default 20 percent).
145
+ schedule_offset: A positive integer for the number of timesteps at which
146
+ the lighting schedule of the resulting objects will be shifted - roughly
147
+ 1/3 of the objects ahead and another 1/3 behind. (Default: 1).
148
+ timestep: An integer for the number of timesteps per hour at which the
149
+ shifting is occurring. This must be a value between 1 and 60, which
150
+ is evenly divisible by 60. 1 indicates that each step is an hour
151
+ while 60 indicates that each step is a minute. (Default: 1).
152
+ schedule_indices: An optional list of integers from 0 to 2 with a length
153
+ equal to the input count, which will be used to set whether a given
154
+ schedule is behind (0), ahead (2), or the same (1). This can be
155
+ used to coordinate schedules across diversified programs. If None
156
+ a random list of integers will be generated. (Default: None).
157
+ """
158
+ # generate shifted schedules and a gaussian distribution of watts_per_area
159
+ usage_schs = self._shift_schedule(self.schedule, schedule_offset, timestep)
160
+ stdev = self.watts_per_area * (watts_stdev / 100)
161
+ new_loads, sch_ints = self._gaussian_values(count, self.watts_per_area, stdev)
162
+ sch_ints = sch_ints if schedule_indices is None else schedule_indices
163
+
164
+ # generate the new objects and return them
165
+ new_objects = []
166
+ for load_val, sch_int in zip(new_loads, sch_ints):
167
+ new_obj = self.duplicate()
168
+ new_obj.identifier = clean_and_id_ep_string(self.identifier)
169
+ new_obj.watts_per_area = load_val
170
+ new_obj.schedule = usage_schs[sch_int]
171
+ new_objects.append(new_obj)
172
+ return new_objects
173
+
174
+ def _check_fractions(self):
175
+ tot = (self._radiant_fraction, self._latent_fraction, self._lost_fraction)
176
+ assert sum(tot) <= 1 + 1e-9, 'Sum of equipment radiant_fraction, ' \
177
+ 'latent_fraction and lost_fraction ({}) is greater than 1.'.format(sum(tot))
178
+
179
+ def _get_idf_values(self, zone_identifier):
180
+ """Get the properties of this object ordered as they are in an IDF."""
181
+ return ('{}..{}'.format(self.identifier, zone_identifier), zone_identifier,
182
+ self.schedule.identifier, 'Watts/Area', '', self.watts_per_area, '',
183
+ self.latent_fraction, self.radiant_fraction, self.lost_fraction)
184
+
185
+ def _add_dict_keys(self, base, abridged):
186
+ """Add keys to a base dictionary."""
187
+ base['identifier'] = self.identifier
188
+ base['watts_per_area'] = self.watts_per_area
189
+ base['radiant_fraction'] = self.radiant_fraction
190
+ base['latent_fraction'] = self.latent_fraction
191
+ base['lost_fraction'] = self.lost_fraction
192
+ base['schedule'] = self.schedule.to_dict() if not \
193
+ abridged else self.schedule.identifier
194
+ if self._display_name is not None:
195
+ base['display_name'] = self.display_name
196
+ if self._user_data is not None:
197
+ base['user_data'] = self.user_data
198
+ prop_dict = self.properties.to_dict()
199
+ if prop_dict is not None:
200
+ base['properties'] = prop_dict
201
+ return base
202
+
203
+ @staticmethod
204
+ def _extract_ep_properties(ep_strs, schedule_dict):
205
+ """Extract relevant EnergyPlus properties from a list of strings."""
206
+ # check the inputs
207
+ if len(ep_strs) > 3:
208
+ assert ep_strs[3].lower() == 'watts/area', 'Equipment must use ' \
209
+ 'Watts/Area method to be loaded from IDF to honeybee.'
210
+ else:
211
+ raise AssertionError('Equipment must use Watts/Area method to be loaded '
212
+ 'from IDF to honeybee.')
213
+ # extract the properties from the string
214
+ rad_fract = 0
215
+ lat_fract = 0
216
+ lost_fract = 0
217
+ try:
218
+ lat_fract = ep_strs[7] if ep_strs[7] != '' else 0
219
+ rad_fract = ep_strs[8] if ep_strs[8] != '' else 0
220
+ lost_fract = ep_strs[9] if ep_strs[9] != '' else 0
221
+ except IndexError:
222
+ pass # shorter equipment definition lacking fractions
223
+ # extract the schedules from the string
224
+ try:
225
+ sched = schedule_dict[ep_strs[2]]
226
+ except KeyError as e:
227
+ raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
228
+ return sched, rad_fract, lat_fract, lost_fract
229
+
230
+ @staticmethod
231
+ def _extract_dict_props(data, expected_type):
232
+ """Extract relevant properties from an equipment dictionary."""
233
+ assert data['type'] == expected_type, \
234
+ 'Expected {} dictionary. Got {}.'.format(expected_type, data['type'])
235
+ sched = _EquipmentBase._get_schedule_from_dict(data['schedule'])
236
+ rad_fract, lat_fract, lost_fract = _EquipmentBase._optional_dict_keys(data)
237
+ return sched, rad_fract, lat_fract, lost_fract
238
+
239
+ @staticmethod
240
+ def _extract_abridged_dict_props(data, expected_type, schedule_dict):
241
+ """Extract relevant properties from an equipment dictionary."""
242
+ assert data['type'] == expected_type, \
243
+ 'Expected {} dictionary. Got {}.'.format(expected_type, data['type'])
244
+ try:
245
+ sched = schedule_dict[data['schedule']]
246
+ except KeyError as e:
247
+ raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
248
+ rad_fract, lat_fract, lost_fract = _EquipmentBase._optional_dict_keys(data)
249
+ return sched, rad_fract, lat_fract, lost_fract
250
+
251
+ @staticmethod
252
+ def _optional_dict_keys(data):
253
+ """Get the optional keys from an Equipment dictionary."""
254
+ rad_fract = data['radiant_fraction'] if 'radiant_fraction' in data else 0
255
+ lat_fract = data['latent_fraction'] if 'latent_fraction' in data else 0
256
+ lost_fract = data['lost_fraction'] if 'lost_fraction' in data else 0
257
+ return rad_fract, lat_fract, lost_fract
258
+
259
+ @staticmethod
260
+ def _average_properties(identifier, equipments, weights, timestep_resolution):
261
+ """Get average properties across several equipment objects."""
262
+ weights, u_weights = \
263
+ _EquipmentBase._check_avg_weights(equipments, weights, 'Equipment')
264
+
265
+ # calculate the average values
266
+ pd = sum([eq.watts_per_area * w for eq, w in zip(equipments, weights)])
267
+ rad_fract = sum([eq.radiant_fraction * w for eq, w in zip(equipments, u_weights)])
268
+ lat_fract = sum([eq.latent_fraction * w for eq, w in zip(equipments, u_weights)])
269
+ lost_fract = sum([eq.lost_fraction * w for eq, w in zip(equipments, u_weights)])
270
+
271
+ # calculate the average schedules
272
+ sched = _EquipmentBase._average_schedule(
273
+ '{} Schedule'.format(identifier), [eq.schedule for eq in equipments],
274
+ u_weights, timestep_resolution)
275
+
276
+ return pd, sched, rad_fract, lat_fract, lost_fract
277
+
278
+ def __key(self):
279
+ """A tuple based on the object properties, useful for hashing."""
280
+ return (self.identifier, self.watts_per_area, hash(self.schedule),
281
+ self.radiant_fraction, self.latent_fraction, self.lost_fraction)
282
+
283
+ def __hash__(self):
284
+ return hash(self.__key())
285
+
286
+ def __eq__(self, other):
287
+ return isinstance(other, _EquipmentBase) and self.__key() == other.__key()
288
+
289
+ def __ne__(self, other):
290
+ return not self.__eq__(other)
291
+
292
+ def __copy__(self):
293
+ new_obj = _EquipmentBase(
294
+ self.identifier, self.watts_per_area, self.schedule,
295
+ self.radiant_fraction, self.latent_fraction, self.lost_fraction)
296
+ new_obj._display_name = self._display_name
297
+ new_obj._user_data = None if self._user_data is None else self._user_data.copy()
298
+ return new_obj
299
+
300
+ def __repr__(self):
301
+ return '{}: {} [{} W/m2] [schedule: {}]'.format(
302
+ self.__class__.__name__, self.display_name, round(self.watts_per_area, 1),
303
+ self.schedule.display_name)
304
+
305
+
306
+ @lockable
307
+ class ElectricEquipment(_EquipmentBase):
308
+ """A complete definition of electric equipment, including schedules and load.
309
+
310
+ Args:
311
+ identifier: Text string for a unique ElectricEquipment ID. Must be < 100
312
+ characters and not contain any EnergyPlus special characters. This will
313
+ be used to identify the object across a model and in the exported IDF.
314
+ watts_per_area: A numerical value for the equipment power density in
315
+ Watts per square meter of floor area.
316
+ schedule: A ScheduleRuleset or ScheduleFixedInterval for the use of equipment
317
+ over the course of the year. The type of this schedule should be
318
+ Fractional and the fractional values will get multiplied by the
319
+ watts_per_area to yield a complete equipment profile.
320
+ radiant_fraction: A number between 0 and 1 for the fraction of the total
321
+ equipment load given off as long wave radiant heat. (Default: 0).
322
+ latent_fraction: A number between 0 and 1 for the fraction of the total
323
+ equipment load that is latent (as opposed to sensible). (Default: 0).
324
+ lost_fraction: A number between 0 and 1 for the fraction of the total
325
+ equipment load that is lost outside of the zone and the HVAC system.
326
+ Typically, this is used to represent heat that is exhausted directly
327
+ out of a zone (as you would for a stove). (Default: 0).
328
+
329
+ Properties:
330
+ * identifier
331
+ * display_name
332
+ * watts_per_area
333
+ * schedule
334
+ * radiant_fraction
335
+ * latent_fraction
336
+ * lost_fraction
337
+ * convected_fraction
338
+ * user_data
339
+ """
340
+ __slots__ = ()
341
+
342
+ def __init__(self, identifier, watts_per_area, schedule, radiant_fraction=0,
343
+ latent_fraction=0, lost_fraction=0):
344
+ """Initialize Electric Equipment."""
345
+ _EquipmentBase.__init__(self, identifier, watts_per_area, schedule,
346
+ radiant_fraction, latent_fraction, lost_fraction)
347
+ self._properties = ElectricEquipmentProperties(self)
348
+
349
+ @classmethod
350
+ def from_idf(cls, idf_string, schedule_dict):
351
+ """Create an ElectricEquipment object from an EnergyPlus IDF text string.
352
+
353
+ Note that the ElectricEquipment idf_string must use the 'watts per zone floor
354
+ area' method in order to be successfully imported.
355
+
356
+ Args:
357
+ idf_string: A text string fully describing an EnergyPlus
358
+ ElectricEquipment definition.
359
+ schedule_dict: A dictionary with schedule identifiers as keys and honeybee
360
+ schedule objects as values (either ScheduleRuleset or
361
+ ScheduleFixedInterval). These will be used to assign the schedules to
362
+ the ElectricEquipment object.
363
+
364
+ Returns:
365
+ A tuple with two elements
366
+
367
+ - equipment: An ElectricEquipment object loaded from the idf_string.
368
+
369
+ - zone_identifier: The identifier of the zone to which the
370
+ ElectricEquipment object should be assigned.
371
+ """
372
+ # check the inputs
373
+ ep_strs = parse_idf_string(idf_string, 'ElectricEquipment,')
374
+ # get the relevant properties
375
+ sched, rad_f, lat_f, lost_f = cls._extract_ep_properties(ep_strs, schedule_dict)
376
+ # return the equipment object and the zone identifier for the equip object
377
+ obj_id = ep_strs[0].split('..')[0]
378
+ zone_id = ep_strs[1]
379
+ equipment = cls(obj_id, ep_strs[5], sched, rad_f, lat_f, lost_f)
380
+ return equipment, zone_id
381
+
382
+ @classmethod
383
+ def from_dict(cls, data):
384
+ """Create a ElectricEquipment object from a dictionary.
385
+
386
+ Note that the dictionary must be a non-abridged version for this classmethod
387
+ to work.
388
+
389
+ Args:
390
+ data: A ElectricEquipment dictionary in following the format below.
391
+
392
+ .. code-block:: python
393
+
394
+ {
395
+ "type": 'ElectricEquipment',
396
+ "identifier": 'Open_Office_Equipment_50_03',
397
+ "display_name": 'Office Equipment',
398
+ "watts_per_area": 5, # equipment watts per square meter of floor area
399
+ "schedule": {}, # ScheduleRuleset/ScheduleFixedInterval dictionary
400
+ "radiant_fraction": 0.3, # fraction of heat that is long wave radiant
401
+ "latent_fraction": 0, # fraction of heat that is latent
402
+ "lost_fraction": 0 # fraction of heat that is lost
403
+ }
404
+ """
405
+ sched, rad_f, lat_f, lost_f = cls._extract_dict_props(data, 'ElectricEquipment')
406
+ new_obj = cls(data['identifier'], data['watts_per_area'],
407
+ sched, rad_f, lat_f, lost_f)
408
+ if 'display_name' in data and data['display_name'] is not None:
409
+ new_obj.display_name = data['display_name']
410
+ if 'user_data' in data and data['user_data'] is not None:
411
+ new_obj.user_data = data['user_data']
412
+ if 'properties' in data and data['properties'] is not None:
413
+ new_obj.properties._load_extension_attr_from_dict(data['properties'])
414
+ return new_obj
415
+
416
+ @classmethod
417
+ def from_dict_abridged(cls, data, schedule_dict):
418
+ """Create a ElectricEquipment object from an abridged dictionary.
419
+
420
+ Args:
421
+ data: A ElectricEquipmentAbridged dictionary in following the format below.
422
+ schedule_dict: A dictionary with schedule identifiers as keys and honeybee
423
+ schedule objects as values (either ScheduleRuleset or
424
+ ScheduleFixedInterval). These will be used to assign the schedules
425
+ to the equipment object.
426
+
427
+ .. code-block:: python
428
+
429
+ {
430
+ "type": 'ElectricEquipmentAbridged',
431
+ "identifier": 'Open_Office_Equipment_50_03',
432
+ "display_name": 'Office Equipment',
433
+ "watts_per_area": 5, # equipment watts per square meter of floor area
434
+ "schedule": "Office Equipment Schedule", # Schedule identifier
435
+ "radiant_fraction": 0.3, # fraction of heat that is long wave radiant
436
+ "latent_fraction": 0, # fraction of heat that is latent
437
+ "lost_fraction": 0 # fraction of heat that is lost
438
+ }
439
+ """
440
+ sched, rad_f, lat_f, lost_f = cls._extract_abridged_dict_props(
441
+ data, 'ElectricEquipmentAbridged', schedule_dict)
442
+ new_obj = cls(data['identifier'], data['watts_per_area'],
443
+ sched, rad_f, lat_f, lost_f)
444
+ if 'display_name' in data and data['display_name'] is not None:
445
+ new_obj.display_name = data['display_name']
446
+ if 'user_data' in data and data['user_data'] is not None:
447
+ new_obj.user_data = data['user_data']
448
+ if 'properties' in data and data['properties'] is not None:
449
+ new_obj.properties._load_extension_attr_from_dict(data['properties'])
450
+ return new_obj
451
+
452
+ def to_idf(self, zone_identifier):
453
+ """IDF string representation of ElectricEquipment object.
454
+
455
+ Note that this method only outputs a single string for the ElectricEquipment
456
+ object and, to write everything needed to describe the object into an IDF,
457
+ this object's schedule must also be written.
458
+
459
+ Args:
460
+ zone_identifier: Text for the zone identifier that the ElectricEquipment
461
+ object is assigned to.
462
+
463
+ .. code-block:: shell
464
+
465
+ ElectricEquipment,
466
+ test_equipment, !- Name
467
+ test_zone, !- Zone or ZoneList Name
468
+ test_equip_schedule, !- Schedule Name
469
+ EquipmentLevel, !- Design Level Calculation Method
470
+ , !- Design Level {W}
471
+ 5, !- Watts per Zone Floor Area {W/m2}
472
+ , !- Watts per Person {W/person}
473
+ 0, !- Fraction Latent
474
+ 0.3, !- Fraction Radiant
475
+ 0; !- Fraction Lost
476
+ """
477
+ return generate_idf_string(
478
+ 'ElectricEquipment', self._get_idf_values(zone_identifier),
479
+ self._idf_comments)
480
+
481
+ def to_dict(self, abridged=False):
482
+ """ElectricEquipment dictionary representation.
483
+
484
+ Args:
485
+ abridged: Boolean to note whether the full dictionary describing the
486
+ object should be returned (False) or just an abridged version (True),
487
+ which only specifies the identifiers of schedules. Default: False.
488
+ """
489
+ base = {'type': 'ElectricEquipment'} if not abridged else \
490
+ {'type': 'ElectricEquipmentAbridged'}
491
+ return self._add_dict_keys(base, abridged)
492
+
493
+ @staticmethod
494
+ def average(identifier, equipments, weights=None, timestep_resolution=1):
495
+ """Get an ElectricEquipment object that's an average between other objects.
496
+
497
+ Args:
498
+ identifier: Text string for a unique ID for the new averaged ElectricEquipment.
499
+ Must be < 100 characters and not contain any EnergyPlus special
500
+ characters. This will be used to identify the object across a model
501
+ and in the exported IDF.
502
+ equipments: A list of ElectricEquipment objects that will be averaged
503
+ together to make a new ElectricEquipment.
504
+ weights: An optional list of fractional numbers with the same length
505
+ as the input equipments. These will be used to weight each of the
506
+ equipment objects in the resulting average. Note that these weights
507
+ can sum to less than 1 in which case the average watts_per_area will
508
+ assume 0 for the unaccounted fraction of the weights.
509
+ If None, the objects will be weighted equally. Default: None.
510
+ timestep_resolution: An optional integer for the timestep resolution
511
+ at which the schedules will be averaged. Any schedule details
512
+ smaller than this timestep will be lost in the averaging process.
513
+ Default: 1.
514
+ """
515
+ pd, sched, rad_f, lat_f, lost_f = ElectricEquipment._average_properties(
516
+ identifier, equipments, weights, timestep_resolution)
517
+ return ElectricEquipment(identifier, pd, sched, rad_f, lat_f, lost_f)
518
+
519
+ def __key(self):
520
+ """A tuple based on the object properties, useful for hashing."""
521
+ return (self.identifier, self.watts_per_area, hash(self.schedule),
522
+ self.radiant_fraction, self.latent_fraction, self.lost_fraction)
523
+
524
+ def __hash__(self):
525
+ return hash(self.__key())
526
+
527
+ def __eq__(self, other):
528
+ return isinstance(other, ElectricEquipment) and self.__key() == other.__key()
529
+
530
+ def __copy__(self):
531
+ new_obj = ElectricEquipment(
532
+ self.identifier, self.watts_per_area, self.schedule,
533
+ self.radiant_fraction, self.latent_fraction, self.lost_fraction)
534
+ new_obj._display_name = self._display_name
535
+ new_obj._user_data = None if self._user_data is None else self._user_data.copy()
536
+ new_obj._properties._duplicate_extension_attr(self._properties)
537
+ return new_obj
538
+
539
+
540
+ @lockable
541
+ class GasEquipment(_EquipmentBase):
542
+ """A complete definition of gas equipment, including schedules and load.
543
+
544
+ Args:
545
+ identifier: Text string for a unique GasEquipment ID. Must be < 100
546
+ characters and not contain any EnergyPlus special characters. This will
547
+ be used to identify the object across a model and in the exported IDF.
548
+ watts_per_area: A numerical value for the equipment power density in
549
+ Watts per square meter of floor area.
550
+ schedule: A ScheduleRuleset or ScheduleFixedInterval for the use of equipment
551
+ over the course of the year. The type of this schedule should be
552
+ Fractional and the fractional values will get multiplied by the
553
+ watts_per_area to yield a complete equipment profile.
554
+ radiant_fraction: A number between 0 and 1 for the fraction of the total
555
+ equipment load given off as long wave radiant heat. (Default: 0).
556
+ latent_fraction: A number between 0 and 1 for the fraction of the total
557
+ equipment load that is latent (as opposed to sensible). (Default: 0).
558
+ lost_fraction: A number between 0 and 1 for the fraction of the total
559
+ equipment load that is lost outside of the zone and the HVAC system.
560
+ Typically, this is used to represent heat that is exhausted directly
561
+ out of a zone (as you would for a stove). (Default: 0).
562
+
563
+ Properties:
564
+ * identifier
565
+ * display_name
566
+ * watts_per_area
567
+ * schedule
568
+ * radiant_fraction
569
+ * latent_fraction
570
+ * lost_fraction
571
+ * convected_fraction
572
+ * user_data
573
+ """
574
+ __slots__ = ()
575
+
576
+ def __init__(self, identifier, watts_per_area, schedule, radiant_fraction=0,
577
+ latent_fraction=0, lost_fraction=0):
578
+ """Initialize Gas Equipment."""
579
+ _EquipmentBase.__init__(self, identifier, watts_per_area, schedule,
580
+ radiant_fraction, latent_fraction, lost_fraction)
581
+ self._properties = GasEquipmentProperties(self)
582
+
583
+ @classmethod
584
+ def from_idf(cls, idf_string, schedule_dict):
585
+ """Create an GasEquipment object from an EnergyPlus IDF text string.
586
+
587
+ Note that the GasEquipment idf_string must use the 'watts per zone floor
588
+ area' method in order to be successfully imported.
589
+
590
+ Args:
591
+ idf_string: A text string fully describing an EnergyPlus
592
+ GasEquipment definition.
593
+ schedule_dict: A dictionary with schedule identifiers as keys and honeybee
594
+ schedule objects as values (either ScheduleRuleset or
595
+ ScheduleFixedInterval). These will be used to assign the schedules to
596
+ the GasEquipment object.
597
+
598
+ Returns:
599
+ A tuple with two elements
600
+
601
+ - equipment: An GasEquipment object loaded from the idf_string.
602
+
603
+ - zone_identifier: The identifier of the zone to which the GasEquipment
604
+ object should be assigned.
605
+ """
606
+ # check the inputs
607
+ ep_strs = parse_idf_string(idf_string, 'GasEquipment,')
608
+ # get the relevant properties
609
+ sched, rad_f, lat_f, lost_f = cls._extract_ep_properties(ep_strs, schedule_dict)
610
+ # return the equipment object and the zone identifier for the equip object
611
+ obj_id = ep_strs[0].split('..')[0]
612
+ zone_id = ep_strs[1]
613
+ equipment = cls(obj_id, ep_strs[5], sched, rad_f, lat_f, lost_f)
614
+ return equipment, zone_id
615
+
616
+ @classmethod
617
+ def from_dict(cls, data):
618
+ """Create a GasEquipment object from a dictionary.
619
+
620
+ Note that the dictionary must be a non-abridged version for this classmethod
621
+ to work.
622
+
623
+ Args:
624
+ data: A GasEquipment dictionary in following the format below.
625
+
626
+ .. code-block:: python
627
+
628
+ {
629
+ "type": 'GasEquipment',
630
+ "identifier": 'Kitchen_Equipment_200_03_02_0',
631
+ "display_name": 'Kitchen Equipment',
632
+ "watts_per_area": 20, # equipment watts per square meter of floor area
633
+ "schedule": {}, # ScheduleRuleset/ScheduleFixedInterval dictionary
634
+ "radiant_fraction": 0.3, # fraction of heat that is long wave radiant
635
+ "latent_fraction": 0.2, # fraction of heat that is latent
636
+ "lost_fraction": 0 # fraction of heat that is lost
637
+ }
638
+ """
639
+ sched, rad_f, lat_f, lost_f = cls._extract_dict_props(data, 'GasEquipment')
640
+ new_obj = cls(data['identifier'], data['watts_per_area'],
641
+ sched, rad_f, lat_f, lost_f)
642
+ if 'display_name' in data and data['display_name'] is not None:
643
+ new_obj.display_name = data['display_name']
644
+ if 'user_data' in data and data['user_data'] is not None:
645
+ new_obj.user_data = data['user_data']
646
+ if 'properties' in data and data['properties'] is not None:
647
+ new_obj.properties._load_extension_attr_from_dict(data['properties'])
648
+ return new_obj
649
+
650
+ @classmethod
651
+ def from_dict_abridged(cls, data, schedule_dict):
652
+ """Create a GasEquipment object from an abridged dictionary.
653
+
654
+ Args:
655
+ data: A GasEquipmentAbridged dictionary in following the format below.
656
+ schedule_dict: A dictionary with schedule identifiers as keys and honeybee
657
+ schedule objects as values (either ScheduleRuleset or
658
+ ScheduleFixedInterval). These will be used to assign the schedules to
659
+ the equipment object.
660
+
661
+ .. code-block:: python
662
+
663
+ {
664
+ "type": 'GasEquipmentAbridged',
665
+ "identifier": 'Kitchen_Equipment_200_03_02_0',
666
+ "display_name": 'Kitchen Equipment',
667
+ "watts_per_area": 20, # equipment watts per square meter of floor area
668
+ "schedule": "Kitchen Equipment Schedule", # Schedule identifier
669
+ "radiant_fraction": 0.3, # fraction of heat that is long wave radiant
670
+ "latent_fraction": 0, fraction of heat that is latent
671
+ "lost_fraction": 0 fraction of heat that is lost
672
+ }
673
+ """
674
+ sched, rad_f, lat_f, lost_f = cls._extract_abridged_dict_props(
675
+ data, 'GasEquipmentAbridged', schedule_dict)
676
+ new_obj = cls(data['identifier'], data['watts_per_area'],
677
+ sched, rad_f, lat_f, lost_f)
678
+ if 'display_name' in data and data['display_name'] is not None:
679
+ new_obj.display_name = data['display_name']
680
+ if 'user_data' in data and data['user_data'] is not None:
681
+ new_obj.user_data = data['user_data']
682
+ if 'properties' in data and data['properties'] is not None:
683
+ new_obj.properties._load_extension_attr_from_dict(data['properties'])
684
+ return new_obj
685
+
686
+ def to_idf(self, zone_identifier):
687
+ """IDF string representation of GasEquipment object.
688
+
689
+ Note that this method only outputs a single string for the GasEquipment
690
+ object and, to write everything needed to describe the object into an IDF,
691
+ this object's schedule must also be written.
692
+
693
+ Args:
694
+ zone_identifier: Text for the zone identifier that the GasEquipment object
695
+ is assigned to
696
+
697
+ .. code-block:: shell
698
+
699
+ GasEquipment,
700
+ DORM ROOMS AND COMMON AREAS GasEq 1, !- Name
701
+ DORM ROOMS AND COMMON AREAS, !- Zone or ZoneList or Space or SpaceList Name
702
+ Gas Eq Sch, !-Schedule Name
703
+ EquipmentLevel, !- Design Level Calculation Method
704
+ 29287.51, !- Design Level {W}
705
+ , !- Power per Zone Floor Area {W/m2}
706
+ , !- Power per Person {W/Person}
707
+ 0, !- Fraction Latent
708
+ 0.3, !- Fraction Radiant
709
+ 0, !- Fraction Lost
710
+ 0; !- Carbon Dioxide Generation Rate {m3/s-W}
711
+ """
712
+ return generate_idf_string('GasEquipment', self._get_idf_values(zone_identifier),
713
+ self._idf_comments)
714
+
715
+ def to_dict(self, abridged=False):
716
+ """GasEquipment dictionary representation.
717
+
718
+ Args:
719
+ abridged: Boolean to note whether the full dictionary describing the
720
+ object should be returned (False) or just an abridged version (True),
721
+ which only specifies the identifiers of schedules. Default: False.
722
+ """
723
+ base = {'type': 'GasEquipment'} if not abridged else \
724
+ {'type': 'GasEquipmentAbridged'}
725
+ return self._add_dict_keys(base, abridged)
726
+
727
+ @staticmethod
728
+ def average(identifier, equipments, weights=None, timestep_resolution=1):
729
+ """Get a GasEquipment object that's an average between other objects.
730
+
731
+ Args:
732
+ identifier: Text string for a unique ID for the new averaged GasEquipment.
733
+ Must be < 100 characters and not contain any EnergyPlus special
734
+ characters. This will be used to identify the object across a model
735
+ and in the exported IDF.
736
+ equipments: A list of GasEquipment objects that will be averaged
737
+ together to make a new GasEquipment.
738
+ weights: An optional list of fractional numbers with the same length
739
+ as the input equipments. These will be used to weight each of the
740
+ equipment objects in the resulting average. Note that these weights
741
+ can sum to less than 1 in which case the average watts_per_area will
742
+ assume 0 for the unaccounted fraction of the weights.
743
+ If None, the objects will be weighted equally. Default: None.
744
+ timestep_resolution: An optional integer for the timestep resolution
745
+ at which the schedules will be averaged. Any schedule details
746
+ smaller than this timestep will be lost in the averaging process.
747
+ Default: 1.
748
+ """
749
+ pd, sched, rad_f, lat_f, lost_f = GasEquipment._average_properties(
750
+ identifier, equipments, weights, timestep_resolution)
751
+ return GasEquipment(identifier, pd, sched, rad_f, lat_f, lost_f)
752
+
753
+ def __key(self):
754
+ """A tuple based on the object properties, useful for hashing."""
755
+ return (self.identifier, self.watts_per_area, hash(self.schedule),
756
+ self.radiant_fraction, self.latent_fraction, self.lost_fraction)
757
+
758
+ def __hash__(self):
759
+ return hash(self.__key())
760
+
761
+ def __eq__(self, other):
762
+ return isinstance(other, GasEquipment) and self.__key() == other.__key()
763
+
764
+ def __copy__(self):
765
+ new_obj = GasEquipment(
766
+ self.identifier, self.watts_per_area, self.schedule,
767
+ self.radiant_fraction, self.latent_fraction, self.lost_fraction)
768
+ new_obj._display_name = self._display_name
769
+ new_obj._user_data = None if self._user_data is None else self._user_data.copy()
770
+ new_obj._properties._duplicate_extension_attr(self._properties)
771
+ return new_obj