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,1087 @@
1
+ # coding=utf-8
2
+ """Gas materials representing gaps within window constructions.
3
+
4
+ They can only exist within window constructions bounded by glazing materials
5
+ (they cannot be in the interior or exterior layer).
6
+ """
7
+ from __future__ import division
8
+
9
+ from ._base import _EnergyMaterialWindowBase
10
+ from ..properties.extension import (
11
+ EnergyWindowMaterialGasProperties,
12
+ EnergyWindowMaterialGasMixtureProperties,
13
+ EnergyWindowMaterialGasCustomProperties,
14
+ )
15
+ from ..reader import parse_idf_string
16
+ from ..writer import generate_idf_string
17
+
18
+ from honeybee._lockable import lockable
19
+ from honeybee.typing import float_positive, float_in_range, tuple_with_length
20
+
21
+ import math
22
+
23
+
24
+ @lockable
25
+ class _EnergyWindowMaterialGasBase(_EnergyMaterialWindowBase):
26
+ """Base for gas gap layer."""
27
+ GASES = ('Air', 'Argon', 'Krypton', 'Xenon')
28
+ CONDUCTIVITYCURVES = {'Air': (0.002873, 0.0000776, 0.0),
29
+ 'Argon': (0.002285, 0.00005149, 0.0),
30
+ 'Krypton': (0.0009443, 0.00002826, 0.0),
31
+ 'Xenon': (0.0004538, 0.00001723, 0.0)}
32
+ VISCOSITYCURVES = {'Air': (0.00000372, 0.00000005, 0.0),
33
+ 'Argon': (0.00000338, 0.00000006, 0.0),
34
+ 'Krypton': (0.00000221, 0.00000008, 0.0),
35
+ 'Xenon': (0.00000107, 0.00000007, 0.0)}
36
+ SPECIFICHEATCURVES = {'Air': (1002.73699951, 0.012324, 0.0),
37
+ 'Argon': (521.92852783, 0.0, 0.0),
38
+ 'Krypton': (248.09069824, 0.0, 0.0),
39
+ 'Xenon': (158.33970642, 0.0, 0.0)}
40
+ MOLECULARWEIGHTS = {'Air': 28.97, 'Argon': 39.948,
41
+ 'Krypton': 83.8, 'Xenon': 131.3}
42
+ __slots__ = ('_thickness',)
43
+
44
+ def __init__(self, identifier, thickness=0.0125):
45
+ """Initialize gas base material."""
46
+ _EnergyMaterialWindowBase.__init__(self, identifier)
47
+ self.thickness = thickness
48
+
49
+ @property
50
+ def is_gas_material(self):
51
+ """Boolean to note whether the material is a gas gap layer."""
52
+ return True
53
+
54
+ @property
55
+ def thickness(self):
56
+ """Get or set the thickess of the gas layer [m]."""
57
+ return self._thickness
58
+
59
+ @property
60
+ def molecular_weight(self):
61
+ """Default placeholder gas molecular weight."""
62
+ return self.MOLECULARWEIGHTS['Air']
63
+
64
+ @thickness.setter
65
+ def thickness(self, thick):
66
+ self._thickness = float_positive(thick, 'gas gap thickness')
67
+
68
+ @property
69
+ def conductivity(self):
70
+ """Conductivity of the gas in the absence of convection at 0C [W/m-K]."""
71
+ return self.conductivity_at_temperature(273.15)
72
+
73
+ @property
74
+ def viscosity(self):
75
+ """Viscosity of the gas at 0C [kg/m-s]."""
76
+ return self.viscosity_at_temperature(273.15)
77
+
78
+ @property
79
+ def specific_heat(self):
80
+ """Specific heat of the gas at 0C [J/kg-K]."""
81
+ return self.specific_heat_at_temperature(273.15)
82
+
83
+ @property
84
+ def density(self):
85
+ """Density of the gas at 0C and sea-level pressure [J/kg-K]."""
86
+ return self.density_at_temperature(273.15)
87
+
88
+ @property
89
+ def prandtl(self):
90
+ """Prandtl number of the gas at 0C."""
91
+ return self.prandtl_at_temperature(273.15)
92
+
93
+ def density_at_temperature(self, t_kelvin, pressure=101325):
94
+ """Get the density of the gas [kg/m3] at a given temperature and pressure.
95
+
96
+ This method uses the ideal gas law to estimate the density.
97
+
98
+ Args:
99
+ t_kelvin: The average temperature of the gas cavity in Kelvin.
100
+ pressure: The average pressure of the gas cavity in Pa.
101
+ Default is 101325 Pa for standard pressure at sea level.
102
+ """
103
+ return (pressure * self.molecular_weight * 0.001) / (8.314 * t_kelvin)
104
+
105
+ def prandtl_at_temperature(self, t_kelvin):
106
+ """Get the Prandtl number of the gas at a given Kelvin temperature."""
107
+ return self.viscosity_at_temperature(t_kelvin) * \
108
+ self.specific_heat_at_temperature(t_kelvin) / \
109
+ self.conductivity_at_temperature(t_kelvin)
110
+
111
+ def grashof(self, delta_t=15, t_kelvin=273.15, pressure=101325):
112
+ """Get Grashof number given the temperature difference across the cavity.
113
+
114
+ Args:
115
+ delta_t: The temperature difference across the gas cavity [C]. Default is
116
+ 15C, which is consistent with the NFRC standard for double glazed units.
117
+ t_kelvin: The average temperature of the gas cavity in Kelvin.
118
+ Default: 273.15 K (0C).
119
+ pressure: The average pressure of the gas cavity in Pa.
120
+ Default is 101325 Pa for standard pressure at sea level.
121
+ """
122
+ return (9.81 * (self.thickness ** 3) * delta_t *
123
+ self.density_at_temperature(t_kelvin, pressure) ** 2) / \
124
+ (t_kelvin * (self.viscosity_at_temperature(t_kelvin) ** 2))
125
+
126
+ def rayleigh(self, delta_t=15, t_kelvin=273.15, pressure=101325):
127
+ """Get Rayleigh number given the temperature difference across the cavity.
128
+
129
+ Args:
130
+ delta_t: The temperature difference across the gas cavity [C]. Default is
131
+ 15C, which is consistent with the NFRC standard for double glazed units.
132
+ t_kelvin: The average temperature of the gas cavity in Kelvin.
133
+ Default: 273.15 K (0C).
134
+ pressure: The average pressure of the gas cavity in Pa.
135
+ Default is 101325 Pa for standard pressure at sea level.
136
+ """
137
+ _numerator = (self.density_at_temperature(t_kelvin, pressure) ** 2) * \
138
+ (self.thickness ** 3) * 9.81 * self.specific_heat_at_temperature(t_kelvin) \
139
+ * delta_t
140
+ _denominator = t_kelvin * self.viscosity_at_temperature(t_kelvin) * \
141
+ self.conductivity_at_temperature(t_kelvin)
142
+ return _numerator / _denominator
143
+
144
+ def nusselt(self, delta_t=15, height=1.0, t_kelvin=273.15, pressure=101325):
145
+ """Get Nusselt number for a vertical cavity given the temp difference and height.
146
+
147
+ Args:
148
+ delta_t: The temperature difference across the gas cavity [C]. Default is
149
+ 15C, which is consistent with the NFRC standard for double glazed units.
150
+ height: An optional height for the cavity in meters. Default is 1.0,
151
+ which is consistent with NFRC standards.
152
+ t_kelvin: The average temperature of the gas cavity in Kelvin.
153
+ Default: 273.15 K (0C).
154
+ pressure: The average pressure of the gas cavity in Pa.
155
+ Default is 101325 Pa for standard pressure at sea level.
156
+ """
157
+ rayleigh = self.rayleigh(delta_t, t_kelvin, pressure)
158
+ if rayleigh > 50000:
159
+ n_u1 = 0.0673838 * (rayleigh ** (1 / 3))
160
+ elif rayleigh > 10000:
161
+ n_u1 = 0.028154 * (rayleigh ** 0.4134)
162
+ else:
163
+ n_u1 = 1 + 1.7596678e-10 * (rayleigh ** 2.2984755)
164
+ n_u2 = 0.242 * ((rayleigh * (self.thickness / height)) ** 0.272)
165
+ return max(n_u1, n_u2)
166
+
167
+ def nusselt_at_angle(self, delta_t=15, height=1.0, angle=90,
168
+ t_kelvin=273.15, pressure=101325):
169
+ """Get Nusselt number for a cavity at a given angle, temp difference and height.
170
+
171
+ Args:
172
+ delta_t: The temperature difference across the gas cavity [C]. Default is
173
+ 15C, which is consistent with the NFRC standard for double glazed units.
174
+ height: An optional height for the cavity in meters. Default is 1.0,
175
+ which is consistent with NFRC standards.
176
+ angle: An angle in degrees between 0 and 180.
177
+
178
+ * 0 = A horizontal cavity with downward heat flow through the layer.
179
+ * 90 = A vertical cavity
180
+ * 180 = A horizontal cavity with upward heat flow through the layer.
181
+
182
+ t_kelvin: The average temperature of the gas cavity in Kelvin.
183
+ Default: 273.15 K (0C).
184
+ pressure: The average pressure of the gas cavity in Pa.
185
+ Default is 101325 Pa for standard pressure at sea level.
186
+ """
187
+ def dot_x(x):
188
+ return (x + abs(x)) / 2
189
+
190
+ rayleigh = self.rayleigh(delta_t, t_kelvin, pressure)
191
+ if angle < 60:
192
+ cos_a = math.cos(math.radians(angle))
193
+ sin_a_18 = math.sin(1.8 * math.radians(angle))
194
+ term_1 = dot_x(1 - (1708 / (rayleigh * cos_a)))
195
+ term_2 = 1 - ((1708 * (sin_a_18 ** 1.6)) / (rayleigh * cos_a))
196
+ term_3 = dot_x(((rayleigh * cos_a) / 5830) ** (1 / 3) - 1)
197
+ return 1 + (1.44 * term_1 * term_2) + term_3
198
+ elif angle < 90:
199
+ g = 0.5 / ((1 + ((rayleigh / 3160) ** 20.6)) ** 0.1)
200
+ n_u1 = (1 + (((0.0936 * (rayleigh ** 0.314)) / (1 + g)) ** 7)) ** (1 / 7)
201
+ n_u2 = (0.104 + (0.175 / (self.thickness / height))) * (rayleigh ** 0.283)
202
+ n_u_60 = max(n_u1, n_u2)
203
+ n_u_90 = self.nusselt(delta_t, height, t_kelvin, pressure)
204
+ return (n_u_60 + n_u_90) / 2
205
+ elif angle == 90:
206
+ return self.nusselt(delta_t, height, t_kelvin, pressure)
207
+ else:
208
+ n_u_90 = self.nusselt(delta_t, height, t_kelvin, pressure)
209
+ return 1 + ((n_u_90 - 1) * math.sin(math.radians(angle)))
210
+
211
+ def convective_conductance(self, delta_t=15, height=1.0,
212
+ t_kelvin=273.15, pressure=101325):
213
+ """Get convective conductance of the cavity in a vertical position.
214
+
215
+ Args:
216
+ delta_t: The temperature difference across the gas cavity [C]. Default is
217
+ 15C, which is consistent with the NFRC standard for double glazed units.
218
+ height: An optional height for the cavity in meters. Default is 1.0,
219
+ which is consistent with NFRC standards.
220
+ t_kelvin: The average temperature of the gas cavity in Kelvin.
221
+ Default: 273.15 K (0C).
222
+ pressure: The average pressure of the gas cavity in Pa.
223
+ Default is 101325 Pa for standard pressure at sea level.
224
+ """
225
+ return self.nusselt(delta_t, height, t_kelvin, pressure) * \
226
+ (self.conductivity_at_temperature(t_kelvin) / self.thickness)
227
+
228
+ def convective_conductance_at_angle(self, delta_t=15, height=1.0, angle=90,
229
+ t_kelvin=273.15, pressure=101325):
230
+ """Get convective conductance of the cavity in an angle.
231
+
232
+ Args:
233
+ delta_t: The temperature difference across the gas cavity [C]. Default is
234
+ 15C, which is consistent with the NFRC standard for double glazed units.
235
+ height: An optional height for the cavity in meters. Default is 1.0,
236
+ which is consistent with NFRC standards.
237
+ angle: An angle in degrees between 0 and 180.
238
+
239
+ * 0 = A horizontal cavity with downward heat flow through the layer.
240
+ * 90 = A vertical cavity
241
+ * 180 = A horizontal cavity with upward heat flow through the layer.
242
+
243
+ t_kelvin: The average temperature of the gas cavity in Kelvin.
244
+ Default: 273.15 K (0C).
245
+ pressure: The average pressure of the gas cavity in Pa.
246
+ Default is 101325 Pa for standard pressure at sea level.
247
+ """
248
+ return self.nusselt_at_angle(delta_t, height, angle, t_kelvin, pressure) * \
249
+ (self.conductivity_at_temperature(t_kelvin) / self.thickness)
250
+
251
+ def radiative_conductance(self, emissivity_1=0.84, emissivity_2=0.84,
252
+ t_kelvin=273.15):
253
+ """Get the radiative conductance of the cavity given emissivities on both sides.
254
+
255
+ Args:
256
+ emissivity_1: The emissivity of the surface on one side of the cavity.
257
+ Default is 0.84, which is typical of clear, uncoated glass.
258
+ emissivity_2: The emissivity of the surface on the other side of the cavity.
259
+ Default is 0.84, which is typical of clear, uncoated glass.
260
+ t_kelvin: The average temperature of the gas cavity in Kelvin.
261
+ Default: 273.15 K (0C).
262
+ """
263
+ return (4 * 5.6697e-8) * (((1 / emissivity_1) + (1 / emissivity_2) - 1) ** -1) \
264
+ * (t_kelvin ** 3)
265
+
266
+ def u_value(self, delta_t=15, emissivity_1=0.84, emissivity_2=0.84, height=1.0,
267
+ t_kelvin=273.15, pressure=101325):
268
+ """Get the U-value of a vertical gas cavity given temp difference and emissivity.
269
+
270
+ Args:
271
+ delta_t: The temperature difference across the gas cavity [C]. This
272
+ influences how strong the convection is within the gas gap. Default is
273
+ 15C, which is consistent with the NFRC standard for double glazed units.
274
+ emissivity_1: The emissivity of the surface on one side of the cavity.
275
+ Default is 0.84, which is typical of clear, uncoated glass.
276
+ emissivity_2: The emissivity of the surface on the other side of the cavity.
277
+ Default is 0.84, which is typical of clear, uncoated glass.
278
+ height: An optional height for the cavity in meters. Default is 1.0,
279
+ which is consistent with NFRC standards.
280
+ t_kelvin: The average temperature of the gas cavity in Kelvin.
281
+ Default: 273.15 K (0C).
282
+ pressure: The average pressure of the gas cavity in Pa.
283
+ Default is 101325 Pa for standard pressure at sea level.
284
+ """
285
+ return self.convective_conductance(delta_t, height, t_kelvin, pressure) + \
286
+ self.radiative_conductance(emissivity_1, emissivity_2, t_kelvin)
287
+
288
+ def u_value_at_angle(self, delta_t=15, emissivity_1=0.84, emissivity_2=0.84,
289
+ height=1.0, angle=90, t_kelvin=273.15, pressure=101325):
290
+ """Get the U-value of a vertical gas cavity given temp difference and emissivity.
291
+
292
+ Args:
293
+ delta_t: The temperature difference across the gas cavity [C]. This
294
+ influences how strong the convection is within the gas gap. Default is
295
+ 15C, which is consistent with the NFRC standard for double glazed units.
296
+ emissivity_1: The emissivity of the surface on one side of the cavity.
297
+ Default is 0.84, which is typical of clear, uncoated glass.
298
+ emissivity_2: The emissivity of the surface on the other side of the cavity.
299
+ Default is 0.84, which is typical of clear, uncoated glass.
300
+ height: An optional height for the cavity in meters. Default is 1.0,
301
+ which is consistent with NFRC standards.
302
+ angle: An angle in degrees between 0 and 180.
303
+ 0 = A horizontal cavity with downward heat flow through the layer.
304
+ 90 = A vertical cavity
305
+ 180 = A horizontal cavity with upward heat flow through the layer.
306
+ t_kelvin: The average temperature of the gas cavity in Kelvin.
307
+ Default: 273.15 K (0C).
308
+ pressure: The average pressure of the gas cavity in Pa.
309
+ Default is 101325 Pa for standard pressure at sea level.
310
+ """
311
+ return self.convective_conductance_at_angle(
312
+ delta_t, height, angle, t_kelvin, pressure) + \
313
+ self.radiative_conductance(emissivity_1, emissivity_2, t_kelvin)
314
+
315
+
316
+ @lockable
317
+ class EnergyWindowMaterialGas(_EnergyWindowMaterialGasBase):
318
+ """Gas gap layer.
319
+
320
+ Args:
321
+ identifier: Text string for a unique Material ID. Must be < 100 characters
322
+ and not contain any EnergyPlus special characters. This will be used to
323
+ identify the object across a model and in the exported IDF.
324
+ thickness: Number for the thickness of the air gap layer [m].
325
+ Default: 0.0125
326
+ gas_type: Text describing the type of gas in the gap.
327
+ Must be one of the following: 'Air', 'Argon', 'Krypton', 'Xenon'.
328
+ Default: 'Air'
329
+
330
+ Properties:
331
+ * identifier
332
+ * display_name
333
+ * thickness
334
+ * gas_type
335
+ * conductivity
336
+ * viscosity
337
+ * specific_heat
338
+ * density
339
+ * prandtl
340
+ * user_data
341
+ * properties
342
+ """
343
+ __slots__ = ('_gas_type',)
344
+
345
+ def __init__(self, identifier, thickness=0.0125, gas_type='Air'):
346
+ """Initialize gas energy material."""
347
+ _EnergyWindowMaterialGasBase.__init__(self, identifier, thickness)
348
+ self.gas_type = gas_type
349
+ self._properties = EnergyWindowMaterialGasProperties(self)
350
+
351
+ @property
352
+ def gas_type(self):
353
+ """Get or set the text describing the gas in the gas gap layer."""
354
+ return self._gas_type
355
+
356
+ @gas_type.setter
357
+ def gas_type(self, gas):
358
+ assert gas.title() in self.GASES, 'Invalid input "{}" for gas type.' \
359
+ '\nGas type must be one of the following:{}'.format(gas, self.GASES)
360
+ self._gas_type = gas.title()
361
+
362
+ @property
363
+ def molecular_weight(self):
364
+ """Get the gas molecular weight."""
365
+ return self.MOLECULARWEIGHTS[self._gas_type]
366
+
367
+ def conductivity_at_temperature(self, t_kelvin):
368
+ """Get the conductivity of the gas [W/m-K] at a given Kelvin temperature."""
369
+ return self._coeff_property(self.CONDUCTIVITYCURVES, t_kelvin)
370
+
371
+ def viscosity_at_temperature(self, t_kelvin):
372
+ """Get the viscosity of the gas [kg/m-s] at a given Kelvin temperature."""
373
+ return self._coeff_property(self.VISCOSITYCURVES, t_kelvin)
374
+
375
+ def specific_heat_at_temperature(self, t_kelvin):
376
+ """Get the specific heat of the gas [J/kg-K] at a given Kelvin temperature."""
377
+ return self._coeff_property(self.SPECIFICHEATCURVES, t_kelvin)
378
+
379
+ @classmethod
380
+ def from_idf(cls, idf_string):
381
+ """Create EnergyWindowMaterialGas from an EnergyPlus text string.
382
+
383
+ Args:
384
+ idf_string: A text string fully describing an EnergyPlus material.
385
+ """
386
+ ep_strs = parse_idf_string(idf_string, 'WindowMaterial:Gas,')
387
+ assert ep_strs[1].title() != 'Custom', \
388
+ 'Honeybee EnergyWindowMaterialGas cannot use EnergyPlus Custom gas type.\n' \
389
+ 'Use honeybee EnergyWindowMaterialGasCustom instead.'
390
+ return cls(ep_strs[0], ep_strs[2], ep_strs[1])
391
+
392
+ @classmethod
393
+ def from_dict(cls, data):
394
+ """Create a EnergyWindowMaterialGas from a dictionary.
395
+
396
+ Args:
397
+ data: A python dictionary in the following format
398
+
399
+ .. code-block:: python
400
+
401
+ {
402
+ "type": 'EnergyWindowMaterialGas',
403
+ "identifier": 'Argon_Gap_0010',
404
+ "display_name": 'Argon Gap',
405
+ "thickness": 0.01,
406
+ "gas_type": 'Argon'
407
+ }
408
+ """
409
+ assert data['type'] == 'EnergyWindowMaterialGas', \
410
+ 'Expected EnergyWindowMaterialGas. Got {}.'.format(data['type'])
411
+ thickness = 0.0125 if 'thickness' not in data else data['thickness']
412
+ gas_type = 'Air' if 'gas_type' not in data else data['gas_type']
413
+ new_obj = cls(data['identifier'], thickness, gas_type)
414
+ if 'display_name' in data and data['display_name'] is not None:
415
+ new_obj.display_name = data['display_name']
416
+ if 'user_data' in data and data['user_data'] is not None:
417
+ new_obj.user_data = data['user_data']
418
+ if 'properties' in data and data['properties'] is not None:
419
+ new_obj._properties._load_extension_attr_from_dict(data['properties'])
420
+ return new_obj
421
+
422
+ def to_idf(self):
423
+ """Get an EnergyPlus string representation of the material.
424
+
425
+ .. code-block:: shell
426
+
427
+ WindowMaterial:Gas,
428
+ Gas_1_W_0_0100, !- gap name - Air
429
+ Air, !- type
430
+ 0.0100; !- thickness
431
+ """
432
+ values = (self.identifier, self.gas_type, self.thickness)
433
+ comments = ('name', 'gas type', 'thickness {m}')
434
+ return generate_idf_string('WindowMaterial:Gas', values, comments)
435
+
436
+ def to_dict(self):
437
+ """Energy Material Gas dictionary representation."""
438
+ base = {
439
+ 'type': 'EnergyWindowMaterialGas',
440
+ 'identifier': self.identifier,
441
+ 'thickness': self.thickness,
442
+ 'gas_type': self.gas_type
443
+ }
444
+ if self._display_name is not None:
445
+ base['display_name'] = self.display_name
446
+ if self._user_data is not None:
447
+ base['user_data'] = self.user_data
448
+ prop_dict = self._properties.to_dict()
449
+ if prop_dict is not None:
450
+ base['properties'] = prop_dict
451
+ return base
452
+
453
+ def _coeff_property(self, dictionary, t_kelvin):
454
+ """Get a property given a dictionary of coefficients and kelvin temperature."""
455
+ return dictionary[self._gas_type][0] + \
456
+ dictionary[self._gas_type][1] * t_kelvin + \
457
+ dictionary[self._gas_type][2] * t_kelvin ** 2
458
+
459
+ def __key(self):
460
+ """A tuple based on the object properties, useful for hashing."""
461
+ return (self.identifier, self.thickness, self.gas_type)
462
+
463
+ def __hash__(self):
464
+ return hash(self.__key())
465
+
466
+ def __eq__(self, other):
467
+ return isinstance(other, EnergyWindowMaterialGas) and \
468
+ self.__key() == other.__key()
469
+
470
+ def __ne__(self, other):
471
+ return not self.__eq__(other)
472
+
473
+ def __repr__(self):
474
+ return self.to_idf()
475
+
476
+ def __copy__(self):
477
+ new_obj = EnergyWindowMaterialGas(self.identifier, self.thickness, self.gas_type)
478
+ new_obj._display_name = self._display_name
479
+ new_obj._user_data = None if self._user_data is None else self._user_data.copy()
480
+ new_obj._properties._duplicate_extension_attr(self._properties)
481
+ return new_obj
482
+
483
+
484
+ @lockable
485
+ class EnergyWindowMaterialGasMixture(_EnergyWindowMaterialGasBase):
486
+ """Gas gap layer with a mixture of gasses.
487
+
488
+ Args:
489
+ identifier: Text string for a unique Material ID. Must be < 100 characters
490
+ and not contain any EnergyPlus special characters. This will be used to
491
+ identify the object across a model and in the exported IDF.
492
+ thickness: Number for the thickness of the air gap layer [m].
493
+ Default: 0.0125
494
+ gas_types: A list of text describing the types of gas in the gap.
495
+ Text must be one of the following: 'Air', 'Argon', 'Krypton', 'Xenon'.
496
+ Default: ('Argon', 'Air')
497
+ gas_fractions: A list of fractional numbers describing the volumetric
498
+ fractions of gas types in the mixture. This list must align with
499
+ the gas_types input list and must sum to 1. Default: (0.9, 0.1).
500
+
501
+ Properties:
502
+ * identifier
503
+ * display_name
504
+ * thickness
505
+ * gas_types
506
+ * gas_fractions
507
+ * gas_count
508
+ * conductivity
509
+ * viscosity
510
+ * specific_heat
511
+ * density
512
+ * prandtl
513
+ * user_data
514
+ * properties
515
+ """
516
+ __slots__ = ('_gas_count', '_gas_types', '_gas_fractions')
517
+
518
+ def __init__(self, identifier, thickness=0.0125,
519
+ gas_types=('Argon', 'Air'), gas_fractions=(0.9, 0.1)):
520
+ """Initialize gas mixture energy material."""
521
+ _EnergyWindowMaterialGasBase.__init__(self, identifier, thickness)
522
+ try: # check the number of gases
523
+ self._gas_count = len(gas_types)
524
+ except (TypeError, ValueError):
525
+ raise TypeError(
526
+ 'Expected list for gas_types. Got {}.'.format(type(gas_types)))
527
+ assert 2 <= self._gas_count <= 4, 'Number of gases in gas mixture must be ' \
528
+ 'between 2 anf 4. Got {}.'.format(self._gas_count)
529
+ self.gas_types = gas_types
530
+ self.gas_fractions = gas_fractions
531
+ self._properties = EnergyWindowMaterialGasMixtureProperties(self)
532
+
533
+ @property
534
+ def gas_types(self):
535
+ """Get or set a tuple of text describing the gases in the gas gap layer."""
536
+ return self._gas_types
537
+
538
+ @gas_types.setter
539
+ def gas_types(self, g_types):
540
+ self._gas_types = tuple_with_length(
541
+ g_types, self._gas_count, str, 'gas mixture gas_types')
542
+ self._gas_types = tuple(gas.title() for gas in self._gas_types)
543
+ for gas in self._gas_types:
544
+ assert gas in self.GASES, 'Invalid input "{}" for gas type.' \
545
+ '\nGas type must be one of the following:{}'.format(gas, self.GASES)
546
+
547
+ @property
548
+ def gas_fractions(self):
549
+ """Get or set a tuple of numbers the fractions of gases in the gas gap layer."""
550
+ return self._gas_fractions
551
+
552
+ @gas_fractions.setter
553
+ def gas_fractions(self, g_fracs):
554
+ self._gas_fractions = tuple_with_length(
555
+ g_fracs, self._gas_count, float, 'gas mixture gas_fractions')
556
+ assert sum(self._gas_fractions) == 1, 'Gas fractions must sum to 1. ' \
557
+ 'Got {}.'.format(sum(self._gas_fractions))
558
+
559
+ @property
560
+ def molecular_weight(self):
561
+ """Get the gas molecular weight."""
562
+ return sum(tuple(self.MOLECULARWEIGHTS[gas] * frac for gas, frac
563
+ in zip(self._gas_types, self._gas_fractions)))
564
+
565
+ @property
566
+ def gas_count(self):
567
+ """An integer indicating the number of gasses in the mixture."""
568
+ return self._gas_count
569
+
570
+ def conductivity_at_temperature(self, t_kelvin):
571
+ """Get the conductivity of the gas [W/m-K] at a given Kelvin temperature."""
572
+ return self._weighted_avg_coeff_property(self.CONDUCTIVITYCURVES, t_kelvin)
573
+
574
+ def viscosity_at_temperature(self, t_kelvin):
575
+ """Get the viscosity of the gas [kg/m-s] at a given Kelvin temperature."""
576
+ return self._weighted_avg_coeff_property(self.VISCOSITYCURVES, t_kelvin)
577
+
578
+ def specific_heat_at_temperature(self, t_kelvin):
579
+ """Get the specific heat of the gas [J/kg-K] at a given Kelvin temperature."""
580
+ return self._weighted_avg_coeff_property(self.SPECIFICHEATCURVES, t_kelvin)
581
+
582
+ @classmethod
583
+ def from_idf(cls, idf_string):
584
+ """Create EnergyWindowMaterialGas from an EnergyPlus text string.
585
+
586
+ Args:
587
+ idf_string: A text string fully describing an EnergyPlus material.
588
+ """
589
+ prop_types = (str, float, int, str, float, str, float, str, float, str, float)
590
+ ep_strs = parse_idf_string(idf_string, 'WindowMaterial:GasMixture,')
591
+ ep_s = [typ(prop) for typ, prop in zip(prop_types, ep_strs)]
592
+ gas_types = [ep_s[i] for i in range(3, 3 + ep_s[2] * 2, 2)]
593
+ gas_fracs = [ep_s[i] for i in range(4, 4 + ep_s[2] * 2, 2)]
594
+ return cls(ep_s[0], ep_s[1], gas_types, gas_fracs)
595
+
596
+ @classmethod
597
+ def from_dict(cls, data):
598
+ """Create a EnergyWindowMaterialGasMixture from a dictionary.
599
+
600
+ Args:
601
+ data: A python dictionary in the following format
602
+
603
+ .. code-block:: python
604
+
605
+ {
606
+ 'type': 'EnergyWindowMaterialGasMixture',
607
+ 'identifier': 'Argon_Mixture_001_095_005',
608
+ 'display_name': 'Argon Mixture',
609
+ 'thickness': 0.01,
610
+ 'gas_types': ['Argon', 'Air'],
611
+ 'gas_fractions': [0.95, 0.05]
612
+ }
613
+ """
614
+ assert data['type'] == 'EnergyWindowMaterialGasMixture', \
615
+ 'Expected EnergyWindowMaterialGasMixture. Got {}.'.format(data['type'])
616
+ required_keys = ('identifier', 'gas_types', 'gas_fractions')
617
+ for key in required_keys:
618
+ assert key in data, 'Required key "{}" is missing.'.format(key)
619
+ thickness = 0.0125 if 'thickness' not in data or data['thickness'] is None \
620
+ else data['thickness']
621
+ new_obj = cls(data['identifier'], thickness, data['gas_types'],
622
+ data['gas_fractions'])
623
+ if 'display_name' in data and data['display_name'] is not None:
624
+ new_obj.display_name = data['display_name']
625
+ if 'user_data' in data and data['user_data'] is not None:
626
+ new_obj.user_data = data['user_data']
627
+ if 'properties' in data and data['properties'] is not None:
628
+ new_obj._properties._load_extension_attr_from_dict(data['properties'])
629
+ return new_obj
630
+
631
+ def to_idf(self):
632
+ """Get an EnergyPlus string representation of the material.
633
+
634
+ .. code-block:: shell
635
+
636
+ WindowMaterial:GasMixture,ArgonKryptonMix,
637
+ 0.0125, ! Thickness {m} 1/2 inch
638
+ 2, ! Number of Gases in Mixture
639
+ Argon, ! Gas 1 Type
640
+ 0.6, ! Gas 1 Fraction
641
+ Krypton, ! Gas 2 Type
642
+ 0.4; ! Gas 2 Fraction
643
+ """
644
+ values = [self.identifier, self.thickness, len(self.gas_types)]
645
+ comments = ['name', 'thickness {m}', 'number of gases']
646
+ for i in range(len(self.gas_types)):
647
+ values.append(self.gas_types[i])
648
+ values.append(self.gas_fractions[i])
649
+ comments.append('gas {} type'.format(i))
650
+ comments.append('gas {} fraction'.format(i))
651
+ return generate_idf_string('WindowMaterial:GasMixture', values, comments)
652
+
653
+ def to_dict(self):
654
+ """Energy Material Gas Mixture dictionary representation."""
655
+ base = {
656
+ 'type': 'EnergyWindowMaterialGasMixture',
657
+ 'identifier': self.identifier,
658
+ 'thickness': self.thickness,
659
+ 'gas_types': self.gas_types,
660
+ 'gas_fractions': self.gas_fractions
661
+ }
662
+ if self._display_name is not None:
663
+ base['display_name'] = self.display_name
664
+ if self._user_data is not None:
665
+ base['user_data'] = self.user_data
666
+ prop_dict = self._properties.to_dict()
667
+ if prop_dict is not None:
668
+ base['properties'] = prop_dict
669
+ return base
670
+
671
+ def _weighted_avg_coeff_property(self, dictionary, t_kelvin):
672
+ """Get a weighted average property given a dictionary of coefficients."""
673
+ property = []
674
+ for gas in self._gas_types:
675
+ property.append(dictionary[gas][0] + dictionary[gas][1] * t_kelvin +
676
+ dictionary[gas][2] * t_kelvin ** 2)
677
+ return sum(tuple(pr * frac for pr, frac in zip(property, self._gas_fractions)))
678
+
679
+ def __key(self):
680
+ """A tuple based on the object properties, useful for hashing."""
681
+ return (self.identifier, self.thickness, self.gas_types, self.gas_fractions)
682
+
683
+ def __hash__(self):
684
+ return hash(self.__key())
685
+
686
+ def __eq__(self, other):
687
+ return isinstance(other, EnergyWindowMaterialGasMixture) and \
688
+ self.__key() == other.__key()
689
+
690
+ def __ne__(self, other):
691
+ return not self.__eq__(other)
692
+
693
+ def __repr__(self):
694
+ return self.to_idf()
695
+
696
+ def __copy__(self):
697
+ new_obj = EnergyWindowMaterialGasMixture(
698
+ self.identifier, self.thickness, self.gas_types, self.gas_fractions)
699
+ new_obj._display_name = self._display_name
700
+ new_obj._user_data = None if self._user_data is None else self._user_data.copy()
701
+ new_obj._properties._duplicate_extension_attr(self._properties)
702
+ return new_obj
703
+
704
+
705
+ @lockable
706
+ class EnergyWindowMaterialGasCustom(_EnergyWindowMaterialGasBase):
707
+ """Custom gas gap layer.
708
+
709
+ This object allows you to specify specific values for conductivity,
710
+ viscosity and specific heat through the following formula:
711
+
712
+ property = A + (B * T) + (C * T ** 2)
713
+
714
+ where:
715
+
716
+ * A, B, and C = regression coefficients for the gas
717
+ * T = temperature [K]
718
+
719
+ Note that setting properties B and C to 0 will mean the property will be
720
+ equal to the A coefficient.
721
+
722
+ Args:
723
+ identifier: Text string for a unique Material ID. Must be < 100 characters
724
+ and not contain any EnergyPlus special characters. This will be used to
725
+ identify the object across a model and in the exported IDF.
726
+ thickness: Number for the thickness of the air gap layer [m].
727
+ Default: 0.0125
728
+ conductivity_coeff_a: First conductivity coefficient.
729
+ Or conductivity in [W/m-K] if b and c coefficients are 0.
730
+ viscosity_coeff_a: First viscosity coefficient.
731
+ Or viscosity in [kg/m-s] if b and c coefficients are 0.
732
+ specific_heat_coeff_a: First specific heat coefficient.
733
+ Or specific heat in [J/kg-K] if b and c coefficients are 0.
734
+ conductivity_coeff_b: Second conductivity coefficient. Default = 0.
735
+ viscosity_coeff_b: Second viscosity coefficient. Default = 0.
736
+ specific_heat_coeff_b: Second specific heat coefficient. Default = 0.
737
+ conductivity_coeff_c: Third conductivity coefficient. Default = 0.
738
+ viscosity_coeff_c: Third viscosity coefficient. Default = 0.
739
+ specific_heat_coeff_c: Third specific heat coefficient. Default = 0.
740
+ specific_heat_ratio: A number for the the ratio of the specific heat at
741
+ contant pressure, to the specific heat at constant volume.
742
+ Default is 1.0 for Air.
743
+ molecular_weight: Number between 20 and 200 for the mass of 1 mol of
744
+ the substance in grams. Default is 20.0.
745
+
746
+ Properties:
747
+ * identifier
748
+ * display_name
749
+ * thickness
750
+ * conductivity_coeff_a
751
+ * viscosity_coeff_a
752
+ * specific_heat_coeff_a
753
+ * conductivity_coeff_b
754
+ * viscosity_coeff_b
755
+ * specific_heat_coeff_b
756
+ * conductivity_coeff_c
757
+ * viscosity_coeff_c
758
+ * specific_heat_coeff_c
759
+ * specific_heat_ratio
760
+ * molecular_weight
761
+ * conductivity
762
+ * viscosity
763
+ * specific_heat
764
+ * density
765
+ * prandtl
766
+ * user_data
767
+ * properties
768
+
769
+ Usage:
770
+
771
+ .. code-block:: python
772
+
773
+ co2_gap = EnergyWindowMaterialGasCustom('CO2', 0.0125, 0.0146, 0.000014, 827.73)
774
+ co2_gap.specific_heat_ratio = 1.4
775
+ co2_gap.molecular_weight = 44
776
+ print(co2_gap)
777
+ """
778
+ __slots__ = ('_conductivity_coeff_a', '_viscosity_coeff_a', '_specific_heat_coeff_a',
779
+ '_conductivity_coeff_b', '_viscosity_coeff_b', '_specific_heat_coeff_b',
780
+ '_conductivity_coeff_c', '_viscosity_coeff_c', '_specific_heat_coeff_c',
781
+ '_specific_heat_ratio', '_molecular_weight')
782
+
783
+ def __init__(self, identifier, thickness,
784
+ conductivity_coeff_a, viscosity_coeff_a, specific_heat_coeff_a,
785
+ conductivity_coeff_b=0, viscosity_coeff_b=0, specific_heat_coeff_b=0,
786
+ conductivity_coeff_c=0, viscosity_coeff_c=0, specific_heat_coeff_c=0,
787
+ specific_heat_ratio=1.0, molecular_weight=20.0):
788
+ """Initialize custom gas energy material."""
789
+ _EnergyWindowMaterialGasBase.__init__(self, identifier, thickness)
790
+ self.conductivity_coeff_a = conductivity_coeff_a
791
+ self.viscosity_coeff_a = viscosity_coeff_a
792
+ self.specific_heat_coeff_a = specific_heat_coeff_a
793
+ self.conductivity_coeff_b = conductivity_coeff_b
794
+ self.viscosity_coeff_b = viscosity_coeff_b
795
+ self.specific_heat_coeff_b = specific_heat_coeff_b
796
+ self.conductivity_coeff_c = conductivity_coeff_c
797
+ self.viscosity_coeff_c = viscosity_coeff_c
798
+ self.specific_heat_coeff_c = specific_heat_coeff_c
799
+ self.specific_heat_ratio = specific_heat_ratio
800
+ self.molecular_weight = molecular_weight
801
+ self._user_data = None
802
+ self._properties = EnergyWindowMaterialGasCustomProperties(self)
803
+
804
+ @property
805
+ def conductivity_coeff_a(self):
806
+ """Get or set the first conductivity coefficient."""
807
+ return self._conductivity_coeff_a
808
+
809
+ @conductivity_coeff_a.setter
810
+ def conductivity_coeff_a(self, coeff):
811
+ self._conductivity_coeff_a = float(coeff)
812
+
813
+ @property
814
+ def viscosity_coeff_a(self):
815
+ """Get or set the first viscosity coefficient."""
816
+ return self._viscosity_coeff_a
817
+
818
+ @viscosity_coeff_a.setter
819
+ def viscosity_coeff_a(self, coeff):
820
+ self._viscosity_coeff_a = float_positive(coeff)
821
+
822
+ @property
823
+ def specific_heat_coeff_a(self):
824
+ """Get or set the first specific heat coefficient."""
825
+ return self._specific_heat_coeff_a
826
+
827
+ @specific_heat_coeff_a.setter
828
+ def specific_heat_coeff_a(self, coeff):
829
+ self._specific_heat_coeff_a = float_positive(coeff)
830
+
831
+ @property
832
+ def conductivity_coeff_b(self):
833
+ """Get or set the second conductivity coefficient."""
834
+ return self._conductivity_coeff_b
835
+
836
+ @conductivity_coeff_b.setter
837
+ def conductivity_coeff_b(self, coeff):
838
+ self._conductivity_coeff_b = float(coeff)
839
+
840
+ @property
841
+ def viscosity_coeff_b(self):
842
+ """Get or set the second viscosity coefficient."""
843
+ return self._viscosity_coeff_b
844
+
845
+ @viscosity_coeff_b.setter
846
+ def viscosity_coeff_b(self, coeff):
847
+ self._viscosity_coeff_b = float(coeff)
848
+
849
+ @property
850
+ def specific_heat_coeff_b(self):
851
+ """Get or set the second specific heat coefficient."""
852
+ return self._specific_heat_coeff_b
853
+
854
+ @specific_heat_coeff_b.setter
855
+ def specific_heat_coeff_b(self, coeff):
856
+ self._specific_heat_coeff_b = float(coeff)
857
+
858
+ @property
859
+ def conductivity_coeff_c(self):
860
+ """Get or set the third conductivity coefficient."""
861
+ return self._conductivity_coeff_c
862
+
863
+ @conductivity_coeff_c.setter
864
+ def conductivity_coeff_c(self, coeff):
865
+ self._conductivity_coeff_c = float(coeff)
866
+
867
+ @property
868
+ def viscosity_coeff_c(self):
869
+ """Get or set the third viscosity coefficient."""
870
+ return self._viscosity_coeff_c
871
+
872
+ @viscosity_coeff_c.setter
873
+ def viscosity_coeff_c(self, coeff):
874
+ self._viscosity_coeff_c = float(coeff)
875
+
876
+ @property
877
+ def specific_heat_coeff_c(self):
878
+ """Get or set the third specific heat coefficient."""
879
+ return self._specific_heat_coeff_c
880
+
881
+ @specific_heat_coeff_c.setter
882
+ def specific_heat_coeff_c(self, coeff):
883
+ self._specific_heat_coeff_c = float(coeff)
884
+
885
+ @property
886
+ def specific_heat_ratio(self):
887
+ """Get or set the specific heat ratio."""
888
+ return self._specific_heat_ratio
889
+
890
+ @specific_heat_ratio.setter
891
+ def specific_heat_ratio(self, number):
892
+ number = float(number)
893
+ assert 1 <= number, 'Input specific_heat_ratio ({}) must be > 1.'.format(number)
894
+ self._specific_heat_ratio = number
895
+
896
+ @property
897
+ def molecular_weight(self):
898
+ """Get or set the molecular weight."""
899
+ return self._molecular_weight
900
+
901
+ @molecular_weight.setter
902
+ def molecular_weight(self, number):
903
+ self._molecular_weight = float_in_range(
904
+ number, 20.0, 200.0, 'gas material molecular weight')
905
+
906
+ def conductivity_at_temperature(self, t_kelvin):
907
+ """Get the conductivity of the gas [W/m-K] at a given Kelvin temperature."""
908
+ return self.conductivity_coeff_a + self.conductivity_coeff_b * t_kelvin + \
909
+ self.conductivity_coeff_c * t_kelvin ** 2
910
+
911
+ def viscosity_at_temperature(self, t_kelvin):
912
+ """Get the viscosity of the gas [kg/m-s] at a given Kelvin temperature."""
913
+ return self.viscosity_coeff_a + self.viscosity_coeff_b * t_kelvin + \
914
+ self.viscosity_coeff_c * t_kelvin ** 2
915
+
916
+ def specific_heat_at_temperature(self, t_kelvin):
917
+ """Get the specific heat of the gas [J/kg-K] at a given Kelvin temperature."""
918
+ return self.specific_heat_coeff_a + self.specific_heat_coeff_b * t_kelvin + \
919
+ self.specific_heat_coeff_c * t_kelvin ** 2
920
+
921
+ @classmethod
922
+ def from_idf(cls, idf_string):
923
+ """Create EnergyWindowMaterialGasCustom from an EnergyPlus text string.
924
+
925
+ Args:
926
+ idf_string: A text string fully describing an EnergyPlus material.
927
+ """
928
+ # check that the gas is, in fact custom
929
+ ep_s = parse_idf_string(idf_string, 'WindowMaterial:Gas,')
930
+ assert ep_s[1].title() == 'Custom', 'Exected Custom Gas. Got a specific one.'
931
+ assert len(ep_s) == 14, 'Not enough fields present for Custom Gas ' \
932
+ 'IDF description. Expected 14 properties. Got {}.'.format(len(ep_s))
933
+ ep_s.pop(1)
934
+ # reorder the coefficients
935
+ start = [ep_s[0], ep_s[1]]
936
+ a_coef = [ep_s[2], ep_s[5], ep_s[8]]
937
+ b_coef = [ep_s[3], ep_s[6], ep_s[9]]
938
+ c_coef = [ep_s[4], ep_s[7], ep_s[10]]
939
+ end = [ep_s[12], ep_s[11]]
940
+ eps_cl = start + a_coef + b_coef + c_coef + end
941
+ # assume that any blank strings are just coefficients of 0
942
+ for i, val in enumerate(eps_cl[2:11]):
943
+ clean_val = val if val != '' else 0
944
+ eps_cl[i + 2] = clean_val
945
+ return cls(*eps_cl)
946
+
947
+ @classmethod
948
+ def from_dict(cls, data):
949
+ """Create a EnergyWindowMaterialGasCustom from a dictionary.
950
+
951
+ Args:
952
+ data: A python dictionary in the following format
953
+
954
+ .. code-block:: python
955
+
956
+ {
957
+ "type": 'EnergyWindowMaterialGasCustom',
958
+ "identifier": 'CO2_0010_00146_0000014_82773_140_44',
959
+ "display_name": 'CO2'
960
+ "thickness": 0.01,
961
+ "conductivity_coeff_a": 0.0146,
962
+ "viscosity_coeff_a": 0.000014,
963
+ "specific_heat_coeff_a": 827.73,
964
+ "specific_heat_ratio": 1.4
965
+ "molecular_weight": 44
966
+ }
967
+ """
968
+ assert data['type'] == 'EnergyWindowMaterialGasCustom', \
969
+ 'Expected EnergyWindowMaterialGasCustom. Got {}.'.format(data['type'])
970
+ con_b = 0 if 'conductivity_coeff_b' not in data else data['conductivity_coeff_b']
971
+ vis_b = 0 if 'viscosity_coeff_b' not in data else data['viscosity_coeff_b']
972
+ sph_b = 0 if 'specific_heat_coeff_b' not in data \
973
+ else data['specific_heat_coeff_b']
974
+ con_c = 0 if 'conductivity_coeff_c' not in data else data['conductivity_coeff_c']
975
+ vis_c = 0 if 'viscosity_coeff_c' not in data else data['viscosity_coeff_c']
976
+ sph_c = 0 if 'specific_heat_coeff_c' not in data \
977
+ else data['specific_heat_coeff_c']
978
+ sphr = 1.0 if 'specific_heat_ratio' not in data else data['specific_heat_ratio']
979
+ mw = 20.0 if 'molecular_weight' not in data else data['molecular_weight']
980
+ new_obj = cls(data['identifier'], data['thickness'],
981
+ data['conductivity_coeff_a'],
982
+ data['viscosity_coeff_a'],
983
+ data['specific_heat_coeff_a'],
984
+ con_b, vis_b, sph_b, con_c, vis_c, sph_c, sphr, mw)
985
+ if 'display_name' in data and data['display_name'] is not None:
986
+ new_obj.display_name = data['display_name']
987
+ if 'user_data' in data and data['user_data'] is not None:
988
+ new_obj.user_data = data['user_data']
989
+ if 'properties' in data and data['properties'] is not None:
990
+ new_obj._properties._load_extension_attr_from_dict(data['properties'])
991
+ return new_obj
992
+
993
+ def to_idf(self):
994
+ """Get an EnergyPlus string representation of the material.
995
+
996
+ .. code-block:: shell
997
+
998
+ WindowMaterial:Gas,
999
+ Gas_16_W_0_0003, !- gap name
1000
+ Custom, !- type
1001
+ 0.0003, !- thickness
1002
+ 2.873000e-003, !- Conductivity Coefficient A
1003
+ 7.760000e-005, !- Conductivity Coefficient B
1004
+ 0.000000e+000, !- Conductivity Coefficient C
1005
+ 3.723000e-006, !- Conductivity Viscosity A
1006
+ 4.940000e-008, !- Conductivity Viscosity B
1007
+ 0.000000e+000, !- Conductivity Viscosity C
1008
+ 1002.737000, !- Specific Heat Coefficient A
1009
+ 0.012324, !- Specific Heat Coefficient B
1010
+ 0.000000, !- Specific Heat Coefficient C
1011
+ 28.969999, !- Molecular Weight
1012
+ 1.400000; !- Specific Heat Ratio
1013
+ """
1014
+ values = (self.identifier, 'Custom', self.thickness, self.conductivity_coeff_a,
1015
+ self.conductivity_coeff_b, self.conductivity_coeff_c,
1016
+ self.viscosity_coeff_a, self.viscosity_coeff_b,
1017
+ self.viscosity_coeff_c, self.specific_heat_coeff_a,
1018
+ self.specific_heat_coeff_b, self.specific_heat_coeff_c,
1019
+ self.molecular_weight, self.specific_heat_ratio)
1020
+ comments = ('name', 'gas type', 'thickness', 'conductivity coeff a',
1021
+ 'conductivity coeff b', 'conductivity coeff c', 'viscosity coeff a',
1022
+ 'viscosity coeff b', 'viscosity coeff c', 'specific heat coeff a',
1023
+ 'specific heat coeff b', 'specific heat coeff c',
1024
+ 'molecular weight', 'specific heat ratio')
1025
+ return generate_idf_string('WindowMaterial:Gas', values, comments)
1026
+
1027
+ def to_dict(self):
1028
+ """Energy Material Gas Custom dictionary representation."""
1029
+ base = {
1030
+ 'type': 'EnergyWindowMaterialGasCustom',
1031
+ 'identifier': self.identifier,
1032
+ 'thickness': self.thickness,
1033
+ 'conductivity_coeff_a': self.conductivity_coeff_a,
1034
+ 'viscosity_coeff_a': self.viscosity_coeff_a,
1035
+ 'specific_heat_coeff_a': self.specific_heat_coeff_a,
1036
+ 'conductivity_coeff_b': self.conductivity_coeff_b,
1037
+ 'viscosity_coeff_b': self.viscosity_coeff_b,
1038
+ 'specific_heat_coeff_b': self.specific_heat_coeff_b,
1039
+ 'conductivity_coeff_c': self.conductivity_coeff_c,
1040
+ 'viscosity_coeff_c': self.viscosity_coeff_c,
1041
+ 'specific_heat_coeff_c': self.specific_heat_coeff_c,
1042
+ 'specific_heat_ratio': self.specific_heat_ratio,
1043
+ 'molecular_weight': self.molecular_weight
1044
+ }
1045
+ if self._display_name is not None:
1046
+ base['display_name'] = self.display_name
1047
+ if self._user_data is not None:
1048
+ base['user_data'] = self.user_data
1049
+ prop_dict = self._properties.to_dict()
1050
+ if prop_dict is not None:
1051
+ base['properties'] = prop_dict
1052
+ return base
1053
+
1054
+ def __key(self):
1055
+ """A tuple based on the object properties, useful for hashing."""
1056
+ return (self.identifier, self.thickness, self.conductivity_coeff_a,
1057
+ self.viscosity_coeff_a, self.specific_heat_coeff_a,
1058
+ self.conductivity_coeff_b, self.viscosity_coeff_b,
1059
+ self.specific_heat_coeff_b, self.conductivity_coeff_c,
1060
+ self.viscosity_coeff_c, self.specific_heat_coeff_c,
1061
+ self.specific_heat_ratio, self.molecular_weight)
1062
+
1063
+ def __hash__(self):
1064
+ return hash(self.__key())
1065
+
1066
+ def __eq__(self, other):
1067
+ return isinstance(other, EnergyWindowMaterialGasCustom) and \
1068
+ self.__key() == other.__key()
1069
+
1070
+ def __ne__(self, other):
1071
+ return not self.__eq__(other)
1072
+
1073
+ def __repr__(self):
1074
+ return self.to_idf()
1075
+
1076
+ def __copy__(self):
1077
+ new_obj = EnergyWindowMaterialGasCustom(
1078
+ self.identifier, self.thickness, self.conductivity_coeff_a,
1079
+ self.viscosity_coeff_a, self.specific_heat_coeff_a,
1080
+ self.conductivity_coeff_b, self.viscosity_coeff_b,
1081
+ self.specific_heat_coeff_b, self.conductivity_coeff_c,
1082
+ self.viscosity_coeff_c, self.specific_heat_coeff_c,
1083
+ self.specific_heat_ratio, self.molecular_weight)
1084
+ new_obj._display_name = self._display_name
1085
+ new_obj._user_data = None if self._user_data is None else self._user_data.copy()
1086
+ new_obj._properties._duplicate_extension_attr(self._properties)
1087
+ return new_obj