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,1655 @@
1
+ # coding=utf-8
2
+ """Energy Construction Set."""
3
+ from __future__ import division
4
+
5
+ from .construction.dictutil import dict_to_construction
6
+ from .construction.opaque import OpaqueConstruction
7
+ from .construction.window import WindowConstruction
8
+ from .construction.windowshade import WindowConstructionShade
9
+ from .construction.dynamic import WindowConstructionDynamic
10
+ from .construction.shade import ShadeConstruction
11
+ from .construction.air import AirBoundaryConstruction
12
+ import honeybee_energy.lib.constructions as _lib
13
+
14
+ from honeybee._lockable import lockable
15
+ from honeybee.typing import valid_ep_string, clean_rad_string
16
+
17
+
18
+ @lockable
19
+ class ConstructionSet(object):
20
+ """Set containing all energy constructions needed to create an energy model.
21
+
22
+ Args:
23
+ identifier: Text string for a unique ConstructionSet ID. Must be < 100
24
+ characters and not contain any EnergyPlus special characters. This
25
+ will be used to identify the object across a model and in the
26
+ exported IDF.
27
+ wall_set: An optional WallConstructionSet object for this ConstructionSet.
28
+ If None, it will be the honeybee generic default WallConstructionSet.
29
+ floor_set: An optional FloorConstructionSet object for this ConstructionSet.
30
+ If None, it will be the honeybee generic default FloorConstructionSet.
31
+ roof_ceiling_set: An optional RoofCeilingConstructionSet object for this
32
+ ConstructionSet. If None, it will be the honeybee generic default
33
+ RoofCeilingConstructionSet.
34
+ aperture_set: An optional ApertureConstructionSet object for this
35
+ ConstructionSet. If None, it will be the honeybee generic default
36
+ ApertureConstructionSet.
37
+ door_set: An optional DoorConstructionSet object for this ConstructionSet.
38
+ If None, it will be the honeybee generic default DoorConstructionSet.
39
+ shade_construction: An optional ShadeConstruction to set the reflectance
40
+ properties of all outdoor shades to which this ConstructionSet is
41
+ assigned. If None, it will be the honeybee generic shade construction.
42
+ air_boundary_construction: An optional AirBoundaryConstruction or
43
+ OpaqueConstruction to set the properties of Faces with an AirBoundary
44
+ type. If None, it will be the honeybee generic air boundary construction.
45
+
46
+ Properties:
47
+ * identifier
48
+ * display_name
49
+ * wall_set
50
+ * floor_set
51
+ * roof_ceiling_set
52
+ * aperture_set
53
+ * door_set
54
+ * shade_construction
55
+ * air_boundary_construction
56
+ * constructions
57
+ * modified_constructions
58
+ * constructions_unique
59
+ * modified_constructions_unique
60
+ * materials_unique
61
+ * modified_materials_unique
62
+ * is_interior_defaulted
63
+ * is_interior_symmetric
64
+ * user_data
65
+ """
66
+ __slots__ = ('_identifier', '_display_name', '_wall_set', '_floor_set',
67
+ '_roof_ceiling_set', '_aperture_set', '_door_set',
68
+ '_shade_construction', '_air_boundary_construction',
69
+ '_locked', '_user_data')
70
+
71
+ def __init__(self, identifier, wall_set=None, floor_set=None, roof_ceiling_set=None,
72
+ aperture_set=None, door_set=None, shade_construction=None,
73
+ air_boundary_construction=None):
74
+ """Initialize energy construction set."""
75
+ self._locked = False # unlocked by default
76
+ self.identifier = identifier
77
+ self._display_name = None
78
+ self.wall_set = wall_set
79
+ self.floor_set = floor_set
80
+ self.roof_ceiling_set = roof_ceiling_set
81
+ self.aperture_set = aperture_set
82
+ self.door_set = door_set
83
+ self.shade_construction = shade_construction
84
+ self.air_boundary_construction = air_boundary_construction
85
+ self._user_data = None
86
+
87
+ @property
88
+ def identifier(self):
89
+ """Get or set a text string for a unique construction set identifier."""
90
+ return self._identifier
91
+
92
+ @identifier.setter
93
+ def identifier(self, identifier):
94
+ self._identifier = valid_ep_string(identifier, 'construction set identifier')
95
+
96
+ @property
97
+ def display_name(self):
98
+ """Get or set a string for the object name without any character restrictions.
99
+
100
+ If not set, this will be equal to the identifier.
101
+ """
102
+ if self._display_name is None:
103
+ return self._identifier
104
+ return self._display_name
105
+
106
+ @display_name.setter
107
+ def display_name(self, value):
108
+ if value is not None:
109
+ try:
110
+ value = str(value)
111
+ except UnicodeEncodeError: # Python 2 machine lacking the character set
112
+ pass # keep it as unicode
113
+ self._display_name = value
114
+
115
+ @property
116
+ def wall_set(self):
117
+ """Get or set the WallConstructionSet assigned to this ConstructionSet."""
118
+ return self._wall_set
119
+
120
+ @wall_set.setter
121
+ def wall_set(self, value):
122
+ if value is not None:
123
+ assert isinstance(value, WallConstructionSet), \
124
+ 'Expected WallConstructionSet. Got {}'.format(type(value))
125
+ self._wall_set = value
126
+ else:
127
+ self._wall_set = WallConstructionSet()
128
+
129
+ @property
130
+ def floor_set(self):
131
+ """Get or set the FloorConstructionSet assigned to this ConstructionSet."""
132
+ return self._floor_set
133
+
134
+ @floor_set.setter
135
+ def floor_set(self, value):
136
+ if value is not None:
137
+ assert isinstance(value, FloorConstructionSet), \
138
+ 'Expected FloorConstructionSet. Got {}'.format(type(value))
139
+ self._floor_set = value
140
+ else:
141
+ self._floor_set = FloorConstructionSet()
142
+
143
+ @property
144
+ def roof_ceiling_set(self):
145
+ """Get or set the RoofCeilingConstructionSet assigned to this ConstructionSet."""
146
+ return self._roof_ceiling_set
147
+
148
+ @roof_ceiling_set.setter
149
+ def roof_ceiling_set(self, value):
150
+ if value is not None:
151
+ assert isinstance(value, RoofCeilingConstructionSet), \
152
+ 'Expected RoofCeilingConstructionSet. Got {}'.format(type(value))
153
+ self._roof_ceiling_set = value
154
+ else:
155
+ self._roof_ceiling_set = RoofCeilingConstructionSet()
156
+
157
+ @property
158
+ def aperture_set(self):
159
+ """Get or set the ApertureConstructionSet assigned to this ConstructionSet."""
160
+ return self._aperture_set
161
+
162
+ @aperture_set.setter
163
+ def aperture_set(self, value):
164
+ if value is not None:
165
+ assert isinstance(value, ApertureConstructionSet), \
166
+ 'Expected ApertureConstructionSet. Got {}'.format(type(value))
167
+ self._aperture_set = value
168
+ else:
169
+ self._aperture_set = ApertureConstructionSet()
170
+
171
+ @property
172
+ def door_set(self):
173
+ """Get or set the DoorConstructionSet assigned to this ConstructionSet."""
174
+ return self._door_set
175
+
176
+ @door_set.setter
177
+ def door_set(self, value):
178
+ if value is not None:
179
+ assert isinstance(value, DoorConstructionSet), \
180
+ 'Expected DoorConstructionSet. Got {}'.format(type(value))
181
+ self._door_set = value
182
+ else:
183
+ self._door_set = DoorConstructionSet()
184
+
185
+ @property
186
+ def shade_construction(self):
187
+ """Get or set the ShadeConstruction assigned to this ConstructionSet."""
188
+ if self._shade_construction is None:
189
+ return _lib.generic_shade
190
+ return self._shade_construction
191
+
192
+ @shade_construction.setter
193
+ def shade_construction(self, value):
194
+ if value is not None:
195
+ assert isinstance(value, ShadeConstruction), \
196
+ 'Expected ShadeConstruction. Got {}'.format(type(value))
197
+ value.lock() # lock editing in case construction has multiple references
198
+ self._shade_construction = value
199
+
200
+ @property
201
+ def air_boundary_construction(self):
202
+ """Get or set the AirBoundaryConstruction assigned to this ConstructionSet."""
203
+ if self._air_boundary_construction is None:
204
+ return _lib.air_boundary
205
+ return self._air_boundary_construction
206
+
207
+ @air_boundary_construction.setter
208
+ def air_boundary_construction(self, value):
209
+ if value is not None:
210
+ assert isinstance(value, (AirBoundaryConstruction, OpaqueConstruction)), \
211
+ 'Expected AirBoundaryConstruction or OpaqueConstruction. ' \
212
+ 'Got {}'.format(type(value))
213
+ value.lock() # lock editing in case construction has multiple references
214
+ self._air_boundary_construction = value
215
+
216
+ @property
217
+ def constructions(self):
218
+ """List of all constructions contained within the set."""
219
+ return self.wall_set.constructions + \
220
+ self.floor_set.constructions + \
221
+ self.roof_ceiling_set.constructions + \
222
+ self.aperture_set.constructions + \
223
+ self.door_set.constructions + \
224
+ [self.shade_construction, self.air_boundary_construction]
225
+
226
+ @property
227
+ def modified_constructions(self):
228
+ """List of all constructions that are not defaulted within the set."""
229
+ mod_constructions = self.wall_set.modified_constructions + \
230
+ self.floor_set.modified_constructions + \
231
+ self.roof_ceiling_set.modified_constructions + \
232
+ self.aperture_set.modified_constructions + \
233
+ self.door_set.modified_constructions
234
+ if self._shade_construction is not None:
235
+ mod_constructions.append(self._shade_construction)
236
+ if self._air_boundary_construction is not None:
237
+ mod_constructions.append(self._air_boundary_construction)
238
+ return mod_constructions
239
+
240
+ @property
241
+ def constructions_unique(self):
242
+ """List of all unique constructions contained within the set."""
243
+ return list(set(self.constructions))
244
+
245
+ @property
246
+ def modified_constructions_unique(self):
247
+ """List of all unique constructions that are not defaulted within the set."""
248
+ return list(set(self.modified_constructions))
249
+
250
+ @property
251
+ def materials_unique(self):
252
+ """List of all unique materials contained within the set."""
253
+ materials = []
254
+ for constr in self.constructions:
255
+ try:
256
+ materials.extend(constr.materials)
257
+ if constr.has_frame:
258
+ materials.append(constr.frame)
259
+ except AttributeError:
260
+ pass # ShadeConstruction or AirBoundaryConstruction
261
+ return list(set(materials))
262
+
263
+ @property
264
+ def modified_materials_unique(self):
265
+ """List of all unique materials that are not defaulted within the set."""
266
+ materials = []
267
+ for constr in self.modified_constructions:
268
+ try:
269
+ materials.extend(constr.materials)
270
+ if constr.has_frame:
271
+ materials.append(constr.frame)
272
+ except AttributeError:
273
+ pass # ShadeConstruction or AirBoundaryConstruction
274
+ return list(set(materials))
275
+
276
+ @property
277
+ def is_interior_defaulted(self):
278
+ """Get a boolean for whether all interior constructions of the set are defaulted.
279
+
280
+ If all construction sets in a model use the same default interior constructions,
281
+ it meas that the construction sets won't create any conflicts between adjacent
282
+ interior Faces.
283
+ """
284
+ return self.wall_set._interior_construction is None and \
285
+ self.aperture_set._interior_construction is None and \
286
+ self.door_set._interior_construction is None and \
287
+ self.floor_set._interior_construction is None and \
288
+ self.roof_ceiling_set._interior_construction is None
289
+
290
+ @property
291
+ def is_interior_symmetric(self):
292
+ """Get a boolean for whether all interior constructions of the set are symmetric.
293
+
294
+ This will only be true if the interior wall, window and door constructions
295
+ are symmetric and the material layers of the interior floor construction
296
+ is a reversed version of the ceiling construction. When these criteria
297
+ are met, it meas that the construction set can be applied to all rooms
298
+ of a Model without concern for it creating conflicts between adjacent
299
+ interior Faces.
300
+ """
301
+ # check the constructions that are supposed to be symmetric
302
+ wall_con = self.wall_set._interior_construction
303
+ if wall_con is not None and not wall_con.is_symmetric:
304
+ return False
305
+ win_con = self.aperture_set._interior_construction
306
+ if win_con is not None and not win_con.is_symmetric:
307
+ return False
308
+ door_con = self.door_set._interior_construction
309
+ if door_con is not None and not door_con.is_symmetric:
310
+ return False
311
+ glz_dr_con = self.door_set._interior_glass_construction
312
+ if glz_dr_con is not None and not glz_dr_con.is_symmetric:
313
+ return False
314
+ # check that the floor is the reverse of the floor
315
+ floor_con = self.floor_set.interior_construction
316
+ ceil_con = self.roof_ceiling_set.interior_construction
317
+ if floor_con.materials != tuple(reversed(ceil_con.materials)):
318
+ return False
319
+ return True
320
+
321
+ @property
322
+ def user_data(self):
323
+ """Get or set an optional dictionary for additional meta data for this object.
324
+
325
+ This will be None until it has been set. All keys and values of this
326
+ dictionary should be of a standard Python type to ensure correct
327
+ serialization of the object to/from JSON (eg. str, float, int, list, dict)
328
+ """
329
+ if self._user_data is not None:
330
+ return self._user_data
331
+
332
+ @user_data.setter
333
+ def user_data(self, value):
334
+ if value is not None:
335
+ assert isinstance(value, dict), 'Expected dictionary for honeybee_energy' \
336
+ 'object user_data. Got {}.'.format(type(value))
337
+ self._user_data = value
338
+
339
+ def get_face_construction(self, face_type, boundary_condition):
340
+ """Get a construction object that will be assigned to a given type of face.
341
+
342
+ Args:
343
+ face_type: Text string for the type of face (eg. 'Wall', 'Floor',
344
+ 'Roof', 'AirBoundary').
345
+ boundary_condition: Text string for the boundary condition
346
+ (eg. 'Outdoors', 'Surface', 'Adiabatic', 'Ground')
347
+ """
348
+ if face_type == 'Wall':
349
+ return self._get_constr_from_set(self.wall_set, boundary_condition)
350
+ elif face_type == 'Floor':
351
+ return self._get_constr_from_set(self.floor_set, boundary_condition)
352
+ elif face_type == 'RoofCeiling':
353
+ return self._get_constr_from_set(self.roof_ceiling_set, boundary_condition)
354
+ elif face_type == 'AirBoundary':
355
+ return self.air_boundary_construction
356
+ else:
357
+ raise NotImplementedError(
358
+ 'Face type {} is not recognized for ConstructionSet'.format(face_type))
359
+
360
+ def get_aperture_construction(self, boundary_condition, is_operable,
361
+ parent_face_type):
362
+ """Get a construction object that will be assigned to a given type of aperture.
363
+
364
+ Args:
365
+ boundary_condition: Text string for the boundary condition
366
+ (eg. 'Outdoors', 'Surface')
367
+ is_operable: Boolean to note whether the aperture is operable.
368
+ parent_face_type: Text string for the type of face to which the aperture
369
+ is a child (eg. 'Wall', 'Floor', 'Roof').
370
+ """
371
+ if boundary_condition == 'Outdoors':
372
+ if not is_operable:
373
+ if parent_face_type == 'Wall':
374
+ return self.aperture_set.window_construction
375
+ else:
376
+ return self.aperture_set.skylight_construction
377
+ else:
378
+ return self.aperture_set.operable_construction
379
+ elif boundary_condition == 'Surface':
380
+ return self.aperture_set.interior_construction
381
+ else:
382
+ raise NotImplementedError(
383
+ 'Boundary condition {} is not recognized for apertures in '
384
+ 'ConstructionSet'.format(boundary_condition))
385
+
386
+ def get_door_construction(self, boundary_condition, is_glass, parent_face_type):
387
+ """Get a construction object that will be assigned to a given type of door.
388
+
389
+ Args:
390
+ boundary_condition: Text string for the boundary condition
391
+ (eg. 'Outdoors', 'Surface')
392
+ is_glass: Boolean to note whether the door is glass (instead of opaque).
393
+ parent_face_type: Text string for the type of face to which the door
394
+ is a child (eg. 'Wall', 'Floor', 'Roof').
395
+ """
396
+ if boundary_condition == 'Outdoors':
397
+ if not is_glass:
398
+ if parent_face_type == 'Wall':
399
+ return self.door_set.exterior_construction
400
+ else:
401
+ return self.door_set.overhead_construction
402
+ else:
403
+ return self.door_set.exterior_glass_construction
404
+ elif boundary_condition == 'Surface':
405
+ if not is_glass:
406
+ return self.door_set.interior_construction
407
+ else:
408
+ return self.door_set.interior_glass_construction
409
+ else:
410
+ raise NotImplementedError(
411
+ 'Boundary condition {} is not recognized for doors in '
412
+ 'ConstructionSet'.format(boundary_condition))
413
+
414
+ @classmethod
415
+ def from_dict(cls, data):
416
+ """Create a ConstructionSet from a dictionary.
417
+
418
+ Note that the dictionary must be a non-abridged version for this
419
+ classmethod to work.
420
+
421
+ Args:
422
+ data: Dictionary describing the ConstructionSet with the
423
+ format below.
424
+
425
+ .. code-block:: python
426
+
427
+ {
428
+ "type": 'ConstructionSet',
429
+ "identifier": str, # ConstructionSet identifier
430
+ "display_name": str, # ConstructionSet display name
431
+ "wall_set": {}, # A WallConstructionSet dictionary
432
+ "floor_set": {}, # A FloorConstructionSet dictionary
433
+ "roof_ceiling_set": {}, # A RoofCeilingConstructionSet dictionary
434
+ "aperture_set": {}, # A ApertureConstructionSet dictionary
435
+ "door_set": {}, # A DoorConstructionSet dictionary
436
+ "shade_construction": {}, # ShadeConstruction dictionary
437
+ "air_boundary_construction": {}, # AirBoundaryConstruction dictionary
438
+ }
439
+ """
440
+ assert data['type'] == 'ConstructionSet', \
441
+ 'Expected ConstructionSet. Got {}.'.format(data['type'])
442
+
443
+ # build each of the sub-construction sets
444
+ wall_set = WallConstructionSet.from_dict(data['wall_set']) if 'wall_set' \
445
+ in data and data['wall_set'] is not None else None
446
+ floor_set = FloorConstructionSet.from_dict(data['floor_set']) if 'floor_set' \
447
+ in data and data['floor_set'] is not None else None
448
+ roof_ceiling_set = \
449
+ RoofCeilingConstructionSet.from_dict(data['roof_ceiling_set']) \
450
+ if 'roof_ceiling_set' in data and data['roof_ceiling_set'] \
451
+ is not None else None
452
+ aperture_set = ApertureConstructionSet.from_dict(data['aperture_set']) if \
453
+ 'aperture_set' in data and data['aperture_set'] is not None else None
454
+ door_set = DoorConstructionSet.from_dict(data['door_set']) if \
455
+ 'door_set' in data and data['door_set'] is not None else None
456
+ shade_con = ShadeConstruction.from_dict(data['shade_construction']) if \
457
+ 'shade_construction' in data and data['shade_construction'] is not None \
458
+ else None
459
+ air_con = None
460
+ if 'air_boundary_construction' in data and \
461
+ data['air_boundary_construction'] is not None:
462
+ if data['air_boundary_construction']['type'] == 'AirBoundaryConstruction':
463
+ air_con = AirBoundaryConstruction.from_dict(
464
+ data['air_boundary_construction'])
465
+ else:
466
+ air_con = OpaqueConstruction.from_dict(data['air_boundary_construction'])
467
+
468
+ new_obj = cls(data['identifier'], wall_set, floor_set, roof_ceiling_set,
469
+ aperture_set, door_set, shade_con, air_con)
470
+ if 'display_name' in data and data['display_name'] is not None:
471
+ new_obj.display_name = data['display_name']
472
+ if 'user_data' in data and data['user_data'] is not None:
473
+ new_obj.user_data = data['user_data']
474
+ return new_obj
475
+
476
+ @classmethod
477
+ def from_dict_abridged(cls, data, construction_dict):
478
+ """Create a ConstructionSet from an abridged dictionary.
479
+
480
+ Args:
481
+ data: A ConstructionSetAbridged dictionary.
482
+ construction_dict: A dictionary with construction identifiers as keys and
483
+ honeybee construction objects as values. These will be used to
484
+ assign the constructions to the ConstructionSet object.
485
+
486
+ .. code-block:: python
487
+
488
+ {
489
+ "type": 'ConstructionSetAbridged',
490
+ "identifier": str, # ConstructionSet identifier
491
+ "display_name": str, # ConstructionSet display name
492
+ "wall_set": {}, # A WallConstructionSetAbridged dictionary
493
+ "floor_set": {}, # A FloorConstructionSetAbridged dictionary
494
+ "roof_ceiling_set": {}, # A RoofCeilingConstructionSetAbridged dictionary
495
+ "aperture_set": {}, # A ApertureConstructionSetAbridged dictionary
496
+ "door_set": {}, # A DoorConstructionSetAbridged dictionary
497
+ "shade_construction": str, # ShadeConstruction identifier
498
+ "air_boundary_construction": str # AirBoundaryConstruction identifier
499
+ }
500
+ """
501
+ assert data['type'] == 'ConstructionSetAbridged', \
502
+ 'Expected ConstructionSetAbridged. Got {}.'.format(data['type'])
503
+ try:
504
+ wall_set, floor_set, roof_ceiling_set, aperture_set, door_set, shade_con, \
505
+ air_con = cls._get_subsets_from_abridged(data, construction_dict)
506
+ except KeyError as e:
507
+ raise ValueError(
508
+ 'The following construction is missing from the model: {}'.format(e)
509
+ )
510
+ new_obj = cls(data['identifier'], wall_set, floor_set, roof_ceiling_set,
511
+ aperture_set, door_set, shade_con, air_con)
512
+ if 'display_name' in data and data['display_name'] is not None:
513
+ new_obj.display_name = data['display_name']
514
+ if 'user_data' in data and data['user_data'] is not None:
515
+ new_obj.user_data = data['user_data']
516
+ return new_obj
517
+
518
+ def to_dict(self, abridged=False, none_for_defaults=True):
519
+ """Get ConstructionSet as a dictionary.
520
+
521
+ Args:
522
+ abridged: Boolean noting whether detailed materials and construction
523
+ objects should be written into the ConstructionSet (False) or just
524
+ an abridged version (True). Default: False.
525
+ none_for_defaults: Boolean to note whether default constructions in the
526
+ set should be included in detail (False) or should be None (True).
527
+ Default: True.
528
+ """
529
+ base = {'type': 'ConstructionSet'} if not \
530
+ abridged else {'type': 'ConstructionSetAbridged'}
531
+ base['identifier'] = self.identifier
532
+ base['wall_set'] = self.wall_set.to_dict(abridged, none_for_defaults)
533
+ base['floor_set'] = self.floor_set.to_dict(abridged, none_for_defaults)
534
+ base['roof_ceiling_set'] = \
535
+ self.roof_ceiling_set.to_dict(abridged, none_for_defaults)
536
+ base['aperture_set'] = self.aperture_set.to_dict(abridged, none_for_defaults)
537
+ base['door_set'] = self.door_set.to_dict(abridged, none_for_defaults)
538
+ if none_for_defaults:
539
+ if abridged:
540
+ base['shade_construction'] = self._shade_construction.identifier \
541
+ if self._shade_construction is not None else None
542
+ else:
543
+ base['shade_construction'] = self._shade_construction.to_dict() \
544
+ if self._shade_construction is not None else None
545
+ else:
546
+ base['shade_construction'] = self.shade_construction.identifier \
547
+ if abridged else self.shade_construction.to_dict()
548
+ if none_for_defaults:
549
+ if abridged:
550
+ base['air_boundary_construction'] = \
551
+ self._air_boundary_construction.identifier if \
552
+ self._air_boundary_construction is not None else None
553
+ else:
554
+ base['air_boundary_construction'] = \
555
+ self._air_boundary_construction.to_dict() if \
556
+ self._air_boundary_construction is not None else None
557
+ else:
558
+ base['air_boundary_construction'] = \
559
+ self.air_boundary_construction.identifier \
560
+ if abridged else self.air_boundary_construction.to_dict()
561
+
562
+ if self._display_name is not None:
563
+ base['display_name'] = self.display_name
564
+ if self._user_data is not None:
565
+ base['user_data'] = self.user_data
566
+ return base
567
+
568
+ def to_radiance_solar_interior(self):
569
+ """Honeybee Radiance modifier set for the interior solar properties."""
570
+ # convert all interior constructions into modifiers
571
+ unique_mods = {}
572
+ for constr in self.constructions_unique:
573
+ unique_mods[constr.identifier] = constr.to_radiance_solar_interior() \
574
+ if isinstance(constr, OpaqueConstruction) else constr.to_radiance_solar()
575
+ return self._create_modifier_set('Solar_Interior', unique_mods)
576
+
577
+ def to_radiance_visible_interior(self):
578
+ """Honeybee Radiance modifier set for the interior visible properties."""
579
+ # convert all interior constructions into modifiers
580
+ unique_mods = {}
581
+ for constr in self.constructions_unique:
582
+ unique_mods[constr.identifier] = constr.to_radiance_visible_interior() \
583
+ if isinstance(constr, OpaqueConstruction) \
584
+ else constr.to_radiance_visible()
585
+ return self._create_modifier_set('Visible_Interior', unique_mods)
586
+
587
+ def to_radiance_solar_exterior(self):
588
+ """Honeybee Radiance modifier set for the exterior solar properties."""
589
+ # convert all exterior constructions into modifiers
590
+ unique_mods = {}
591
+ for constr in self.constructions_unique:
592
+ unique_mods[constr.identifier] = constr.to_radiance_solar_exterior() \
593
+ if isinstance(constr, OpaqueConstruction) else constr.to_radiance_solar()
594
+ return self._create_modifier_set('Solar_Exterior', unique_mods)
595
+
596
+ def to_radiance_visible_exterior(self):
597
+ """Honeybee Radiance modifier set for the exterior visible properties."""
598
+ # convert all exterior constructions into modifiers
599
+ unique_mods = {}
600
+ for constr in self.constructions_unique:
601
+ unique_mods[constr.identifier] = constr.to_radiance_visible_exterior() \
602
+ if isinstance(constr, OpaqueConstruction) \
603
+ else constr.to_radiance_visible()
604
+ return self._create_modifier_set('Visible_Exterior', unique_mods)
605
+
606
+ def duplicate(self):
607
+ """Get a copy of this ConstructionSet."""
608
+ return self.__copy__()
609
+
610
+ def lock(self):
611
+ """The lock() method to will also lock the WallConstructionSet, etc."""
612
+ self._locked = True
613
+ self._wall_set.lock()
614
+ self._floor_set.lock()
615
+ self._roof_ceiling_set.lock()
616
+ self._aperture_set.lock()
617
+ self._door_set.lock()
618
+
619
+ def unlock(self):
620
+ """The unlock() method will also unlock the WallConstructionSet, etc."""
621
+ self._locked = False
622
+ self._wall_set.unlock()
623
+ self._floor_set.unlock()
624
+ self._roof_ceiling_set.unlock()
625
+ self._aperture_set.unlock()
626
+ self._door_set.unlock()
627
+
628
+ def _create_modifier_set(self, mod_type, unique_mods):
629
+ """Create a modifier set from a dictionary of radiance modifiers."""
630
+ # import the radiance dependency
631
+ try:
632
+ from honeybee_radiance.modifierset import ModifierSet
633
+ except ImportError as e:
634
+ raise ImportError('honeybee_radiance library must be installed to use '
635
+ 'to_radiance_solar() method. {}'.format(e))
636
+ # create the modifier set object
637
+ mod_set = ModifierSet(
638
+ '{}_{}'.format(clean_rad_string(self.identifier), mod_type))
639
+ mod_set.wall_set.exterior_modifier = \
640
+ unique_mods[self.wall_set.exterior_construction.identifier]
641
+ mod_set.wall_set.interior_modifier = \
642
+ unique_mods[self.wall_set.interior_construction.identifier]
643
+ mod_set.floor_set.exterior_modifier = \
644
+ unique_mods[self.floor_set.exterior_construction.identifier]
645
+ mod_set.floor_set.interior_modifier = \
646
+ unique_mods[self.floor_set.interior_construction.identifier]
647
+ mod_set.roof_ceiling_set.exterior_modifier = \
648
+ unique_mods[self.roof_ceiling_set.exterior_construction.identifier]
649
+ mod_set.roof_ceiling_set.interior_modifier = \
650
+ unique_mods[self.roof_ceiling_set.interior_construction.identifier]
651
+ mod_set.aperture_set.window_modifier = \
652
+ unique_mods[self.aperture_set.window_construction.identifier]
653
+ mod_set.aperture_set.interior_modifier = \
654
+ unique_mods[self.aperture_set.interior_construction.identifier]
655
+ mod_set.aperture_set.skylight_modifier = \
656
+ unique_mods[self.aperture_set.skylight_construction.identifier]
657
+ mod_set.aperture_set.operable_modifier = \
658
+ unique_mods[self.aperture_set.operable_construction.identifier]
659
+ mod_set.door_set.exterior_modifier = \
660
+ unique_mods[self.door_set.exterior_construction.identifier]
661
+ mod_set.door_set.interior_modifier = \
662
+ unique_mods[self.door_set.interior_construction.identifier]
663
+ mod_set.door_set.exterior_glass_modifier = \
664
+ unique_mods[self.door_set.exterior_glass_construction.identifier]
665
+ mod_set.door_set.interior_glass_modifier = \
666
+ unique_mods[self.door_set.interior_glass_construction.identifier]
667
+ mod_set.door_set.overhead_modifier = \
668
+ unique_mods[self.door_set.overhead_construction.identifier]
669
+ mod_set.shade_set.exterior_modifier = \
670
+ unique_mods[self.shade_construction.identifier]
671
+ mod_set.air_boundary_modifier = \
672
+ unique_mods[self.air_boundary_construction.identifier]
673
+ return mod_set
674
+
675
+ def _get_constr_from_set(self, face_type_set, boundary_condition):
676
+ """Get a specific construction from a face_type_set."""
677
+ if boundary_condition == 'Outdoors':
678
+ return face_type_set.exterior_construction
679
+ elif boundary_condition == 'Surface' or boundary_condition == 'Adiabatic':
680
+ return face_type_set.interior_construction
681
+ else:
682
+ return face_type_set.ground_construction
683
+
684
+ @staticmethod
685
+ def _get_subsets_from_abridged(data, constructions):
686
+ """Get subset objects from and abridged dictionary."""
687
+ wall_set = ConstructionSet._make_construction_subset(
688
+ data, WallConstructionSet(), 'wall_set', constructions)
689
+ floor_set = ConstructionSet._make_construction_subset(
690
+ data, FloorConstructionSet(), 'floor_set', constructions)
691
+ roof_ceiling_set = ConstructionSet._make_construction_subset(
692
+ data, RoofCeilingConstructionSet(), 'roof_ceiling_set', constructions)
693
+ aperture_set = ConstructionSet._make_aperture_subset(
694
+ data, ApertureConstructionSet(), constructions)
695
+ door_set = ConstructionSet._make_door_subset(
696
+ data, DoorConstructionSet(), constructions)
697
+ if 'shade_construction' in data and data['shade_construction'] is not None:
698
+ shade = constructions[data['shade_construction']]
699
+ else:
700
+ shade = None
701
+ if 'air_boundary_construction' in data and \
702
+ data['air_boundary_construction'] is not None:
703
+ air = constructions[data['air_boundary_construction']]
704
+ else:
705
+ air = None
706
+ return wall_set, floor_set, roof_ceiling_set, aperture_set, door_set, shade, air
707
+
708
+ @staticmethod
709
+ def _make_construction_subset(data, sub_set, sub_set_id, constructions):
710
+ """Make a wall set, floor set, or roof ceiling set from dictionary."""
711
+ if sub_set_id in data:
712
+ if 'exterior_construction' in data[sub_set_id] and \
713
+ data[sub_set_id]['exterior_construction'] is not None:
714
+ sub_set.exterior_construction = \
715
+ constructions[data[sub_set_id]['exterior_construction']]
716
+ if 'interior_construction' in data[sub_set_id] and \
717
+ data[sub_set_id]['interior_construction'] is not None:
718
+ sub_set.interior_construction = \
719
+ constructions[data[sub_set_id]['interior_construction']]
720
+ if 'ground_construction' in data[sub_set_id] and \
721
+ data[sub_set_id]['ground_construction'] is not None:
722
+ sub_set.ground_construction = \
723
+ constructions[data[sub_set_id]['ground_construction']]
724
+ return sub_set
725
+
726
+ @staticmethod
727
+ def _make_aperture_subset(data, sub_set, constructions):
728
+ """Make an ApertureConstructionSet from a dictionary."""
729
+ if 'aperture_set' in data:
730
+ if 'window_construction' in data['aperture_set'] and \
731
+ data['aperture_set']['window_construction'] is not None:
732
+ sub_set.window_construction = \
733
+ constructions[data['aperture_set']['window_construction']]
734
+ if 'interior_construction' in data['aperture_set'] and \
735
+ data['aperture_set']['interior_construction'] is not None:
736
+ sub_set.interior_construction = \
737
+ constructions[data['aperture_set']['interior_construction']]
738
+ if 'skylight_construction' in data['aperture_set'] and \
739
+ data['aperture_set']['skylight_construction'] is not None:
740
+ sub_set.skylight_construction = \
741
+ constructions[data['aperture_set']['skylight_construction']]
742
+ if 'operable_construction' in data['aperture_set'] and \
743
+ data['aperture_set']['operable_construction'] is not None:
744
+ sub_set.operable_construction = \
745
+ constructions[data['aperture_set']['operable_construction']]
746
+ return sub_set
747
+
748
+ @staticmethod
749
+ def _make_door_subset(data, sub_set, constructions):
750
+ """Make a DoorConstructionSet from dictionary."""
751
+ if 'door_set' in data:
752
+ if 'exterior_construction' in data['door_set'] and \
753
+ data['door_set']['exterior_construction'] is not None:
754
+ sub_set.exterior_construction = \
755
+ constructions[data['door_set']['exterior_construction']]
756
+ if 'interior_construction' in data['door_set'] and \
757
+ data['door_set']['interior_construction'] is not None:
758
+ sub_set.interior_construction = \
759
+ constructions[data['door_set']['interior_construction']]
760
+ if 'exterior_glass_construction' in data['door_set'] and \
761
+ data['door_set']['exterior_glass_construction'] is not None:
762
+ sub_set.exterior_glass_construction = \
763
+ constructions[data['door_set']['exterior_glass_construction']]
764
+ if 'interior_glass_construction' in data['door_set'] and \
765
+ data['door_set']['interior_glass_construction'] is not None:
766
+ sub_set.interior_glass_construction = \
767
+ constructions[data['door_set']['interior_glass_construction']]
768
+ if 'overhead_construction' in data['door_set'] and \
769
+ data['door_set']['overhead_construction'] is not None:
770
+ sub_set.overhead_construction = \
771
+ constructions[data['door_set']['overhead_construction']]
772
+ return sub_set
773
+
774
+ def ToString(self):
775
+ """Overwrite .NET ToString."""
776
+ return self.__repr__()
777
+
778
+ def __copy__(self):
779
+ new_obj = ConstructionSet(self.identifier,
780
+ self.wall_set.duplicate(),
781
+ self.floor_set.duplicate(),
782
+ self.roof_ceiling_set.duplicate(),
783
+ self.aperture_set.duplicate(),
784
+ self.door_set.duplicate(),
785
+ self._shade_construction,
786
+ self._air_boundary_construction)
787
+ new_obj._display_name = self._display_name
788
+ new_obj.user_data = None if self._user_data is None else self._user_data.copy()
789
+ return new_obj
790
+
791
+ def __key(self):
792
+ """A tuple based on the object properties, useful for hashing."""
793
+ return (self.identifier,) + tuple(hash(cnstr) for cnstr in self.constructions)
794
+
795
+ def __hash__(self):
796
+ return hash(self.__key())
797
+
798
+ def __eq__(self, other):
799
+ return isinstance(other, ConstructionSet) and self.__key() == other.__key()
800
+
801
+ def __ne__(self, other):
802
+ return not self.__eq__(other)
803
+
804
+ def __repr__(self):
805
+ return 'Energy Construction Set: {}'.format(self.display_name)
806
+
807
+
808
+ @lockable
809
+ class _FaceSetBase(object):
810
+ """Base class for the sets assigned to Faces.
811
+
812
+ This includesWallConstructionSet, FloorConstructionSet, and the
813
+ RoofCeilingConstructionSet.
814
+
815
+ Args:
816
+ exterior_construction: An OpaqueConstruction object for faces with an
817
+ Outdoors boundary condition.
818
+ interior_construction: An OpaqueConstruction object for faces with a
819
+ Surface or Adiabatic boundary condition.
820
+ ground_construction: : An OpaqueConstruction object for faces with a
821
+ Ground boundary condition.
822
+ """
823
+
824
+ __slots__ = ('_exterior_construction', '_interior_construction',
825
+ '_ground_construction', '_locked')
826
+
827
+ def __init__(self, exterior_construction=None, interior_construction=None,
828
+ ground_construction=None):
829
+ """Initialize set."""
830
+ self._locked = False # unlocked by default
831
+ self.exterior_construction = exterior_construction
832
+ self.interior_construction = interior_construction
833
+ self.ground_construction = ground_construction
834
+
835
+ @property
836
+ def exterior_construction(self):
837
+ """Get or set the OpaqueConstruction for exterior Faces."""
838
+ return self._exterior_construction
839
+
840
+ @exterior_construction.setter
841
+ def exterior_construction(self, value):
842
+ self._exterior_construction = value
843
+
844
+ @property
845
+ def interior_construction(self):
846
+ """Get or set the OpaqueConstruction for interior Faces."""
847
+ return self._interior_construction
848
+
849
+ @interior_construction.setter
850
+ def interior_construction(self, value):
851
+ self._interior_construction = value
852
+
853
+ @property
854
+ def ground_construction(self):
855
+ """Get or set the OpaqueConstruction for underground Faces."""
856
+ return self._ground_construction
857
+
858
+ @ground_construction.setter
859
+ def ground_construction(self, value):
860
+ self._ground_construction = value
861
+
862
+ @property
863
+ def constructions(self):
864
+ """List of all constructions contained within the set."""
865
+ return [self.exterior_construction,
866
+ self.interior_construction,
867
+ self.ground_construction]
868
+
869
+ @property
870
+ def modified_constructions(self):
871
+ """List of all constructions that are not defaulted within the set."""
872
+ constructions = []
873
+ if self._exterior_construction is not None:
874
+ constructions.append(self._exterior_construction)
875
+ if self._interior_construction is not None:
876
+ constructions.append(self._interior_construction)
877
+ if self._ground_construction is not None:
878
+ constructions.append(self._ground_construction)
879
+ return constructions
880
+
881
+ @property
882
+ def is_modified(self):
883
+ """Boolean noting whether any constructions are modified from the default."""
884
+ return self._exterior_construction is not None or \
885
+ self._interior_construction is not None or \
886
+ self._ground_construction is not None
887
+
888
+ @classmethod
889
+ def from_dict(cls, data):
890
+ """Create a SubSet from a dictionary.
891
+
892
+ Note that the dictionary must be a non-abridged version for this
893
+ classmethod to work.
894
+
895
+ Args:
896
+ data: Dictionary describing the Set of the object.
897
+ """
898
+ assert data['type'] == cls.__name__, \
899
+ 'Expected {}. Got {}.'.format(cls.__name__, data['type'])
900
+ extc = OpaqueConstruction.from_dict(data['exterior_construction']) \
901
+ if 'exterior_construction' in data and data['exterior_construction'] \
902
+ is not None else None
903
+ intc = OpaqueConstruction.from_dict(data['interior_construction']) \
904
+ if 'interior_construction' in data and data['interior_construction'] \
905
+ is not None else None
906
+ gndc = OpaqueConstruction.from_dict(data['ground_construction']) \
907
+ if 'ground_construction' in data and data['ground_construction'] \
908
+ is not None else None
909
+ return cls(extc, intc, gndc)
910
+
911
+ def to_dict(self, abridged=False, none_for_defaults=True):
912
+ """Get the Set as a dictionary.
913
+
914
+ Args:
915
+ abridged: Boolean noting whether detailed materials and construction
916
+ objects should be written into the ConstructionSet (False) or just
917
+ an abridged version (True). Default: False.
918
+ none_for_defaults: Boolean to note whether default constructions in the
919
+ set should be included in detail (False) or should be None (True).
920
+ Default: True.
921
+ """
922
+ base = {'type': self.__class__.__name__ + 'Abridged'} if abridged else \
923
+ {'type': self.__class__.__name__}
924
+ if none_for_defaults:
925
+ if abridged:
926
+ base['exterior_construction'] = self._exterior_construction.identifier \
927
+ if self._exterior_construction is not None else None
928
+ base['interior_construction'] = self._interior_construction.identifier \
929
+ if self._interior_construction is not None else None
930
+ base['ground_construction'] = self._ground_construction.identifier \
931
+ if self._ground_construction is not None else None
932
+ else:
933
+ base['exterior_construction'] = self._exterior_construction.to_dict() \
934
+ if self._exterior_construction is not None else None
935
+ base['interior_construction'] = self._interior_construction.to_dict() \
936
+ if self._interior_construction is not None else None
937
+ base['ground_construction'] = self._ground_construction.to_dict() \
938
+ if self._ground_construction is not None else None
939
+ else:
940
+ base['exterior_construction'] = self.exterior_construction.identifier \
941
+ if abridged else self.exterior_construction.to_dict()
942
+ base['interior_construction'] = self.interior_construction.identifier \
943
+ if abridged else self.exterior_construction.to_dict()
944
+ base['ground_construction'] = self.ground_construction.identifier \
945
+ if abridged else self.exterior_construction.to_dict()
946
+ return base
947
+
948
+ def duplicate(self):
949
+ """Get a copy of this set."""
950
+ return self.__copy__()
951
+
952
+ def _check_construction(self, value):
953
+ """Check an OpaqueConstruction before assigning it."""
954
+ assert isinstance(value, OpaqueConstruction), \
955
+ 'Expected OpaqueConstruction. Got {}'.format(type(value))
956
+ value.lock() # lock editing in case construction has multiple references
957
+
958
+ def ToString(self):
959
+ """Overwrite .NET ToString."""
960
+ return self.__repr__()
961
+
962
+ def __len__(self):
963
+ return 3
964
+
965
+ def __iter__(self):
966
+ return iter(self.constructions)
967
+
968
+ def __copy__(self):
969
+ new_obj = self.__class__(
970
+ self._exterior_construction, self._interior_construction,
971
+ self._ground_construction)
972
+ return new_obj
973
+
974
+ def __repr__(self):
975
+ return 'Base Face Set'
976
+
977
+
978
+ @lockable
979
+ class WallConstructionSet(_FaceSetBase):
980
+ """Set containing all energy constructions needed to for an energy model's Walls.
981
+
982
+ Properties:
983
+ * exterior_construction
984
+ * interior_construction
985
+ * ground_construction
986
+ * constructions
987
+ * modified_constructions
988
+ * is_modified
989
+ """
990
+ __slots__ = ()
991
+
992
+ @property
993
+ def exterior_construction(self):
994
+ """Get or set the OpaqueConstruction for exterior walls."""
995
+ if self._exterior_construction is None:
996
+ return _lib.generic_exterior_wall
997
+ return self._exterior_construction
998
+
999
+ @exterior_construction.setter
1000
+ def exterior_construction(self, value):
1001
+ if value is not None:
1002
+ self._check_construction(value)
1003
+ self._exterior_construction = value
1004
+
1005
+ @property
1006
+ def interior_construction(self):
1007
+ """Get or set the OpaqueConstruction for interior walls."""
1008
+ if self._interior_construction is None:
1009
+ return _lib.generic_interior_wall
1010
+ return self._interior_construction
1011
+
1012
+ @interior_construction.setter
1013
+ def interior_construction(self, value):
1014
+ if value is not None:
1015
+ self._check_construction(value)
1016
+ self._interior_construction = value
1017
+
1018
+ @property
1019
+ def ground_construction(self):
1020
+ """Get or set the OpaqueConstruction for underground walls."""
1021
+ if self._ground_construction is None:
1022
+ return _lib.generic_underground_wall
1023
+ return self._ground_construction
1024
+
1025
+ @ground_construction.setter
1026
+ def ground_construction(self, value):
1027
+ if value is not None:
1028
+ self._check_construction(value)
1029
+ self._ground_construction = value
1030
+
1031
+ def __repr__(self):
1032
+ return 'Wall Construction Set: [Exterior: {}] [Interior: {}]' \
1033
+ ' [Ground: {}]'.format(self.exterior_construction.display_name,
1034
+ self.interior_construction.display_name,
1035
+ self.ground_construction.display_name)
1036
+
1037
+
1038
+ @lockable
1039
+ class FloorConstructionSet(_FaceSetBase):
1040
+ """Set containing all energy constructions needed to for an energy model's Floors.
1041
+
1042
+ Properties:
1043
+ * exterior_construction
1044
+ * interior_construction
1045
+ * ground_construction
1046
+ * constructions
1047
+ * modified_constructions
1048
+ * is_modified
1049
+ """
1050
+ __slots__ = ()
1051
+
1052
+ @property
1053
+ def exterior_construction(self):
1054
+ """Get or set the OpaqueConstruction for exterior-exposed floors."""
1055
+ if self._exterior_construction is None:
1056
+ return _lib.generic_exposed_floor
1057
+ return self._exterior_construction
1058
+
1059
+ @exterior_construction.setter
1060
+ def exterior_construction(self, value):
1061
+ if value is not None:
1062
+ self._check_construction(value)
1063
+ self._exterior_construction = value
1064
+
1065
+ @property
1066
+ def interior_construction(self):
1067
+ """Get or set the OpaqueConstruction for interior floors."""
1068
+ if self._interior_construction is None:
1069
+ return _lib.generic_interior_floor
1070
+ return self._interior_construction
1071
+
1072
+ @interior_construction.setter
1073
+ def interior_construction(self, value):
1074
+ if value is not None:
1075
+ self._check_construction(value)
1076
+ self._interior_construction = value
1077
+
1078
+ @property
1079
+ def ground_construction(self):
1080
+ """Get or set the OpaqueConstruction for ground-contact floor slabs."""
1081
+ if self._ground_construction is None:
1082
+ return _lib.generic_ground_slab
1083
+ return self._ground_construction
1084
+
1085
+ @ground_construction.setter
1086
+ def ground_construction(self, value):
1087
+ if value is not None:
1088
+ self._check_construction(value)
1089
+ self._ground_construction = value
1090
+
1091
+ def __repr__(self):
1092
+ return 'Floor Construction Set: [Exterior: {}] [Interior: {}]' \
1093
+ ' [Ground: {}]'.format(self.exterior_construction.display_name,
1094
+ self.interior_construction.display_name,
1095
+ self.ground_construction.display_name)
1096
+
1097
+
1098
+ @lockable
1099
+ class RoofCeilingConstructionSet(_FaceSetBase):
1100
+ """Set containing all energy constructions needed to for an energy model's Roofs.
1101
+
1102
+ Properties:
1103
+ * exterior_construction
1104
+ * interior_construction
1105
+ * ground_construction
1106
+ * constructions
1107
+ * modified_constructions
1108
+ * is_modified
1109
+ """
1110
+ __slots__ = ()
1111
+
1112
+ @property
1113
+ def exterior_construction(self):
1114
+ """Get or set the OpaqueConstruction for exterior roofs."""
1115
+ if self._exterior_construction is None:
1116
+ return _lib.generic_roof
1117
+ return self._exterior_construction
1118
+
1119
+ @exterior_construction.setter
1120
+ def exterior_construction(self, value):
1121
+ if value is not None:
1122
+ self._check_construction(value)
1123
+ self._exterior_construction = value
1124
+
1125
+ @property
1126
+ def interior_construction(self):
1127
+ """Get or set the OpaqueConstruction for interior ceilings."""
1128
+ if self._interior_construction is None:
1129
+ return _lib.generic_interior_ceiling
1130
+ return self._interior_construction
1131
+
1132
+ @interior_construction.setter
1133
+ def interior_construction(self, value):
1134
+ if value is not None:
1135
+ self._check_construction(value)
1136
+ self._interior_construction = value
1137
+
1138
+ @property
1139
+ def ground_construction(self):
1140
+ """Get or set the OpaqueConstruction for underground roofs."""
1141
+ if self._ground_construction is None:
1142
+ return _lib.generic_underground_roof
1143
+ return self._ground_construction
1144
+
1145
+ @ground_construction.setter
1146
+ def ground_construction(self, value):
1147
+ if value is not None:
1148
+ self._check_construction(value)
1149
+ self._ground_construction = value
1150
+
1151
+ def __repr__(self):
1152
+ return 'RoofCeiling Construction Set: [Exterior: {}] [Interior: {}]' \
1153
+ ' [Ground: {}]'.format(self.exterior_construction.display_name,
1154
+ self.interior_construction.display_name,
1155
+ self.ground_construction.display_name)
1156
+
1157
+
1158
+ @lockable
1159
+ class ApertureConstructionSet(object):
1160
+ """Set containing all constructions needed to for an energy model's Apertures.
1161
+
1162
+ Args:
1163
+ window_construction: A WindowConstruction object for apertures
1164
+ with an Outdoors boundary condition, False is_operable property,
1165
+ and a Wall face type for their parent face.
1166
+ interior_construction: A WindowConstruction object for all apertures
1167
+ with a Surface boundary condition.
1168
+ skylight_construction: : A WindowConstruction object for apertures with a
1169
+ Outdoors boundary condition, False is_operable property, and a
1170
+ RoofCeiling or Floor face type for their parent face.
1171
+ operable_construction: A WindowConstruction object for all apertures
1172
+ with an Outdoors boundary condition and True is_operable property.
1173
+
1174
+ Properties:
1175
+ * window_construction
1176
+ * interior_construction
1177
+ * skylight_construction
1178
+ * operable_construction
1179
+ * constructions
1180
+ * modified_constructions
1181
+ * is_modified
1182
+ """
1183
+ __slots__ = ('_window_construction', '_interior_construction',
1184
+ '_skylight_construction', '_operable_construction', '_locked')
1185
+
1186
+ def __init__(self, window_construction=None, interior_construction=None,
1187
+ skylight_construction=None, operable_construction=None):
1188
+ """Initialize aperture set."""
1189
+ self._locked = False # unlocked by default
1190
+ self.window_construction = window_construction
1191
+ self.interior_construction = interior_construction
1192
+ self.skylight_construction = skylight_construction
1193
+ self.operable_construction = operable_construction
1194
+
1195
+ @property
1196
+ def window_construction(self):
1197
+ """Get or set the WindowConstruction for exterior fixed windows in walls."""
1198
+ if self._window_construction is None:
1199
+ return _lib.generic_double_pane
1200
+ return self._window_construction
1201
+
1202
+ @window_construction.setter
1203
+ def window_construction(self, value):
1204
+ if value is not None:
1205
+ self._check_window_construction(value)
1206
+ self._window_construction = value
1207
+
1208
+ @property
1209
+ def interior_construction(self):
1210
+ """Get or set the WindowConstruction for all interior apertures."""
1211
+ if self._interior_construction is None:
1212
+ return _lib.generic_single_pane
1213
+ return self._interior_construction
1214
+
1215
+ @interior_construction.setter
1216
+ def interior_construction(self, value):
1217
+ if value is not None:
1218
+ self._check_window_construction(value)
1219
+ self._interior_construction = value
1220
+
1221
+ @property
1222
+ def skylight_construction(self):
1223
+ """Get or set the WindowConstruction for exterior fixed windows in roofs."""
1224
+ if self._skylight_construction is None:
1225
+ return _lib.generic_double_pane
1226
+ return self._skylight_construction
1227
+
1228
+ @skylight_construction.setter
1229
+ def skylight_construction(self, value):
1230
+ if value is not None:
1231
+ self._check_window_construction(value)
1232
+ self._skylight_construction = value
1233
+
1234
+ @property
1235
+ def operable_construction(self):
1236
+ """Get or set the WindowConstruction for all exterior operable windows."""
1237
+ if self._operable_construction is None:
1238
+ return _lib.generic_double_pane
1239
+ return self._operable_construction
1240
+
1241
+ @operable_construction.setter
1242
+ def operable_construction(self, value):
1243
+ if value is not None:
1244
+ self._check_window_construction(value)
1245
+ self._operable_construction = value
1246
+
1247
+ @property
1248
+ def constructions(self):
1249
+ """List of all constructions contained within the set."""
1250
+ return [self.window_construction,
1251
+ self.interior_construction,
1252
+ self.skylight_construction,
1253
+ self.operable_construction]
1254
+
1255
+ @property
1256
+ def modified_constructions(self):
1257
+ """List of all constructions that are not defaulted within the set."""
1258
+ constructions = []
1259
+ if self._window_construction is not None:
1260
+ constructions.append(self._window_construction)
1261
+ if self._interior_construction is not None:
1262
+ constructions.append(self._interior_construction)
1263
+ if self._skylight_construction is not None:
1264
+ constructions.append(self._skylight_construction)
1265
+ if self._operable_construction is not None:
1266
+ constructions.append(self._operable_construction)
1267
+ return constructions
1268
+
1269
+ @property
1270
+ def is_modified(self):
1271
+ """Boolean noting whether any constructions are modified from the default."""
1272
+ return self._window_construction is not None or \
1273
+ self._interior_construction is not None or \
1274
+ self._skylight_construction is not None or \
1275
+ self._operable_construction is not None
1276
+
1277
+ @classmethod
1278
+ def from_dict(cls, data):
1279
+ """Create a ApertureConstructionSet from a dictionary.
1280
+
1281
+ Note that the dictionary must be a non-abridged version for this
1282
+ classmethod to work.
1283
+
1284
+ Args:
1285
+ data: Dictionary describing the Set of the object.
1286
+ """
1287
+ assert data['type'] == 'ApertureConstructionSet', \
1288
+ 'Expected ApertureConstructionSet. Got {}.'.format(data['type'])
1289
+ winc = dict_to_construction(data['window_construction']) \
1290
+ if 'window_construction' in data and data['window_construction'] \
1291
+ is not None else None
1292
+ intc = dict_to_construction(data['interior_construction']) \
1293
+ if 'interior_construction' in data and data['interior_construction'] \
1294
+ is not None else None
1295
+ skyc = dict_to_construction(data['skylight_construction']) \
1296
+ if 'skylight_construction' in data and data['skylight_construction'] \
1297
+ is not None else None
1298
+ opc = dict_to_construction(data['operable_construction'])\
1299
+ if 'operable_construction' in data and data['operable_construction'] \
1300
+ is not None else None
1301
+ return cls(winc, intc, skyc, opc)
1302
+
1303
+ def to_dict(self, abridged=False, none_for_defaults=True):
1304
+ """Get ApertureConstructionSet as a dictionary.
1305
+
1306
+ Args:
1307
+ abridged: Boolean noting whether detailed materials and construction
1308
+ objects should be written into the ConstructionSet (False) or just
1309
+ an abridged version (True). Default: False.
1310
+ none_for_defaults: Boolean to note whether default constructions in the
1311
+ set should be included in detail (False) or should be None (True).
1312
+ Default: True.
1313
+ """
1314
+ base = {'type': 'ApertureConstructionSetAbridged'} if abridged \
1315
+ else {'type': 'ApertureConstructionSet'}
1316
+ if none_for_defaults:
1317
+ if abridged:
1318
+ base['window_construction'] = self._window_construction.identifier if \
1319
+ self._window_construction is not None else None
1320
+ base['interior_construction'] = \
1321
+ self._interior_construction.identifier if \
1322
+ self._interior_construction is not None else None
1323
+ base['skylight_construction'] = \
1324
+ self._skylight_construction.identifier if \
1325
+ self._skylight_construction is not None else None
1326
+ base['operable_construction'] = \
1327
+ self._operable_construction.identifier if \
1328
+ self._operable_construction is not None else None
1329
+ else:
1330
+ base['window_construction'] = self._window_construction.to_dict() \
1331
+ if self._window_construction is not None else None
1332
+ base['interior_construction'] = self._interior_construction.to_dict() \
1333
+ if self._interior_construction is not None else None
1334
+ base['skylight_construction'] = self._skylight_construction.to_dict() \
1335
+ if self._skylight_construction is not None else None
1336
+ base['operable_construction'] = \
1337
+ self._operable_construction.to_dict() if \
1338
+ self._operable_construction is not None else None
1339
+ else:
1340
+ base['window_construction'] = self.window_construction.identifier if \
1341
+ abridged else self.window_construction.to_dict()
1342
+ base['interior_construction'] = self.interior_construction.identifier if \
1343
+ abridged else self.interior_construction.to_dict()
1344
+ base['skylight_construction'] = self.skylight_construction.identifier if \
1345
+ abridged else self.skylight_construction.to_dict()
1346
+ base['operable_construction'] = self.operable_construction.identifier if \
1347
+ abridged else self.operable_construction.to_dict()
1348
+ return base
1349
+
1350
+ def duplicate(self):
1351
+ """Get a copy of this set."""
1352
+ return self.__copy__()
1353
+
1354
+ def _check_window_construction(self, value):
1355
+ """Check that a construction is valid before assigning it."""
1356
+ val_w = (WindowConstruction, WindowConstructionShade, WindowConstructionDynamic)
1357
+ assert isinstance(value, val_w), \
1358
+ 'Expected Window Construction. Got {}'.format(type(value))
1359
+ value.lock() # lock editing in case construction has multiple references
1360
+
1361
+ def ToString(self):
1362
+ """Overwrite .NET ToString."""
1363
+ return self.__repr__()
1364
+
1365
+ def __len__(self):
1366
+ return 4
1367
+
1368
+ def __iter__(self):
1369
+ return iter(self.constructions)
1370
+
1371
+ def __copy__(self):
1372
+ new_obj = self.__class__(
1373
+ self._window_construction, self._interior_construction,
1374
+ self._skylight_construction, self._operable_construction
1375
+ )
1376
+ return new_obj
1377
+
1378
+ def __repr__(self):
1379
+ return 'Aperture Construction Set: [Window: {}] [Interior: {}]' \
1380
+ ' [Skylight: {}] [Operable: {}]'.format(
1381
+ self.window_construction.display_name,
1382
+ self.interior_construction.display_name,
1383
+ self.skylight_construction.display_name,
1384
+ self.operable_construction.display_name)
1385
+
1386
+
1387
+ @lockable
1388
+ class DoorConstructionSet(object):
1389
+ """Set containing all energy constructions needed to for an energy model's Roofs.
1390
+
1391
+ Args:
1392
+ exterior_construction: An OpaqueConstruction object for opaque doors with an
1393
+ Outdoors boundary condition and a Wall face type for their parent face.
1394
+ interior_construction: An OpaqueConstruction object for all opaque doors
1395
+ with a Surface boundary condition.
1396
+ exterior_glass_construction: A WindowConstruction object for all glass
1397
+ doors with an Outdoors boundary condition.
1398
+ interior_glass_construction: A WindowConstruction object for all glass
1399
+ doors with a Surface boundary condition.
1400
+ overhead_construction: An OpaqueConstruction object for opaque doors with
1401
+ an Outdoors boundary condition and a RoofCeiling or Floor face type for
1402
+ their parent face.
1403
+
1404
+ Properties:
1405
+ * exterior_construction
1406
+ * interior_construction
1407
+ * exterior_glass_construction
1408
+ * interior_glass_construction
1409
+ * overhead_construction
1410
+ * constructions
1411
+ * modified_constructions
1412
+ * is_modified
1413
+ """
1414
+ __slots__ = ('_exterior_construction', '_interior_construction',
1415
+ '_exterior_glass_construction', '_interior_glass_construction',
1416
+ '_overhead_construction', '_locked')
1417
+
1418
+ def __init__(self, exterior_construction=None, interior_construction=None,
1419
+ exterior_glass_construction=None, interior_glass_construction=None,
1420
+ overhead_construction=None):
1421
+ """Initialize door set."""
1422
+ self._locked = False # unlocked by default
1423
+ self.exterior_construction = exterior_construction
1424
+ self.interior_construction = interior_construction
1425
+ self.exterior_glass_construction = exterior_glass_construction
1426
+ self.interior_glass_construction = interior_glass_construction
1427
+ self.overhead_construction = overhead_construction
1428
+
1429
+ @property
1430
+ def exterior_construction(self):
1431
+ """Get or set the OpaqueConstruction for exterior opaque doors in walls."""
1432
+ if self._exterior_construction is None:
1433
+ return _lib.generic_exterior_door
1434
+ return self._exterior_construction
1435
+
1436
+ @exterior_construction.setter
1437
+ def exterior_construction(self, value):
1438
+ if value is not None:
1439
+ self._check_construction(value)
1440
+ self._exterior_construction = value
1441
+
1442
+ @property
1443
+ def interior_construction(self):
1444
+ """Get or set the OpaqueConstruction for all interior opaque doors."""
1445
+ if self._interior_construction is None:
1446
+ return _lib.generic_interior_door
1447
+ return self._interior_construction
1448
+
1449
+ @interior_construction.setter
1450
+ def interior_construction(self, value):
1451
+ if value is not None:
1452
+ self._check_construction(value)
1453
+ self._interior_construction = value
1454
+
1455
+ @property
1456
+ def exterior_glass_construction(self):
1457
+ """Get or set the WindowConstruction for exterior glass doors."""
1458
+ if self._exterior_glass_construction is None:
1459
+ return _lib.generic_double_pane
1460
+ return self._exterior_glass_construction
1461
+
1462
+ @exterior_glass_construction.setter
1463
+ def exterior_glass_construction(self, value):
1464
+ if value is not None:
1465
+ self._check_window_construction(value)
1466
+ self._exterior_glass_construction = value
1467
+
1468
+ @property
1469
+ def interior_glass_construction(self):
1470
+ """Get or set the WindowConstruction for all interior glass doors."""
1471
+ if self._interior_glass_construction is None:
1472
+ return _lib.generic_single_pane
1473
+ return self._interior_glass_construction
1474
+
1475
+ @interior_glass_construction.setter
1476
+ def interior_glass_construction(self, value):
1477
+ if value is not None:
1478
+ self._check_window_construction(value)
1479
+ self._interior_glass_construction = value
1480
+
1481
+ @property
1482
+ def overhead_construction(self):
1483
+ """Get or set the OpaqueConstruction for exterior opaque doors in roofs/floors.
1484
+ """
1485
+ if self._overhead_construction is None:
1486
+ return _lib.generic_exterior_door
1487
+ return self._overhead_construction
1488
+
1489
+ @overhead_construction.setter
1490
+ def overhead_construction(self, value):
1491
+ if value is not None:
1492
+ self._check_construction(value)
1493
+ self._overhead_construction = value
1494
+
1495
+ @property
1496
+ def constructions(self):
1497
+ """List of all constructions contained within the set."""
1498
+ return [self.exterior_construction,
1499
+ self.interior_construction,
1500
+ self.exterior_glass_construction,
1501
+ self.interior_glass_construction,
1502
+ self.overhead_construction]
1503
+
1504
+ @property
1505
+ def modified_constructions(self):
1506
+ """List of all constructions that are not defaulted within the set."""
1507
+ constructions = []
1508
+ if self._exterior_construction is not None:
1509
+ constructions.append(self._exterior_construction)
1510
+ if self._interior_construction is not None:
1511
+ constructions.append(self._interior_construction)
1512
+ if self._exterior_glass_construction is not None:
1513
+ constructions.append(self._exterior_glass_construction)
1514
+ if self._interior_glass_construction is not None:
1515
+ constructions.append(self._interior_glass_construction)
1516
+ if self._overhead_construction is not None:
1517
+ constructions.append(self._overhead_construction)
1518
+ return constructions
1519
+
1520
+ @property
1521
+ def is_modified(self):
1522
+ """Boolean noting whether any constructions are modified from the default."""
1523
+ return self._exterior_construction is not None or \
1524
+ self._interior_construction is not None or \
1525
+ self._exterior_glass_construction is not None or \
1526
+ self._interior_glass_construction is not None or \
1527
+ self._overhead_construction is None
1528
+
1529
+ @classmethod
1530
+ def from_dict(cls, data):
1531
+ """Create a DoorConstructionSet from a dictionary.
1532
+
1533
+ Note that the dictionary must be a non-abridged version for this
1534
+ classmethod to work.
1535
+
1536
+ Args:
1537
+ data: Dictionary describing the Set of the object.
1538
+ """
1539
+ assert data['type'] == 'DoorConstructionSet', \
1540
+ 'Expected DoorConstructionSet. Got {}.'.format(data['type'])
1541
+ extc = OpaqueConstruction.from_dict(data['exterior_construction']) \
1542
+ if 'exterior_construction' in data and data['exterior_construction'] \
1543
+ is not None else None
1544
+ intc = OpaqueConstruction.from_dict(data['interior_construction']) \
1545
+ if 'interior_construction' in data and data['interior_construction'] \
1546
+ is not None else None
1547
+ egc = dict_to_construction(data['exterior_glass_construction']) \
1548
+ if 'exterior_glass_construction' in data and \
1549
+ data['exterior_glass_construction'] is not None else None
1550
+ igc = dict_to_construction(data['interior_glass_construction']) \
1551
+ if 'interior_glass_construction' in data and \
1552
+ data['interior_glass_construction'] is not None else None
1553
+ ohc = OpaqueConstruction.from_dict(data['overhead_construction']) \
1554
+ if 'overhead_construction' in data and data['overhead_construction'] \
1555
+ is not None else None
1556
+
1557
+ return cls(extc, intc, egc, igc, ohc)
1558
+
1559
+ def to_dict(self, abridged=False, none_for_defaults=True):
1560
+ """Get the DoorConstructionSet as a dictionary.
1561
+
1562
+ Args:
1563
+ abridged: Boolean noting whether detailed materials and construction
1564
+ objects should be written into the ConstructionSet (False) or just
1565
+ an abridged version (True). Default: False.
1566
+ none_for_defaults: Boolean to note whether default constructions in the
1567
+ set should be included in detail (False) or should be None (True).
1568
+ Default: True.
1569
+ """
1570
+ base = {'type': 'DoorConstructionSetAbridged'} if abridged \
1571
+ else {'type': 'DoorConstructionSet'}
1572
+ if none_for_defaults:
1573
+ if abridged:
1574
+ base['exterior_construction'] = self._exterior_construction.identifier \
1575
+ if self._exterior_construction is not None else None
1576
+ base['interior_construction'] = self._interior_construction.identifier \
1577
+ if self._interior_construction is not None else None
1578
+ base['exterior_glass_construction'] = \
1579
+ self._exterior_glass_construction.identifier if \
1580
+ self._exterior_glass_construction is not None else None
1581
+ base['interior_glass_construction'] = \
1582
+ self._interior_glass_construction.identifier if \
1583
+ self._interior_glass_construction is not None else None
1584
+ base['overhead_construction'] = self._overhead_construction.identifier \
1585
+ if self._overhead_construction is not None else None
1586
+ else:
1587
+ base['exterior_construction'] = self._exterior_construction.to_dict() \
1588
+ if self._exterior_construction is not None else None
1589
+ base['interior_construction'] = self._interior_construction.to_dict() \
1590
+ if self._interior_construction is not None else None
1591
+ base['exterior_glass_construction'] = \
1592
+ self._exterior_glass_construction.to_dict() \
1593
+ if self._exterior_glass_construction is not None else None
1594
+ base['interior_glass_construction'] = \
1595
+ self._interior_glass_construction.to_dict() if \
1596
+ self._interior_glass_construction is not None else None
1597
+ base['overhead_construction'] = self._overhead_construction.to_dict() \
1598
+ if self._overhead_construction is not None else None
1599
+ else:
1600
+ base['exterior_construction'] = self.exterior_construction.identifier if \
1601
+ abridged else self.exterior_construction.to_dict()
1602
+ base['interior_construction'] = self.interior_construction.identifier if \
1603
+ abridged else self.interior_construction.to_dict()
1604
+ base['exterior_glass_construction'] = \
1605
+ self.exterior_glass_construction.identifier if \
1606
+ abridged else self.exterior_glass_construction.to_dict()
1607
+ base['interior_glass_construction'] = \
1608
+ self.interior_glass_construction.identifier if \
1609
+ abridged else self.interior_glass_construction.to_dict()
1610
+ base['overhead_construction'] = self.overhead_construction.identifier if \
1611
+ abridged else self.overhead_construction.to_dict()
1612
+ return base
1613
+
1614
+ def duplicate(self):
1615
+ """Get a copy of this set."""
1616
+ return self.__copy__()
1617
+
1618
+ def _check_construction(self, value):
1619
+ """Check an OpaqueConstruction before assigning it."""
1620
+ assert isinstance(value, OpaqueConstruction), \
1621
+ 'Expected OpaqueConstruction. Got {}'.format(type(value))
1622
+ value.lock() # lock editing in case construction has multiple references
1623
+
1624
+ def _check_window_construction(self, value):
1625
+ """Check that a construction is valid before assigning it."""
1626
+ val_w = (WindowConstruction, WindowConstructionShade, WindowConstructionDynamic)
1627
+ assert isinstance(value, val_w), \
1628
+ 'Expected Window Construction. Got {}'.format(type(value))
1629
+ value.lock() # lock editing in case construction has multiple references
1630
+
1631
+ def ToString(self):
1632
+ """Overwrite .NET ToString."""
1633
+ return self.__repr__()
1634
+
1635
+ def __len__(self):
1636
+ return 5
1637
+
1638
+ def __iter__(self):
1639
+ return iter(self.constructions)
1640
+
1641
+ def __copy__(self):
1642
+ new_obj = self.__class__(
1643
+ self._exterior_construction, self._interior_construction,
1644
+ self._exterior_glass_construction, self._interior_glass_construction,
1645
+ self._overhead_construction)
1646
+ return new_obj
1647
+
1648
+ def __repr__(self):
1649
+ return 'Door Construction Set: [Exterior: {}] [Interior: {}]' \
1650
+ ' [Exterior Glass: {}] [Interior Glass: {}] [Overhead: {}]'.format(
1651
+ self.exterior_construction.display_name,
1652
+ self.interior_construction.display_name,
1653
+ self.exterior_glass_construction.display_name,
1654
+ self.interior_glass_construction.display_name,
1655
+ self.overhead_construction.display_name)