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 @@
1
+ """honeybee-energy properties."""
@@ -0,0 +1,333 @@
1
+ # coding=utf-8
2
+ """Aperture Energy Properties."""
3
+ from honeybee.units import conversion_factor_to_meters
4
+ from honeybee.checkdup import is_equivalent
5
+
6
+ from ..construction.dictutil import dict_to_construction
7
+ from ..material.glazing import EnergyWindowMaterialSimpleGlazSys
8
+ from ..construction.window import WindowConstruction
9
+ from ..construction.windowshade import WindowConstructionShade
10
+ from ..construction.dynamic import WindowConstructionDynamic
11
+ from ..ventcool.opening import VentilationOpening
12
+ from ..lib.constructionsets import generic_construction_set
13
+
14
+
15
+ class ApertureEnergyProperties(object):
16
+ """Energy Properties for Honeybee Aperture.
17
+
18
+ Args:
19
+ host: A honeybee_core Aperture object that hosts these properties.
20
+ construction: An optional Honeybee WindowConstruction, WindowConstructionShade
21
+ or WindowConstructionDynamic object for the aperture. If None, it will
22
+ be set by the parent Room ConstructionSet or the the Honeybee default
23
+ generic ConstructionSet.
24
+ vent_opening: An optional VentilationOpening to specify the operable
25
+ portion of the Aperture. (Default: None).
26
+
27
+ Properties:
28
+ * host
29
+ * construction
30
+ * vent_opening
31
+ * is_construction_set_on_object
32
+ """
33
+
34
+ __slots__ = ('_host', '_construction', '_vent_opening')
35
+
36
+ def __init__(self, host, construction=None, vent_opening=None):
37
+ """Initialize Aperture energy properties."""
38
+ self._host = host
39
+ self.construction = construction
40
+ self.vent_opening = vent_opening
41
+
42
+ @property
43
+ def host(self):
44
+ """Get the Aperture object hosting these properties."""
45
+ return self._host
46
+
47
+ @property
48
+ def construction(self):
49
+ """Get or set Aperture Construction.
50
+
51
+ If the Construction is not set on the aperture-level, then it will be assigned
52
+ based on the ConstructionSet assigned to the parent Room. If there is no
53
+ parent Room or the parent Room's ConstructionSet has no construction for
54
+ the aperture, it will be assigned using the honeybee default generic
55
+ construction set.
56
+ """
57
+ if self._construction: # set by user
58
+ return self._construction
59
+ elif self._host.has_parent and self._host.parent.has_parent: # set by zone
60
+ constr_set = self._host.parent.parent.properties.energy.construction_set
61
+ return constr_set.get_aperture_construction(
62
+ self._host.boundary_condition.name, self._host.is_operable,
63
+ self._host.parent.type.name)
64
+ elif self._host.has_parent: # generic but influenced by parent face
65
+ return generic_construction_set.get_aperture_construction(
66
+ self._host.boundary_condition.name, self._host.is_operable,
67
+ self._host.parent.type.name)
68
+ else:
69
+ return generic_construction_set.get_aperture_construction(
70
+ self._host.boundary_condition.name, self._host.is_operable, 'Wall')
71
+
72
+ @construction.setter
73
+ def construction(self, value):
74
+ if value is not None:
75
+ vw = (WindowConstruction, WindowConstructionShade, WindowConstructionDynamic)
76
+ assert isinstance(value, vw), 'Expected WindowConstruction, ' \
77
+ 'WindowConstructionShade or WindowConstructionDynamic for aperture.' \
78
+ ' Got {}'.format(type(value))
79
+ value.lock() # lock editing in case construction has multiple references
80
+ self._construction = value
81
+
82
+ @property
83
+ def vent_opening(self):
84
+ """Get or set a VentilationOpening object to specify the operable portion."""
85
+ return self._vent_opening
86
+
87
+ @vent_opening.setter
88
+ def vent_opening(self, value):
89
+ if value is not None:
90
+ assert isinstance(value, VentilationOpening), 'Expected Ventilation' \
91
+ 'Opening for Aperture vent_opening. Got {}'.format(type(value))
92
+ assert self.host.is_operable, 'Aperture must have a "True" is_operable ' \
93
+ 'property in order to assign vent_opening energy properties.'
94
+ if value._parent is None:
95
+ value._parent = self.host
96
+ elif value._parent.identifier != self.host.identifier:
97
+ raise ValueError(
98
+ '{0} objects can be assigned to only one parent.\n{0} cannot be '
99
+ 'assigned to Aperture "{1}" since it is already assigned to "{2}".\n'
100
+ 'Try duplicating the object and then assign it.'.format(
101
+ 'VentilationOpening', self.host.identifier,
102
+ value._parent.identifier))
103
+ self._vent_opening = value
104
+
105
+ @property
106
+ def is_construction_set_on_object(self):
107
+ """Boolean noting if construction is assigned on the level of this Aperture.
108
+
109
+ This is opposed to having the construction assigned by a ConstructionSet.
110
+ """
111
+ return self._construction is not None
112
+
113
+ def r_factor(self, units='Meters'):
114
+ """Get the Aperture R-factor [m2-K/W] (including resistances for air films).
115
+
116
+ The air film resistances are computed using the orientation and height
117
+ of the Aperture geometry. If the window construction has a frame, the
118
+ geometry of the frame will also be accounted for.
119
+
120
+ Args:
121
+ units: Text for the units in which the Aperture geometry exists. These
122
+ will be used to correctly interpret the dimensions of the
123
+ geometry for heat flow calculation. (Default: Meters).
124
+ """
125
+ # compute the center-of-glass R-value
126
+ u_conv = conversion_factor_to_meters(units)
127
+ win_con = self._window_construction()
128
+ height = (self.host.max.z - self.host.min.z) * u_conv
129
+ height = 1 if height < 1 else height
130
+ _, r_vals = win_con.temperature_profile(
131
+ height=height, angle=abs(self.host.altitude - 90))
132
+ if not win_con.has_frame:
133
+ return sum(r_vals)
134
+ # if there is a frame, account for it in the final R-value
135
+ glass_u = (1 / sum(r_vals))
136
+ glass_area = (self.host.area * (u_conv ** 2))
137
+ if win_con.frame.edge_to_center_ratio != 1 and not \
138
+ isinstance(win_con.materials[0], EnergyWindowMaterialSimpleGlazSys):
139
+ edge_u = win_con.frame.edge_to_center_ratio * glass_u
140
+ edge_area = self.host.perimeter * u_conv * 0.06
141
+ cog_area = glass_area - edge_area
142
+ cog_area = 0 if cog_area < 0 else cog_area
143
+ total_area = cog_area + edge_area
144
+ glass_u = ((glass_u * cog_area) + (edge_u * edge_area)) / total_area
145
+ _, fr_r_vals = win_con.temperature_profile_frame(
146
+ angle=abs(self.host.altitude - 90))
147
+ frame_u = 1 / sum(fr_r_vals)
148
+ frame_area = (self.host.perimeter * u_conv * win_con.frame.width) + \
149
+ ((win_con.frame.width * u_conv) ** 2) * len(self.host.geometry)
150
+ assembly_area = glass_area + frame_area
151
+ total_u = ((glass_u * glass_area) + (frame_u * frame_area)) / assembly_area
152
+ return 1 / total_u
153
+
154
+ def u_factor(self, units='Meters'):
155
+ """Get the Aperture U-factor [W/m2-K] (including resistances for air films).
156
+
157
+ The air film resistances are computed using the orientation and height
158
+ of the Aperture geometry. If the window construction has a frame, the
159
+ geometry of the frame will also be accounted for.
160
+
161
+ Args:
162
+ units: Text for the units in which the Aperture geometry exists. These
163
+ will be used to correctly interpret the dimensions of the
164
+ geometry for heat flow calculation. (Default: Meters).
165
+ """
166
+ return 1 / self.r_factor(units)
167
+
168
+ def shgc(self, units='Meters'):
169
+ """Get the Aperture solar heat gain coefficient (SHGC).
170
+
171
+ If this construction is not a simple glazing system, this value is computed
172
+ by summing the transmitted and conducted portions of solar irradiance under
173
+ the NFRC summer conditions. The air film resistances are computed using
174
+ the orientation and height of the Aperture geometry. If the window
175
+ construction has a frame, the geometry of the frame will also be accounted for.
176
+
177
+ Args:
178
+ units: Text for the units in which the Aperture geometry exists. These
179
+ will be used to correctly interpret the dimensions of the
180
+ geometry for heat flow calculation. (Default: Meters).
181
+ """
182
+ win_con = self._window_construction()
183
+ if isinstance(win_con.materials[0], EnergyWindowMaterialSimpleGlazSys):
184
+ if not win_con.has_frame:
185
+ return win_con.materials[0].shgc
186
+ # compute the temperature profile
187
+ t_out, t_in, sol_irr = 32, 24, 783 # NFRC 2010 summer conditions
188
+ u_conv = conversion_factor_to_meters(units)
189
+ height = (self.host.max.z - self.host.min.z) * u_conv
190
+ height = 1 if height < 1 else height
191
+ _, r_vals = win_con.temperature_profile(
192
+ t_out, t_in, height=height, angle=abs(self.host.altitude - 90),
193
+ solar_irradiance=sol_irr)
194
+ heat_gen, transmitted = win_con._heat_gen_from_solar(sol_irr)
195
+ conducted = 0
196
+ r_factor = sum(r_vals)
197
+ for i, heat_g in enumerate(heat_gen):
198
+ if heat_g != 0:
199
+ conducted += heat_g * (1 - (sum(r_vals[i + 1:]) / r_factor))
200
+ if not win_con.has_frame:
201
+ return (transmitted + conducted) / sol_irr
202
+ else: # account for the frame conduction
203
+ _, r_values = win_con.temperature_profile_frame(
204
+ t_out, t_in, height=height, angle=abs(self.host.altitude - 90),
205
+ solar_irradiance=sol_irr)
206
+ heat_gen = [0, sol_irr * win_con.frame.solar_absorptance, 0]
207
+ frame_conducted = 0
208
+ r_factor = sum(r_values)
209
+ for i, heat_g in enumerate(heat_gen):
210
+ if heat_g != 0:
211
+ frame_conducted += heat_g * (1 - (sum(r_values[i + 1:]) / r_factor))
212
+ glass_area = (self.host.area * (u_conv ** 2))
213
+ frame_area = (self.host.perimeter * u_conv * win_con.frame.width) + \
214
+ ((win_con.frame.width * u_conv) ** 2) * len(self.host.geometry)
215
+ glass_trans = transmitted * glass_area
216
+ glass_conduct = conducted * glass_area
217
+ frame_conduct = frame_conducted * frame_area
218
+ total_irr = sol_irr * (glass_area + frame_area)
219
+ return (glass_trans + glass_conduct + frame_conduct) / total_irr
220
+
221
+ def reset_construction_to_set(self):
222
+ """Reset a construction assigned at the level of this Aperture to the default.
223
+
224
+ This means the Aperture's construction will be assigned by a ConstructionSet.
225
+ """
226
+ self._construction = None
227
+ for shade in self.host.shades:
228
+ shade.properties.energy.reset_construction_to_set()
229
+
230
+ @classmethod
231
+ def from_dict(cls, data, host):
232
+ """Create ApertureEnergyProperties from a dictionary.
233
+
234
+ Note that the dictionary must be a non-abridged version for this
235
+ classmethod to work.
236
+
237
+ Args:
238
+ data: A dictionary representation of ApertureEnergyProperties in the
239
+ format below.
240
+ host: A Aperture object that hosts these properties.
241
+
242
+ .. code-block:: python
243
+
244
+ {
245
+ "type": 'ApertureEnergyProperties',
246
+ "construction": {}, # Window Construction dictionary
247
+ "vent_opening": {} # VentilationOpening dict
248
+ }
249
+ """
250
+ assert data['type'] == 'ApertureEnergyProperties', \
251
+ 'Expected ApertureEnergyProperties. Got {}.'.format(data['type'])
252
+
253
+ new_prop = cls(host)
254
+ if 'construction' in data and data['construction'] is not None:
255
+ new_prop.construction = dict_to_construction(data['construction'])
256
+ if 'vent_opening' in data and data['vent_opening'] is not None:
257
+ new_prop.vent_opening = VentilationOpening.from_dict(data['vent_opening'])
258
+ return new_prop
259
+
260
+ def apply_properties_from_dict(self, abridged_data, constructions):
261
+ """Apply properties from a ApertureEnergyPropertiesAbridged dictionary.
262
+
263
+ Args:
264
+ abridged_data: A ApertureEnergyPropertiesAbridged dictionary (typically
265
+ coming from a Model).
266
+ constructions: A dictionary of constructions with constructions identifiers
267
+ as keys, which will be used to re-assign constructions.
268
+ """
269
+ if 'construction' in abridged_data and abridged_data['construction'] is not None:
270
+ try:
271
+ self.construction = constructions[abridged_data['construction']]
272
+ except KeyError:
273
+ raise ValueError('Aperture construction "{}" was not found in '
274
+ 'constructions.'.format(abridged_data['construction']))
275
+ if 'vent_opening' in abridged_data and abridged_data['vent_opening'] is not None:
276
+ self.vent_opening = \
277
+ VentilationOpening.from_dict(abridged_data['vent_opening'])
278
+
279
+ def to_dict(self, abridged=False):
280
+ """Return energy properties as a dictionary.
281
+
282
+ Args:
283
+ abridged: Boolean to note whether the full dictionary describing the
284
+ object should be returned (False) or just an abridged version (True).
285
+ Default: False.
286
+ """
287
+ base = {'energy': {}}
288
+ base['energy']['type'] = 'ApertureEnergyProperties' if not \
289
+ abridged else 'ApertureEnergyPropertiesAbridged'
290
+ if self._construction is not None:
291
+ base['energy']['construction'] = \
292
+ self._construction.identifier if abridged else \
293
+ self._construction.to_dict()
294
+ if self._vent_opening is not None:
295
+ base['energy']['vent_opening'] = self._vent_opening.to_dict()
296
+ return base
297
+
298
+ def duplicate(self, new_host=None):
299
+ """Get a copy of this object.
300
+
301
+ new_host: A new Aperture object that hosts these properties.
302
+ If None, the properties will be duplicated with the same host.
303
+ """
304
+ _host = new_host or self._host
305
+ vo = self._vent_opening.duplicate() if self._vent_opening is not None else None
306
+ return ApertureEnergyProperties(_host, self._construction, vo)
307
+
308
+ def is_equivalent(self, other):
309
+ """Check to see if these energy properties are equivalent to another object.
310
+
311
+ This will only be True if all properties match (except for the host) and
312
+ will otherwise be False.
313
+ """
314
+ if not is_equivalent(self._construction, other._construction):
315
+ return False
316
+ if not is_equivalent(self._vent_opening, other._vent_opening):
317
+ return False
318
+ return True
319
+
320
+ def _window_construction(self):
321
+ """Get the base window construction assigned to the aperture."""
322
+ win_con = self.construction
323
+ if isinstance(win_con, WindowConstructionShade):
324
+ win_con = win_con.window_construction
325
+ elif isinstance(win_con, WindowConstructionDynamic):
326
+ win_con = win_con.constructions[0]
327
+ return win_con
328
+
329
+ def ToString(self):
330
+ return self.__repr__()
331
+
332
+ def __repr__(self):
333
+ return 'Aperture Energy Properties: [host: {}]'.format(self.host.display_name)
@@ -0,0 +1,342 @@
1
+ # coding=utf-8
2
+ """Door Energy Properties."""
3
+ from honeybee.units import conversion_factor_to_meters
4
+ from honeybee.checkdup import is_equivalent
5
+
6
+ from ..construction.dictutil import dict_to_construction
7
+ from ..material.glazing import EnergyWindowMaterialSimpleGlazSys
8
+ from ..construction.opaque import OpaqueConstruction
9
+ from ..construction.window import WindowConstruction
10
+ from ..construction.windowshade import WindowConstructionShade
11
+ from ..construction.dynamic import WindowConstructionDynamic
12
+ from ..ventcool.opening import VentilationOpening
13
+ from ..lib.constructionsets import generic_construction_set
14
+
15
+
16
+ class DoorEnergyProperties(object):
17
+ """Energy Properties for Honeybee Door.
18
+
19
+ Args:
20
+ host_door: A honeybee_core Door object that hosts these properties.
21
+ construction: An optional Honeybee OpaqueConstruction or WindowConstruction
22
+ object for the door. Note that the host Door must have the is_glass
23
+ property set to True to assign a WindowConstruction,
24
+ WindowConstructionShade or WindowConstructionDynamic. If None, it
25
+ will be set by the parent Room ConstructionSet or the the Honeybee
26
+ default generic ConstructionSet.
27
+ vent_opening: An optional VentilationOpening to specify the operable
28
+ portion of the Door. (Default: None).
29
+
30
+ Properties:
31
+ * host
32
+ * construction
33
+ * vent_opening
34
+ * is_construction_set_on_object
35
+ """
36
+
37
+ __slots__ = ('_host', '_construction', '_vent_opening')
38
+
39
+ def __init__(self, host, construction=None, vent_opening=None):
40
+ """Initialize Door energy properties."""
41
+ self._host = host
42
+ self.construction = construction
43
+ self.vent_opening = vent_opening
44
+
45
+ @property
46
+ def host(self):
47
+ """Get the Door object hosting these properties."""
48
+ return self._host
49
+
50
+ @property
51
+ def construction(self):
52
+ """Get or set Door Construction.
53
+
54
+ If the Construction is not set on the door-level, then it will be assigned
55
+ based on the ConstructionSet assigned to the parent Room. If there is no
56
+ parent Room or the parent Room's ConstructionSet has no construction for
57
+ this type of door, it will be assigned using the honeybee default
58
+ generic construction set.
59
+ """
60
+ if self._construction: # set by user
61
+ return self._construction
62
+ elif self._host.has_parent and self._host.parent.has_parent: # set by room
63
+ constr_set = self._host.parent.parent.properties.energy.construction_set
64
+ return constr_set.get_door_construction(
65
+ self._host.boundary_condition.name, self._host.is_glass,
66
+ self._host.parent.type.name)
67
+ elif self._host.has_parent: # generic but influenced by parent face
68
+ return generic_construction_set.get_door_construction(
69
+ self._host.boundary_condition.name, self._host.is_glass,
70
+ self._host.parent.type.name)
71
+ else:
72
+ return generic_construction_set.get_door_construction(
73
+ self._host.boundary_condition.name, self._host.is_glass, 'Wall')
74
+
75
+ @construction.setter
76
+ def construction(self, value):
77
+ if value is not None:
78
+ if not self.host.is_glass:
79
+ assert isinstance(value, OpaqueConstruction), 'Expected ' \
80
+ 'OpaqueConstruction for door. Got {}'.format(type(value))
81
+ else:
82
+ vw = (WindowConstruction, WindowConstructionShade,
83
+ WindowConstructionDynamic)
84
+ assert isinstance(value, vw), 'Expected WindowConstruction, ' \
85
+ 'WindowConstructionShade or WindowConstructionDynamic for ' \
86
+ 'glass door. Got {}'.format(type(value))
87
+ value.lock() # lock editing in case construction has multiple references
88
+ self._construction = value
89
+
90
+ @property
91
+ def vent_opening(self):
92
+ """Get or set a VentilationOpening object to specify the operable portion."""
93
+ return self._vent_opening
94
+
95
+ @vent_opening.setter
96
+ def vent_opening(self, value):
97
+ if value is not None:
98
+ assert isinstance(value, VentilationOpening), 'Expected Ventilation' \
99
+ 'Opening for Door vent_opening. Got {}'.format(type(value))
100
+ if value._parent is None:
101
+ value._parent = self.host
102
+ elif value._parent.identifier != self.host.identifier:
103
+ raise ValueError(
104
+ '{0} objects can be assigned to only one parent.\n{0} cannot be '
105
+ 'assigned to Door "{1}" since it is already assigned to "{2}".\n'
106
+ 'Try duplicating the object and then assign it.'.format(
107
+ 'VentilationOpening', self.host.identifier,
108
+ value._parent.identifier))
109
+ self._vent_opening = value
110
+
111
+ @property
112
+ def is_construction_set_on_object(self):
113
+ """Boolean noting if construction is assigned on the level of this Door.
114
+
115
+ This is opposed to having the construction assigned by a ConstructionSet.
116
+ """
117
+ return self._construction is not None
118
+
119
+ def r_factor(self, units='Meters'):
120
+ """Get the Door R-factor [m2-K/W] (including air film resistance).
121
+
122
+ The air film resistances are computed using the orientation and height
123
+ of the Door geometry.
124
+
125
+ Args:
126
+ units: Text for the units in which the Door geometry exists. These
127
+ will be used to correctly interpret the dimensions of the
128
+ geometry for heat flow calculation. (Default: Meters).
129
+ """
130
+ u_conv = conversion_factor_to_meters(units)
131
+ win_con = self._window_construction()
132
+ height = (self.host.max.z - self.host.min.z) * u_conv
133
+ height = 1 if height < 1 else height
134
+ _, r_vals = win_con.temperature_profile(
135
+ height=height, angle=abs(self.host.altitude - 90))
136
+ if not win_con.has_frame:
137
+ return sum(r_vals)
138
+ # if there is a frame, account for it in the final R-value
139
+ glass_u = (1 / sum(r_vals))
140
+ glass_area = (self.host.area * (u_conv ** 2))
141
+ if win_con.frame.edge_to_center_ratio != 1 and not \
142
+ isinstance(win_con.materials[0], EnergyWindowMaterialSimpleGlazSys):
143
+ edge_u = win_con.frame.edge_to_center_ratio * glass_u
144
+ edge_area = self.host.perimeter * u_conv * 0.06
145
+ cog_area = glass_area - edge_area
146
+ cog_area = 0 if cog_area < 0 else cog_area
147
+ total_area = cog_area + edge_area
148
+ glass_u = ((glass_u * cog_area) + (edge_u * edge_area)) / total_area
149
+ _, fr_r_vals = win_con.temperature_profile_frame(
150
+ angle=abs(self.host.altitude - 90))
151
+ frame_u = 1 / sum(fr_r_vals)
152
+ frame_area = (self.host.perimeter * u_conv * win_con.frame.width) + \
153
+ ((win_con.frame.width * u_conv) ** 2) * len(self.host.geometry)
154
+ assembly_area = glass_area + frame_area
155
+ total_u = ((glass_u * glass_area) + (frame_u * frame_area)) / assembly_area
156
+ return 1 / total_u
157
+
158
+ def u_factor(self, units='Meters'):
159
+ """Get the Door U-factor [W/m2-K] (including resistances for air films).
160
+
161
+ The air film resistances are computed using the orientation and height
162
+ of the Door geometry.
163
+
164
+ Args:
165
+ units: Text for the units in which the Door geometry exists. These
166
+ will be used to correctly interpret the dimensions of the
167
+ geometry for heat flow calculation. (Default: Meters).
168
+ """
169
+ return 1 / self.r_factor(units)
170
+
171
+ def shgc(self, units='Meters'):
172
+ """Get the Door solar heat gain coefficient (SHGC).
173
+
174
+ If this construction is not a simple glazing system, this value is computed
175
+ by summing the transmitted and conducted portions of solar irradiance under
176
+ the NFRC summer conditions. The air film resistances are computed using
177
+ the orientation and height of the Door geometry. If the window construction
178
+ has a frame, the geometry of the frame will also be accounted for.
179
+
180
+ Args:
181
+ units: Text for the units in which the Door geometry exists. These
182
+ will be used to correctly interpret the dimensions of the
183
+ geometry for heat flow calculation. (Default: Meters).
184
+ """
185
+ win_con = self._window_construction()
186
+ if isinstance(win_con.materials[0], EnergyWindowMaterialSimpleGlazSys):
187
+ if not win_con.has_frame:
188
+ return win_con.materials[0].shgc
189
+ # compute the temperature profile
190
+ t_out, t_in, sol_irr = 32, 24, 783 # NFRC 2010 summer conditions
191
+ u_conv = conversion_factor_to_meters(units)
192
+ height = (self.host.max.z - self.host.min.z) * u_conv
193
+ height = 1 if height < 1 else height
194
+ _, r_vals = win_con.temperature_profile(
195
+ t_out, t_in, height=height, angle=abs(self.host.altitude - 90),
196
+ solar_irradiance=sol_irr)
197
+ if isinstance(win_con, OpaqueConstruction):
198
+ heat_gen = sol_irr * (1 - win_con.outside_solar_reflectance)
199
+ r_factor = sum(r_vals)
200
+ conducted = heat_gen * (1 - (sum(r_vals[1:]) / r_factor))
201
+ return conducted / sol_irr
202
+ heat_gen, transmitted = win_con._heat_gen_from_solar(sol_irr)
203
+ conducted = 0
204
+ r_factor = sum(r_vals)
205
+ for i, heat_g in enumerate(heat_gen):
206
+ if heat_g != 0:
207
+ conducted += heat_g * (1 - (sum(r_vals[i + 1:]) / r_factor))
208
+ if not win_con.has_frame:
209
+ return (transmitted + conducted) / sol_irr
210
+ else: # account for the frame conduction
211
+ _, r_values = win_con.temperature_profile_frame(
212
+ t_out, t_in, height=height, angle=abs(self.host.altitude - 90),
213
+ solar_irradiance=sol_irr)
214
+ heat_gen = [0, sol_irr * win_con.frame.solar_absorptance, 0]
215
+ frame_conducted = 0
216
+ r_factor = sum(r_values)
217
+ for i, heat_g in enumerate(heat_gen):
218
+ if heat_g != 0:
219
+ frame_conducted += heat_g * (1 - (sum(r_values[i + 1:]) / r_factor))
220
+ glass_area = (self.host.area * (u_conv ** 2))
221
+ frame_area = (self.host.perimeter * u_conv * win_con.frame.width) + \
222
+ ((win_con.frame.width * u_conv) ** 2) * len(self.host.geometry)
223
+ glass_trans = transmitted * glass_area
224
+ glass_conduct = conducted * glass_area
225
+ frame_conduct = frame_conducted * frame_area
226
+ total_irr = sol_irr * (glass_area + frame_area)
227
+ return (glass_trans + glass_conduct + frame_conduct) / total_irr
228
+
229
+ def reset_construction_to_set(self):
230
+ """Reset a construction assigned at the level of this Door to the default.
231
+
232
+ This means that the Door's construction will be assigned by a ConstructionSet.
233
+ """
234
+ self._construction = None
235
+ for shade in self.host.shades:
236
+ shade.properties.energy.reset_construction_to_set()
237
+
238
+ @classmethod
239
+ def from_dict(cls, data, host):
240
+ """Create DoorEnergyProperties from a dictionary.
241
+
242
+ Note that the dictionary must be a non-abridged version for this
243
+ classmethod to work.
244
+
245
+ Args:
246
+ data: A dictionary representation of DoorEnergyProperties in the
247
+ format below.
248
+ host: A Door object that hosts these properties.
249
+
250
+ .. code-block:: python
251
+
252
+ {
253
+ "type": 'DoorEnergyProperties',
254
+ "construction": {}, # OpaqueConstruction or WindowConstruction dict
255
+ "vent_opening": {} # VentilationOpening dict
256
+ }
257
+ """
258
+ assert data['type'] == 'DoorEnergyProperties', \
259
+ 'Expected DoorEnergyProperties. Got {}.'.format(data['type'])
260
+
261
+ new_prop = cls(host)
262
+ if 'construction' in data and data['construction'] is not None:
263
+ new_prop.construction = dict_to_construction(data['construction'])
264
+ if 'vent_opening' in data and data['vent_opening'] is not None:
265
+ new_prop.vent_opening = VentilationOpening.from_dict(data['vent_opening'])
266
+ return new_prop
267
+
268
+ def apply_properties_from_dict(self, abridged_data, constructions):
269
+ """Apply properties from a DoorEnergyPropertiesAbridged dictionary.
270
+
271
+ Args:
272
+ abridged_data: A DoorEnergyPropertiesAbridged dictionary (typically
273
+ coming from a Model).
274
+ constructions: A dictionary of constructions with constructions identifiers
275
+ as keys, which will be used to re-assign constructions.
276
+ """
277
+ if 'construction' in abridged_data and abridged_data['construction'] is not None:
278
+ try:
279
+ self.construction = constructions[abridged_data['construction']]
280
+ except KeyError:
281
+ raise ValueError('Door construction "{}" was not found in '
282
+ 'constructions.'.format(abridged_data['construction']))
283
+ if 'vent_opening' in abridged_data and abridged_data['vent_opening'] is not None:
284
+ self.vent_opening = \
285
+ VentilationOpening.from_dict(abridged_data['vent_opening'])
286
+
287
+ def to_dict(self, abridged=False):
288
+ """Return energy properties as a dictionary.
289
+
290
+ Args:
291
+ abridged: Boolean to note whether the full dictionary describing the
292
+ object should be returned (False) or just an abridged version (True).
293
+ Default: False.
294
+ """
295
+ base = {'energy': {}}
296
+ base['energy']['type'] = 'DoorEnergyProperties' if not \
297
+ abridged else 'DoorEnergyPropertiesAbridged'
298
+ if self._construction is not None:
299
+ base['energy']['construction'] = \
300
+ self._construction.identifier if abridged else \
301
+ self._construction.to_dict()
302
+ if self._vent_opening is not None:
303
+ base['energy']['vent_opening'] = self._vent_opening.to_dict()
304
+ return base
305
+
306
+ def duplicate(self, new_host=None):
307
+ """Get a copy of this object.
308
+
309
+ Args:
310
+ new_host: A new Door object that hosts these properties.
311
+ If None, the properties will be duplicated with the same host.
312
+ """
313
+ _host = new_host or self._host
314
+ vo = self._vent_opening.duplicate() if self._vent_opening is not None else None
315
+ return DoorEnergyProperties(_host, self._construction, vo)
316
+
317
+ def is_equivalent(self, other):
318
+ """Check to see if these energy properties are equivalent to another object.
319
+
320
+ This will only be True if all properties match (except for the host) and
321
+ will otherwise be False.
322
+ """
323
+ if not is_equivalent(self._construction, other._construction):
324
+ return False
325
+ if not is_equivalent(self._vent_opening, other._vent_opening):
326
+ return False
327
+ return True
328
+
329
+ def _window_construction(self):
330
+ """Get the base window or opaque construction assigned to the door."""
331
+ win_con = self.construction
332
+ if isinstance(win_con, WindowConstructionShade):
333
+ win_con = win_con.window_construction
334
+ elif isinstance(win_con, WindowConstructionDynamic):
335
+ win_con = win_con.constructions[0]
336
+ return win_con
337
+
338
+ def ToString(self):
339
+ return self.__repr__()
340
+
341
+ def __repr__(self):
342
+ return 'Door Energy Properties: [host: {}]'.format(self.host.display_name)