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,607 @@
1
+ # coding=utf-8
2
+ """Window Construction with any number of dynamic states."""
3
+ from __future__ import division
4
+
5
+ import re
6
+
7
+ from honeybee._lockable import lockable
8
+ from honeybee.typing import valid_ep_string
9
+
10
+ from .window import WindowConstruction
11
+ from ..material.glazing import EnergyWindowMaterialSimpleGlazSys
12
+ from ..schedule.dictutil import dict_to_schedule
13
+ from ..schedule.ruleset import ScheduleRuleset
14
+ from ..schedule.fixedinterval import ScheduleFixedInterval
15
+ from ..writer import generate_idf_string
16
+
17
+
18
+ @lockable
19
+ class WindowConstructionDynamic(object):
20
+ """Window Construction with any number of dynamic states.
21
+
22
+ Args:
23
+ identifier: Text string for a unique Construction ID. Must be < 100 characters
24
+ and not contain any EnergyPlus special characters. This will be used to
25
+ identify the object across a model and in the exported IDF.
26
+ constructions: A list of WindowConstruction objects that define the various
27
+ states that the dynamic window can assume.
28
+ schedule: A ScheduleRuleset or ScheduleFixedInterval composed of integers
29
+ that correspond to the indices of the constructions that are active
30
+ at given times throughout the simulation.
31
+
32
+ Properties:
33
+ * identifier
34
+ * display_name
35
+ * constructions
36
+ * schedule
37
+ * materials
38
+ * layers
39
+ * unique_materials
40
+ * frame
41
+ * r_value
42
+ * u_value
43
+ * u_factor
44
+ * r_factor
45
+ * is_symmetric
46
+ * has_frame
47
+ * has_shade
48
+ * is_dynamic
49
+ * inside_emissivity
50
+ * outside_emissivity
51
+ * solar_transmittance
52
+ * visible_transmittance
53
+ * shgc
54
+ * thickness
55
+ * glazing_count
56
+ * gap_count
57
+ * user_data
58
+ """
59
+
60
+ __slots__ = ('_identifier', '_display_name', '_constructions', '_schedule',
61
+ '_locked', '_user_data')
62
+
63
+ def __init__(self, identifier, constructions, schedule):
64
+ """Initialize dynamic window construction."""
65
+ self._locked = False # unlocked by default
66
+ self.identifier = identifier
67
+ self._display_name = None
68
+ self.constructions = constructions
69
+ self.schedule = schedule
70
+ self._user_data = None
71
+
72
+ @property
73
+ def identifier(self):
74
+ """Get or set the text string for construction identifier."""
75
+ return self._identifier
76
+
77
+ @identifier.setter
78
+ def identifier(self, identifier):
79
+ self._identifier = valid_ep_string(identifier, 'construction identifier')
80
+
81
+ @property
82
+ def display_name(self):
83
+ """Get or set a string for the object name without any character restrictions.
84
+
85
+ If not set, this will be equal to the identifier.
86
+ """
87
+ if self._display_name is None:
88
+ return self._identifier
89
+ return self._display_name
90
+
91
+ @display_name.setter
92
+ def display_name(self, value):
93
+ if value is not None:
94
+ try:
95
+ value = str(value)
96
+ except UnicodeEncodeError: # Python 2 machine lacking the character set
97
+ pass # keep it as unicode
98
+ self._display_name = value
99
+
100
+ @property
101
+ def constructions(self):
102
+ """Get or set a list of WindowConstructions that define the dynamic states."""
103
+ return self._constructions
104
+
105
+ @constructions.setter
106
+ def constructions(self, cons):
107
+ try:
108
+ if not isinstance(cons, tuple):
109
+ cons = tuple(cons)
110
+ except TypeError:
111
+ raise TypeError('Expected list or tuple for WindowConstructionDynamic '
112
+ 'constructions. Got {}'.format(type(cons)))
113
+ for construct in cons:
114
+ assert isinstance(construct, WindowConstruction), \
115
+ 'Expected WindowConstruction for WindowConstructionDynamic. ' \
116
+ 'Got {}.'.format(type(construct))
117
+ assert len(cons) > 1, 'There must be at least two constructions ' \
118
+ 'for a WindowConstructionDynamic.'
119
+ self._constructions = cons
120
+
121
+ @property
122
+ def schedule(self):
123
+ """Get or set a control schedule for the constructions active at given time.
124
+
125
+ The values of the schedule should be integers and range from 0 to one
126
+ less then the number of constructions. Zero indicates that the first
127
+ construction is active, one indicates that the second on is active, etc.
128
+ The schedule type limits of this schedule should be "Discrete" and the unit
129
+ type should be "Mode," "Control," or some other fractional unit.
130
+ """
131
+ return self._schedule
132
+
133
+ @schedule.setter
134
+ def schedule(self, value):
135
+ assert isinstance(value, (ScheduleRuleset, ScheduleFixedInterval)), \
136
+ 'Expected schedule for window construction shaded schedule. ' \
137
+ 'Got {}.'.format(type(value))
138
+ if value.schedule_type_limit is not None:
139
+ assert value.schedule_type_limit.numeric_type == 'Discrete', 'Dynamic ' \
140
+ 'window construction schedule should have a Discrete type limit. ' \
141
+ 'Got a schedule of numeric_type [{}].'.format(
142
+ value.schedule_type_limit.numeric_type)
143
+ assert value.schedule_type_limit.unit == 'fraction', 'Dynamic window ' \
144
+ 'construction schedule should have Mode or Control unit types. ' \
145
+ 'Got a schedule of unit_type [{}].'.format(
146
+ value.schedule_type_limit.unit_type)
147
+ value.lock() # lock editing in case schedule has multiple references
148
+ self._schedule = value
149
+
150
+ @property
151
+ def materials(self):
152
+ """Get a list of materials for the constituent constructions.
153
+
154
+ Materials will be listed from outside to inside and will move from the first
155
+ construction to the last.
156
+ """
157
+ return [m for con in self._constructions for m in con.materials]
158
+
159
+ @property
160
+ def layers(self):
161
+ """Get a list of material identifiers for the constituent constructions.
162
+
163
+ Materials will be listed from outside to inside and will move from the first
164
+ construction to the last.
165
+ """
166
+ return [mat.identifier for mat in self.materials]
167
+
168
+ @property
169
+ def unique_materials(self):
170
+ """A list of only unique material objects in the construction.
171
+
172
+ This will include the materials across all dynamic states of the construction.
173
+ """
174
+ return list(set([m for con in self._constructions for m in con.materials]))
175
+
176
+ @property
177
+ def frame(self):
178
+ """Get a window frame for the frame material surrounding the construction."""
179
+ return self._constructions[0].frame
180
+
181
+ @property
182
+ def r_value(self):
183
+ """R-value of the first window construction [m2-K/W] (excluding air films)."""
184
+ return self._constructions[0].r_value
185
+
186
+ @property
187
+ def u_value(self):
188
+ """U-value of the first window construction [W/m2-K] (excluding air films)."""
189
+ return self._constructions[0].u_value
190
+
191
+ @property
192
+ def r_factor(self):
193
+ """First window construction R-factor [m2-K/W] (with standard air films).
194
+
195
+ Formulas for film coefficients come from EN673 / ISO10292.
196
+ """
197
+ return self._constructions[0].r_factor
198
+
199
+ @property
200
+ def u_factor(self):
201
+ """First window construction U-factor [W/m2-K] (with standard air films).
202
+
203
+ Formulas for film coefficients come from EN673 / ISO10292.
204
+ """
205
+ return self._constructions[0].u_factor
206
+
207
+ @property
208
+ def solar_transmittance(self):
209
+ """The solar transmittance of the first window construction at normal incidence.
210
+ """
211
+ return self._constructions[0].solar_transmittance
212
+
213
+ @property
214
+ def visible_transmittance(self):
215
+ """Visible transmittance of the first window construction at normal incidence.
216
+ """
217
+ return self._constructions[0].visible_transmittance
218
+
219
+ @property
220
+ def shgc(self):
221
+ """The solar heat gain coefficient (SHGC) of the first window construction."""
222
+ return self._constructions[0].shgc
223
+
224
+ @property
225
+ def is_symmetric(self):
226
+ """Get a boolean for whether all of the construction layers are symmetric.
227
+
228
+ Symmetric means that the materials in reversed order are equal to those
229
+ in the current order (eg. 'Glass', 'Air Gap', 'Glass'). This is particularly
230
+ helpful for interior constructions, which need to have matching materials
231
+ in reversed order between adjacent Faces.
232
+ """
233
+ for con in self._constructions:
234
+ mats = con.materials
235
+ half_mat = int(len(mats) / 2)
236
+ for i in range(half_mat):
237
+ if mats[i] != mats[-(i + 1)]:
238
+ return False
239
+ return True
240
+
241
+ @property
242
+ def has_frame(self):
243
+ """Get a boolean noting whether the construction has a frame assigned to it."""
244
+ return self._constructions[0].has_frame
245
+
246
+ @property
247
+ def has_shade(self):
248
+ """Get a boolean noting whether dynamic shade materials are in the construction.
249
+ """
250
+ # This is False for all construction types except WindowConstructionShade.
251
+ return False
252
+
253
+ @property
254
+ def is_dynamic(self):
255
+ """Get a boolean noting whether the construction is dynamic.
256
+
257
+ This will always be True for this class.
258
+ """
259
+ return True
260
+
261
+ @property
262
+ def inside_emissivity(self):
263
+ """"The emissivity of the inside face of the first construction."""
264
+ mats = self._constructions[0].materials
265
+ if isinstance(mats[-1], EnergyWindowMaterialSimpleGlazSys):
266
+ return 0.84
267
+ try:
268
+ return mats[-1].emissivity_back
269
+ except AttributeError:
270
+ return mats[-1].emissivity
271
+
272
+ @property
273
+ def outside_emissivity(self):
274
+ """"The emissivity of the outside face of the first construction."""
275
+ mats = self._constructions[0].materials
276
+ if isinstance(mats[0], EnergyWindowMaterialSimpleGlazSys):
277
+ return 0.84
278
+ return mats[0].emissivity
279
+
280
+ @property
281
+ def thickness(self):
282
+ """Thickness of the first construction [m]."""
283
+ return self._constructions[0].thickness
284
+
285
+ @property
286
+ def glazing_count(self):
287
+ """The number of glazing materials contained within the first construction."""
288
+ return self._constructions[0].glazing_count
289
+
290
+ @property
291
+ def gap_count(self):
292
+ """The number of gas gaps contained within the first construction."""
293
+ return self._constructions[0].gap_count
294
+
295
+ @property
296
+ def inside_material(self):
297
+ """The the inside material layer of the first construction.
298
+
299
+ Useful for checking that an asymmetric construction is correctly assigned.
300
+ """
301
+ mats = self._constructions[0].materials
302
+ return mats[-1]
303
+
304
+ @property
305
+ def outside_material(self):
306
+ """The the outside material layer of the first construction.
307
+
308
+ Useful for checking that an asymmetric construction is correctly assigned.
309
+ """
310
+ mats = self._constructions[0].materials
311
+ return mats[0]
312
+
313
+ @property
314
+ def user_data(self):
315
+ """Get or set an optional dictionary for additional meta data for this object.
316
+
317
+ This will be None until it has been set. All keys and values of this
318
+ dictionary should be of a standard Python type to ensure correct
319
+ serialization of the object to/from JSON (eg. str, float, int, list, dict)
320
+ """
321
+ if self._user_data is not None:
322
+ return self._user_data
323
+
324
+ @user_data.setter
325
+ def user_data(self, value):
326
+ if value is not None:
327
+ assert isinstance(value, dict), 'Expected dictionary for honeybee_energy' \
328
+ 'object user_data. Got {}.'.format(type(value))
329
+ self._user_data = value
330
+
331
+ @classmethod
332
+ def from_dict(cls, data):
333
+ """Create a WindowConstructionDynamic from a dictionary.
334
+
335
+ Note that the dictionary must be a non-abridged version for this
336
+ classmethod to work.
337
+
338
+ Args:
339
+ data: A python dictionary in the following format
340
+
341
+ .. code-block:: python
342
+
343
+ {
344
+ "type": 'WindowConstructionDynamic',
345
+ "identifier": 'Double Pane Electrochromic 0.4-0.12',
346
+ "display_name": 'Electrochromic Window',
347
+ "constructions": [], # a list of WindowConstruction dictionaries
348
+ "schedule": {} # a ScheduleRuleset or ScheduleFixedInterval dictionary
349
+ }
350
+ """
351
+ # check the type
352
+ assert data['type'] == 'WindowConstructionDynamic', \
353
+ 'Expected WindowConstructionDynamic. Got {}.'.format(data['type'])
354
+ # re-serialize the inputs
355
+ constrs = [WindowConstruction.from_dict(c_dict)
356
+ for c_dict in data['constructions']]
357
+ schedule = dict_to_schedule(data['schedule'])
358
+ # create the object
359
+ new_obj = cls(data['identifier'], constrs, schedule)
360
+ if 'display_name' in data and data['display_name'] is not None:
361
+ new_obj.display_name = data['display_name']
362
+ if 'user_data' in data and data['user_data'] is not None:
363
+ new_obj.user_data = data['user_data']
364
+ return new_obj
365
+
366
+ @classmethod
367
+ def from_dict_abridged(cls, data, materials, schedules):
368
+ """Create a WindowConstructionDynamic from an abridged dictionary.
369
+
370
+ Args:
371
+ data: An WindowConstructionDynamic dictionary with the format below.
372
+ materials: A dictionary with identifiers of materials as keys and
373
+ Python material objects as values.
374
+ schedules: A dictionary with schedule identifiers as keys and
375
+ honeybee schedule objects as values.
376
+
377
+ .. code-block:: python
378
+
379
+ {
380
+ "type": 'WindowConstructionDynamicAbridged',
381
+ "identifier": 'Double Pane Electrochromic 0.4-0.12',
382
+ "display_name": 'Electrochromic Window',
383
+ "constructions": [], # a list of WindowConstructionAbridged dictionaries
384
+ "schedule": 'DayNight_Schedule' # a schedule identifier
385
+ }
386
+ """
387
+ # check the type
388
+ assert data['type'] == 'WindowConstructionDynamicAbridged', \
389
+ 'Expected WindowConstructionDynamicAbridged. Got {}.'.format(data['type'])
390
+ # re-serialize the inputs
391
+ constrs = [WindowConstruction.from_dict_abridged(c_dict, materials)
392
+ for c_dict in data['constructions']]
393
+ schedule = schedules[data['schedule']]
394
+ # create the object
395
+ new_obj = cls(data['identifier'], constrs, schedule)
396
+ if 'display_name' in data and data['display_name'] is not None:
397
+ new_obj.display_name = data['display_name']
398
+ if 'user_data' in data and data['user_data'] is not None:
399
+ new_obj.user_data = data['user_data']
400
+ return new_obj
401
+
402
+ def to_idf(self):
403
+ """Get an IDF string representation of this construction object.
404
+
405
+ Note that writing this string is not enough to add everything needed for
406
+ the construction to the IDF. The construction's materials also have to
407
+ be added as well as the schedule. The to_program_idf method must also
408
+ be called in order to add the EMS program that controls the constructions.
409
+
410
+ Returns:
411
+ Text string that includes the following EnergyPlus objects.
412
+
413
+ - Construction definitions for each state
414
+
415
+ - The EMS Construction Index Variable object for each state
416
+
417
+ - The EMS Sensor linked to the schedule
418
+
419
+ .. code-block:: shell
420
+
421
+ Construction,
422
+ TCwindow_25, !- Name
423
+ Clear3PPG, !- Outside Layer
424
+ AIR 3MM, !- Layer 2
425
+ WO18RT25, !- Layer 3
426
+ AIR 8MM, !- Layer 4
427
+ SB60Clear3PPG; !- Layer 5
428
+
429
+ EnergyManagementSystem:ConstructionIndexVariable,
430
+ TCwindow_25_obj,
431
+ TCwindow_25;
432
+ """
433
+ idf_strs = []
434
+ # add all of the construction definitions and EMS states
435
+ state_com = ('name', 'construction name')
436
+ for i, con in enumerate(self.constructions):
437
+ con_dup = con.duplicate()
438
+ con_dup.identifier = '{}State{}'.format(con.identifier, i)
439
+ idf_strs.append(con_dup.to_idf())
440
+ state_id = 'State{}{}'.format(i, re.sub('[^A-Za-z0-9]', '', con.identifier))
441
+ vals = [state_id, con_dup.identifier]
442
+ state_str = generate_idf_string(
443
+ 'EnergyManagementSystem:ConstructionIndexVariable', vals, state_com)
444
+ idf_strs.append(state_str)
445
+
446
+ # add the EMS Sensor definition
447
+ sensor_com = ('name', 'variable key name', 'variable name')
448
+ sensor_id = 'Sensor{}'.format(re.sub('[^A-Za-z0-9]', '', self.identifier))
449
+ sen_vals = [sensor_id, self.schedule.identifier, 'Schedule Value']
450
+ sensor = generate_idf_string(
451
+ 'EnergyManagementSystem:Sensor', sen_vals, sensor_com)
452
+ idf_strs.append(sensor)
453
+ return '\n\n'.join(idf_strs)
454
+
455
+ def to_program_idf(self, aperture_identifiers):
456
+ """Get an IDF string representation of the EMS program.
457
+
458
+ Args:
459
+ aperture_identifiers: A list of Aperture identifiers to
460
+ which this construction is assigned.
461
+
462
+ Returns:
463
+ Text string that includes the following EnergyPlus objects.
464
+
465
+ - The EMS Actuators linked to the apertures
466
+
467
+ - The EMS Program definition
468
+
469
+ """
470
+ idf_strs = []
471
+ # add all of the actuators linked to the apertures
472
+ act_com = ('name', 'component name', 'component type', 'component control')
473
+ actuator_ids = []
474
+ for i, ap_id in enumerate(aperture_identifiers):
475
+ act_id = 'Actuator{}{}'.format(i, re.sub('[^A-Za-z0-9]', '', ap_id))
476
+ act_vals = [act_id, ap_id, 'Surface', 'Construction State']
477
+ actuator = generate_idf_string(
478
+ 'EnergyManagementSystem:Actuator', act_vals, act_com)
479
+ actuator_ids.append(act_id)
480
+ idf_strs.append(actuator)
481
+
482
+ # add each construction state to the program
483
+ pid = 'StateChange{}'.format(re.sub('[^A-Za-z0-9]', '', self.identifier))
484
+ ems_program = [pid]
485
+ sensor_id = 'Sensor{}'.format(re.sub('[^A-Za-z0-9]', '', self.identifier))
486
+ max_state_count = len(self.constructions) - 1
487
+ for i, con in enumerate(self.constructions):
488
+ # determine which conditional operator to use
489
+ cond_op = 'IF' if i == 0 else 'ELSEIF'
490
+ # add the conditional statement
491
+ state_count = i + 1
492
+ if i == max_state_count:
493
+ cond_stmt = 'ELSE'
494
+ else:
495
+ cond_stmt = '{} ({} < {})'.format(cond_op, sensor_id, state_count)
496
+ ems_program.append(cond_stmt)
497
+ # loop through the actuators and set the appropriate window state
498
+ state_id = 'State{}{}'.format(i, re.sub('[^A-Za-z0-9]', '', con.identifier))
499
+ for act_name in actuator_ids:
500
+ ems_program.append('SET {} = {}'.format(act_name, state_id))
501
+ ems_program.append('ENDIF')
502
+ ems_prog_str = generate_idf_string('EnergyManagementSystem:Program', ems_program)
503
+ idf_strs.append(ems_prog_str)
504
+ return '\n\n'.join(idf_strs)
505
+
506
+ @staticmethod
507
+ def idf_program_manager(constructions):
508
+ """Get an IDF string representation of the EMS program calling manager.
509
+
510
+ Args:
511
+ constructions: A list of WindowConstructionDynamic objects to be written
512
+ into a program manager.
513
+ """
514
+ man_com = ['name', 'calling point']
515
+ man_vals = ['Dynamic_Window_Constructions', 'BeginTimestepBeforePredictor']
516
+ for i, con in enumerate(constructions):
517
+ pid = 'StateChange{}'.format(re.sub('[^A-Za-z0-9]', '', con.identifier))
518
+ man_vals.append(pid)
519
+ man_com.append('program name{}'.format(i))
520
+ manager = generate_idf_string(
521
+ 'EnergyManagementSystem:ProgramCallingManager', man_vals, man_com)
522
+ return manager
523
+
524
+ def to_radiance_solar(self):
525
+ """Honeybee Radiance material for the first construction."""
526
+ # TODO: add methods that can represent the dynamic window behavior
527
+ return self._constructions[0].to_radiance_solar()
528
+
529
+ def to_radiance_visible(self):
530
+ """Honeybee Radiance material for the first construction."""
531
+ # TODO: add methods that can represent the dynamic window behavior
532
+ return self._constructions[0].to_radiance_visible()
533
+
534
+ def to_dict(self, abridged=False):
535
+ """Window construction dictionary representation.
536
+
537
+ Args:
538
+ abridged: Boolean to note whether the full dictionary describing the
539
+ object should be returned (False) or just an abridged version (True),
540
+ which only specifies the identifiers of material layers and
541
+ schedules. (Default: False).
542
+ """
543
+ base = {'type': 'WindowConstructionDynamic'} if not \
544
+ abridged else {'type': 'WindowConstructionDynamicAbridged'}
545
+ base['identifier'] = self.identifier
546
+ base['constructions'] = [con.to_dict(abridged) for con in self.constructions]
547
+ base['schedule'] = self.schedule.identifier if abridged \
548
+ else self.schedule.to_dict()
549
+ if self._display_name is not None:
550
+ base['display_name'] = self.display_name
551
+ if self._user_data is not None:
552
+ base['user_data'] = self.user_data
553
+ return base
554
+
555
+ def lock(self):
556
+ """The lock() method will also lock the constructions."""
557
+ self._locked = True
558
+ for mat in self.constructions:
559
+ mat.lock()
560
+
561
+ def unlock(self):
562
+ """The unlock() method will also unlock the constructions."""
563
+ self._locked = False
564
+ for mat in self.constructions:
565
+ mat.unlock()
566
+
567
+ def duplicate(self):
568
+ """Get a copy of this construction."""
569
+ return self.__copy__()
570
+
571
+ def __copy__(self):
572
+ new_con = WindowConstructionDynamic(
573
+ self.identifier, self.constructions, self.schedule)
574
+ new_con._display_name = self._display_name
575
+ new_con.user_data = None if self._user_data is None else self._user_data.copy()
576
+ return new_con
577
+
578
+ def __len__(self):
579
+ return len(self._constructions)
580
+
581
+ def __getitem__(self, key):
582
+ return self._constructions[key]
583
+
584
+ def __iter__(self):
585
+ return iter(self._constructions)
586
+
587
+ def __key(self):
588
+ """A tuple based on the object properties, useful for hashing."""
589
+ return (self._identifier, hash(self.schedule)) + \
590
+ tuple(hash(con) for con in self.constructions)
591
+
592
+ def __hash__(self):
593
+ return hash(self.__key())
594
+
595
+ def __eq__(self, other):
596
+ return isinstance(other, WindowConstructionDynamic) and \
597
+ self.__key() == other.__key()
598
+
599
+ def __ne__(self, other):
600
+ return not self.__eq__(other)
601
+
602
+ def ToString(self):
603
+ """Overwrite .NET ToString."""
604
+ return self.__repr__()
605
+
606
+ def __repr__(self):
607
+ return 'WindowConstructionDynamic: [{} states]'.format(len(self.constructions))