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.
- honeybee_energy/__init__.py +24 -0
- honeybee_energy/__main__.py +4 -0
- honeybee_energy/_extend_honeybee.py +145 -0
- honeybee_energy/altnumber.py +21 -0
- honeybee_energy/baseline/__init__.py +2 -0
- honeybee_energy/baseline/create.py +608 -0
- honeybee_energy/baseline/data/__init__.py +1 -0
- honeybee_energy/baseline/data/constructions.csv +64 -0
- honeybee_energy/baseline/data/fen_ratios.csv +15 -0
- honeybee_energy/baseline/data/lpd_building.csv +21 -0
- honeybee_energy/baseline/data/pci_2016.csv +22 -0
- honeybee_energy/baseline/data/pci_2019.csv +22 -0
- honeybee_energy/baseline/data/pci_2022.csv +22 -0
- honeybee_energy/baseline/data/shw.csv +21 -0
- honeybee_energy/baseline/pci.py +512 -0
- honeybee_energy/baseline/result.py +371 -0
- honeybee_energy/boundarycondition.py +128 -0
- honeybee_energy/cli/__init__.py +69 -0
- honeybee_energy/cli/baseline.py +475 -0
- honeybee_energy/cli/edit.py +327 -0
- honeybee_energy/cli/lib.py +1154 -0
- honeybee_energy/cli/result.py +810 -0
- honeybee_energy/cli/setconfig.py +124 -0
- honeybee_energy/cli/settings.py +569 -0
- honeybee_energy/cli/simulate.py +380 -0
- honeybee_energy/cli/translate.py +1714 -0
- honeybee_energy/cli/validate.py +224 -0
- honeybee_energy/config.json +11 -0
- honeybee_energy/config.py +842 -0
- honeybee_energy/construction/__init__.py +1 -0
- honeybee_energy/construction/_base.py +374 -0
- honeybee_energy/construction/air.py +325 -0
- honeybee_energy/construction/dictutil.py +89 -0
- honeybee_energy/construction/dynamic.py +607 -0
- honeybee_energy/construction/opaque.py +460 -0
- honeybee_energy/construction/shade.py +319 -0
- honeybee_energy/construction/window.py +1096 -0
- honeybee_energy/construction/windowshade.py +847 -0
- honeybee_energy/constructionset.py +1655 -0
- honeybee_energy/dictutil.py +56 -0
- honeybee_energy/generator/__init__.py +5 -0
- honeybee_energy/generator/loadcenter.py +204 -0
- honeybee_energy/generator/pv.py +535 -0
- honeybee_energy/hvac/__init__.py +21 -0
- honeybee_energy/hvac/_base.py +124 -0
- honeybee_energy/hvac/_template.py +270 -0
- honeybee_energy/hvac/allair/__init__.py +22 -0
- honeybee_energy/hvac/allair/_base.py +349 -0
- honeybee_energy/hvac/allair/furnace.py +168 -0
- honeybee_energy/hvac/allair/psz.py +131 -0
- honeybee_energy/hvac/allair/ptac.py +163 -0
- honeybee_energy/hvac/allair/pvav.py +109 -0
- honeybee_energy/hvac/allair/vav.py +128 -0
- honeybee_energy/hvac/detailed.py +337 -0
- honeybee_energy/hvac/doas/__init__.py +28 -0
- honeybee_energy/hvac/doas/_base.py +345 -0
- honeybee_energy/hvac/doas/fcu.py +127 -0
- honeybee_energy/hvac/doas/radiant.py +329 -0
- honeybee_energy/hvac/doas/vrf.py +81 -0
- honeybee_energy/hvac/doas/wshp.py +91 -0
- honeybee_energy/hvac/heatcool/__init__.py +23 -0
- honeybee_energy/hvac/heatcool/_base.py +177 -0
- honeybee_energy/hvac/heatcool/baseboard.py +61 -0
- honeybee_energy/hvac/heatcool/evapcool.py +72 -0
- honeybee_energy/hvac/heatcool/fcu.py +92 -0
- honeybee_energy/hvac/heatcool/gasunit.py +53 -0
- honeybee_energy/hvac/heatcool/radiant.py +269 -0
- honeybee_energy/hvac/heatcool/residential.py +77 -0
- honeybee_energy/hvac/heatcool/vrf.py +54 -0
- honeybee_energy/hvac/heatcool/windowac.py +70 -0
- honeybee_energy/hvac/heatcool/wshp.py +62 -0
- honeybee_energy/hvac/idealair.py +699 -0
- honeybee_energy/internalmass.py +310 -0
- honeybee_energy/lib/__init__.py +1 -0
- honeybee_energy/lib/_loadconstructions.py +194 -0
- honeybee_energy/lib/_loadconstructionsets.py +117 -0
- honeybee_energy/lib/_loadmaterials.py +83 -0
- honeybee_energy/lib/_loadprogramtypes.py +125 -0
- honeybee_energy/lib/_loadschedules.py +87 -0
- honeybee_energy/lib/_loadtypelimits.py +64 -0
- honeybee_energy/lib/constructions.py +207 -0
- honeybee_energy/lib/constructionsets.py +95 -0
- honeybee_energy/lib/materials.py +67 -0
- honeybee_energy/lib/programtypes.py +125 -0
- honeybee_energy/lib/schedules.py +61 -0
- honeybee_energy/lib/scheduletypelimits.py +31 -0
- honeybee_energy/load/__init__.py +1 -0
- honeybee_energy/load/_base.py +190 -0
- honeybee_energy/load/daylight.py +397 -0
- honeybee_energy/load/dictutil.py +47 -0
- honeybee_energy/load/equipment.py +771 -0
- honeybee_energy/load/hotwater.py +543 -0
- honeybee_energy/load/infiltration.py +460 -0
- honeybee_energy/load/lighting.py +480 -0
- honeybee_energy/load/people.py +497 -0
- honeybee_energy/load/process.py +472 -0
- honeybee_energy/load/setpoint.py +816 -0
- honeybee_energy/load/ventilation.py +550 -0
- honeybee_energy/material/__init__.py +1 -0
- honeybee_energy/material/_base.py +166 -0
- honeybee_energy/material/dictutil.py +59 -0
- honeybee_energy/material/frame.py +367 -0
- honeybee_energy/material/gas.py +1087 -0
- honeybee_energy/material/glazing.py +854 -0
- honeybee_energy/material/opaque.py +1351 -0
- honeybee_energy/material/shade.py +1360 -0
- honeybee_energy/measure.py +472 -0
- honeybee_energy/programtype.py +723 -0
- honeybee_energy/properties/__init__.py +1 -0
- honeybee_energy/properties/aperture.py +333 -0
- honeybee_energy/properties/door.py +342 -0
- honeybee_energy/properties/extension.py +244 -0
- honeybee_energy/properties/face.py +274 -0
- honeybee_energy/properties/model.py +2640 -0
- honeybee_energy/properties/room.py +1747 -0
- honeybee_energy/properties/shade.py +314 -0
- honeybee_energy/properties/shademesh.py +262 -0
- honeybee_energy/reader.py +48 -0
- honeybee_energy/result/__init__.py +1 -0
- honeybee_energy/result/colorobj.py +648 -0
- honeybee_energy/result/emissions.py +290 -0
- honeybee_energy/result/err.py +101 -0
- honeybee_energy/result/eui.py +100 -0
- honeybee_energy/result/generation.py +160 -0
- honeybee_energy/result/loadbalance.py +890 -0
- honeybee_energy/result/match.py +202 -0
- honeybee_energy/result/osw.py +90 -0
- honeybee_energy/result/rdd.py +59 -0
- honeybee_energy/result/zsz.py +190 -0
- honeybee_energy/run.py +1577 -0
- honeybee_energy/schedule/__init__.py +1 -0
- honeybee_energy/schedule/day.py +626 -0
- honeybee_energy/schedule/dictutil.py +59 -0
- honeybee_energy/schedule/fixedinterval.py +1012 -0
- honeybee_energy/schedule/rule.py +619 -0
- honeybee_energy/schedule/ruleset.py +1867 -0
- honeybee_energy/schedule/typelimit.py +310 -0
- honeybee_energy/shw.py +315 -0
- honeybee_energy/simulation/__init__.py +1 -0
- honeybee_energy/simulation/control.py +214 -0
- honeybee_energy/simulation/daylightsaving.py +185 -0
- honeybee_energy/simulation/dictutil.py +51 -0
- honeybee_energy/simulation/output.py +646 -0
- honeybee_energy/simulation/parameter.py +606 -0
- honeybee_energy/simulation/runperiod.py +443 -0
- honeybee_energy/simulation/shadowcalculation.py +295 -0
- honeybee_energy/simulation/sizing.py +546 -0
- honeybee_energy/ventcool/__init__.py +5 -0
- honeybee_energy/ventcool/_crack_data.py +91 -0
- honeybee_energy/ventcool/afn.py +289 -0
- honeybee_energy/ventcool/control.py +269 -0
- honeybee_energy/ventcool/crack.py +126 -0
- honeybee_energy/ventcool/fan.py +493 -0
- honeybee_energy/ventcool/opening.py +365 -0
- honeybee_energy/ventcool/simulation.py +314 -0
- honeybee_energy/writer.py +1078 -0
- honeybee_energy-1.116.106.dist-info/METADATA +113 -0
- honeybee_energy-1.116.106.dist-info/RECORD +162 -0
- honeybee_energy-1.116.106.dist-info/WHEEL +5 -0
- honeybee_energy-1.116.106.dist-info/entry_points.txt +2 -0
- honeybee_energy-1.116.106.dist-info/licenses/LICENSE +661 -0
- honeybee_energy-1.116.106.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""Opaque Construction."""
|
|
3
|
+
from __future__ import division
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
from honeybee._lockable import lockable
|
|
8
|
+
|
|
9
|
+
from ._base import _ConstructionBase
|
|
10
|
+
from ..material.dictutil import dict_to_material
|
|
11
|
+
from ..material._base import _EnergyMaterialOpaqueBase
|
|
12
|
+
from ..material.opaque import EnergyMaterial, EnergyMaterialNoMass, \
|
|
13
|
+
EnergyMaterialVegetation
|
|
14
|
+
from ..reader import parse_idf_string, clean_idf_file_contents
|
|
15
|
+
from ..properties.extension import OpaqueConstructionProperties
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@lockable
|
|
19
|
+
class OpaqueConstruction(_ConstructionBase):
|
|
20
|
+
"""Opaque energy construction.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
identifier: Text string for a unique Construction ID. Must be < 100 characters
|
|
24
|
+
and not contain any EnergyPlus special characters. This will be used to
|
|
25
|
+
identify the object across a model and in the exported IDF.
|
|
26
|
+
materials: List of materials in the construction (from outside to inside).
|
|
27
|
+
All materials must be opaque and a maximum of 10 materials are allowed.
|
|
28
|
+
|
|
29
|
+
Properties:
|
|
30
|
+
* identifier
|
|
31
|
+
* display_name
|
|
32
|
+
* materials
|
|
33
|
+
* layers
|
|
34
|
+
* unique_materials
|
|
35
|
+
* r_value
|
|
36
|
+
* u_value
|
|
37
|
+
* u_factor
|
|
38
|
+
* r_factor
|
|
39
|
+
* is_symmetric
|
|
40
|
+
* has_frame
|
|
41
|
+
* has_shade
|
|
42
|
+
* is_dynamic
|
|
43
|
+
* inside_emissivity
|
|
44
|
+
* inside_solar_reflectance
|
|
45
|
+
* inside_visible_reflectance
|
|
46
|
+
* outside_emissivity
|
|
47
|
+
* outside_solar_reflectance
|
|
48
|
+
* outside_visible_reflectance
|
|
49
|
+
* mass_area_density
|
|
50
|
+
* area_heat_capacity
|
|
51
|
+
* thickness
|
|
52
|
+
* inside_material
|
|
53
|
+
* outside_material
|
|
54
|
+
* user_data
|
|
55
|
+
* properties
|
|
56
|
+
"""
|
|
57
|
+
__slots__ = ()
|
|
58
|
+
|
|
59
|
+
def __init__(self, identifier, materials):
|
|
60
|
+
"""Initialize opaque construction."""
|
|
61
|
+
_ConstructionBase.__init__(self, identifier, materials)
|
|
62
|
+
self._properties = OpaqueConstructionProperties(self)
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def from_simple_parameters(
|
|
66
|
+
cls, identifier, r_value, roughness='MediumRough',
|
|
67
|
+
thermal_absorptance=0.9, solar_absorptance=0.7, visible_absorptance=None
|
|
68
|
+
):
|
|
69
|
+
"""Create a no-mass OpaqueConstruction from a specification of simple parameters.
|
|
70
|
+
|
|
71
|
+
The result will have a single EnergyMaterialNoMass layer that is derived
|
|
72
|
+
from the input parameters.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
identifier: Text string for a unique construction ID.
|
|
76
|
+
Must be <90 characters and not contain any EnergyPlus special
|
|
77
|
+
characters. This will be used to identify the object across a model
|
|
78
|
+
and in the exported IDF.
|
|
79
|
+
r_value: Number for the R-value of the construction [m2-K/W].
|
|
80
|
+
roughness: Text describing the relative roughness of the construction.
|
|
81
|
+
Must be one of the following: 'VeryRough', 'Rough', 'MediumRough',
|
|
82
|
+
'MediumSmooth', 'Smooth', 'VerySmooth'. Default: 'MediumRough'.
|
|
83
|
+
thermal_absorptance: A number between 0 and 1 for the fraction of
|
|
84
|
+
incident long wavelength radiation that is absorbed by the construction.
|
|
85
|
+
Default: 0.9.
|
|
86
|
+
solar_absorptance: A number between 0 and 1 for the fraction of incident
|
|
87
|
+
solar radiation absorbed by the construction. Default: 0.7.
|
|
88
|
+
visible_absorptance: A number between 0 and 1 for the fraction of incident
|
|
89
|
+
visible wavelength radiation absorbed by the construction.
|
|
90
|
+
Default value is None, which will use the same value as the
|
|
91
|
+
solar_absorptance.
|
|
92
|
+
"""
|
|
93
|
+
mat_layer = EnergyMaterialNoMass(
|
|
94
|
+
'{} Material'.format(identifier), r_value, roughness,
|
|
95
|
+
thermal_absorptance, solar_absorptance, visible_absorptance
|
|
96
|
+
)
|
|
97
|
+
return cls(identifier, (mat_layer,))
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def materials(self):
|
|
101
|
+
"""Get or set the list of materials in the construction (outside to inside).
|
|
102
|
+
|
|
103
|
+
All materials must be opaque and a maximum of 10 materials are allowed.
|
|
104
|
+
"""
|
|
105
|
+
return self._materials
|
|
106
|
+
|
|
107
|
+
@materials.setter
|
|
108
|
+
def materials(self, mats):
|
|
109
|
+
try:
|
|
110
|
+
if not isinstance(mats, tuple):
|
|
111
|
+
mats = tuple(mats)
|
|
112
|
+
except TypeError:
|
|
113
|
+
raise TypeError('Expected list or tuple for construction materials. '
|
|
114
|
+
'Got {}'.format(type(mats)))
|
|
115
|
+
for i, mat in enumerate(mats):
|
|
116
|
+
assert isinstance(mat, _EnergyMaterialOpaqueBase), 'Expected opaque energy' \
|
|
117
|
+
' material for construction. Got {}.'.format(type(mat))
|
|
118
|
+
if isinstance(mat, EnergyMaterialVegetation):
|
|
119
|
+
assert i == 0, 'Vegetation material layer must bet the first ' \
|
|
120
|
+
'(exterior) layer in the construction.'
|
|
121
|
+
assert len(mats) > 0, 'Construction must possess at least one material.'
|
|
122
|
+
assert len(mats) <= 10, 'Opaque Construction cannot have more than 10 materials.'
|
|
123
|
+
self._materials = mats
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def inside_solar_reflectance(self):
|
|
127
|
+
"""The solar reflectance of the inside face of the construction."""
|
|
128
|
+
return 1 - self.materials[-1].solar_absorptance
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def inside_visible_reflectance(self):
|
|
132
|
+
"""The visible reflectance of the inside face of the construction."""
|
|
133
|
+
return 1 - self.materials[-1].visible_absorptance
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def outside_solar_reflectance(self):
|
|
137
|
+
"""The solar reflectance of the outside face of the construction."""
|
|
138
|
+
return 1 - self.materials[0].solar_absorptance
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def outside_visible_reflectance(self):
|
|
142
|
+
"""The visible reflectance of the outside face of the construction."""
|
|
143
|
+
return 1 - self.materials[0].visible_absorptance
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def mass_area_density(self):
|
|
147
|
+
"""The area density of the construction [kg/m2]."""
|
|
148
|
+
return sum(tuple(mat.mass_area_density for mat in self.materials))
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def area_heat_capacity(self):
|
|
152
|
+
"""The heat capacity per unit area of the construction [J/K-m2]."""
|
|
153
|
+
return sum(tuple(mat.area_heat_capacity for mat in self.materials))
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def thickness(self):
|
|
157
|
+
"""Thickness of the construction [m]."""
|
|
158
|
+
thickness = 0
|
|
159
|
+
for mat in self.materials:
|
|
160
|
+
thickness += mat.thickness
|
|
161
|
+
return thickness
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def inside_material(self):
|
|
165
|
+
"""The the inside material layer of the construction.
|
|
166
|
+
|
|
167
|
+
Useful for checking that an asymmetric construction is correctly assigned.
|
|
168
|
+
"""
|
|
169
|
+
return self.materials[-1]
|
|
170
|
+
|
|
171
|
+
@property
|
|
172
|
+
def outside_material(self):
|
|
173
|
+
"""The the outside material layer of the construction.
|
|
174
|
+
|
|
175
|
+
Useful for checking that an asymmetric construction is correctly assigned.
|
|
176
|
+
"""
|
|
177
|
+
return self.materials[0]
|
|
178
|
+
|
|
179
|
+
def temperature_profile(self, outside_temperature=-18, inside_temperature=21,
|
|
180
|
+
outside_wind_speed=6.7, solar_irradiance=0,
|
|
181
|
+
height=1.0, angle=90.0, pressure=101325):
|
|
182
|
+
"""Get a list of temperatures at each material boundary across the construction.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
outside_temperature: The temperature on the outside of the
|
|
186
|
+
construction [C]. (Default: -18, consistent with NFRC 100-2010).
|
|
187
|
+
inside_temperature: The temperature on the inside of the
|
|
188
|
+
construction [C]. (Default: 21, consistent with NFRC 100-2010).
|
|
189
|
+
wind_speed: The average outdoor wind speed [m/s]. This affects outdoor
|
|
190
|
+
convective heat transfer coefficient. (Default: 6.7 m/s).
|
|
191
|
+
solar_irradiance: An optional value for solar irradiance that is incident
|
|
192
|
+
on the front (exterior) of the construction [W/m2]. (Default: 0 W/m2).
|
|
193
|
+
height: An optional height for the surface in meters. (Default: 1.0 m).
|
|
194
|
+
angle: An angle in degrees between 0 and 180.
|
|
195
|
+
|
|
196
|
+
* 0 = A horizontal surface with the outside boundary on the top.
|
|
197
|
+
* 90 = A vertical surface
|
|
198
|
+
* 180 = A horizontal surface with the outside boundary on the bottom.
|
|
199
|
+
|
|
200
|
+
pressure: The average pressure of in Pa. (Default: 101325 Pa for
|
|
201
|
+
standard pressure at sea level).
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
A tuple with two elements
|
|
205
|
+
|
|
206
|
+
- temperatures: A list of temperature values [C].
|
|
207
|
+
The first value will always be the outside temperature and the
|
|
208
|
+
second will be the exterior surface temperature.
|
|
209
|
+
The last value will always be the inside temperature and the second
|
|
210
|
+
to last will be the interior surface temperature.
|
|
211
|
+
|
|
212
|
+
- r_values: A list of R-values for each of the material layers [m2-K/W].
|
|
213
|
+
The first value will always be the resistance of the exterior air
|
|
214
|
+
and the last value is the resistance of the interior air.
|
|
215
|
+
The sum of this list is the R-factor for this construction given
|
|
216
|
+
the input parameters.
|
|
217
|
+
"""
|
|
218
|
+
# reverse the angle if the outside temperature is greater than the inside one
|
|
219
|
+
if angle != 90 and outside_temperature > inside_temperature:
|
|
220
|
+
angle = abs(180 - angle)
|
|
221
|
+
|
|
222
|
+
# compute delta temperature from solar irradiance if applicable
|
|
223
|
+
heat_gen = None
|
|
224
|
+
if solar_irradiance != 0:
|
|
225
|
+
heat_gen = self.materials[0].solar_absorptance * solar_irradiance
|
|
226
|
+
|
|
227
|
+
# use the r-values to get the temperature profile
|
|
228
|
+
in_r_init = 1 / self.in_h_simple()
|
|
229
|
+
r_values = [1 / self.out_h(outside_wind_speed, outside_temperature + 273.15)] + \
|
|
230
|
+
[mat.r_value for mat in self.materials] + [in_r_init]
|
|
231
|
+
in_delta_t = (in_r_init / sum(r_values)) * \
|
|
232
|
+
(outside_temperature - inside_temperature)
|
|
233
|
+
r_values[-1] = 1 / self.in_h(inside_temperature - (in_delta_t / 2) + 273.15,
|
|
234
|
+
in_delta_t, height, angle, pressure)
|
|
235
|
+
temperatures = self._temperature_profile_from_r_values(
|
|
236
|
+
r_values, outside_temperature, inside_temperature, heat_gen)
|
|
237
|
+
return temperatures, r_values
|
|
238
|
+
|
|
239
|
+
@classmethod
|
|
240
|
+
def from_idf(cls, idf_string, ep_mat_strings):
|
|
241
|
+
"""Create an OpaqueConstruction from an EnergyPlus IDF text string.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
idf_string: A text string fully describing an EnergyPlus construction.
|
|
245
|
+
ep_mat_strings: A list of text strings for each of the materials in
|
|
246
|
+
the construction.
|
|
247
|
+
"""
|
|
248
|
+
materials_dict = cls._idf_materials_dictionary(ep_mat_strings)
|
|
249
|
+
ep_strs = parse_idf_string(idf_string)
|
|
250
|
+
try:
|
|
251
|
+
materials = [materials_dict[mat.upper()] for mat in ep_strs[1:]]
|
|
252
|
+
except KeyError as e:
|
|
253
|
+
raise ValueError('Failed to find {} in the input ep_mat_strings.'.format(e))
|
|
254
|
+
return cls(ep_strs[0], materials)
|
|
255
|
+
|
|
256
|
+
@classmethod
|
|
257
|
+
def from_dict(cls, data):
|
|
258
|
+
"""Create a OpaqueConstruction from a dictionary.
|
|
259
|
+
|
|
260
|
+
Note that the dictionary must be a non-abridged version for this
|
|
261
|
+
classmethod to work.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
data: A python dictionary in the following format
|
|
265
|
+
|
|
266
|
+
.. code-block:: python
|
|
267
|
+
|
|
268
|
+
{
|
|
269
|
+
"type": 'OpaqueConstruction',
|
|
270
|
+
"identifier": 'Generic Brick Wall R-10',
|
|
271
|
+
"display_name": 'Brick Wall',
|
|
272
|
+
"materials": [] # list of unique material objects (from outside to inside)
|
|
273
|
+
}
|
|
274
|
+
"""
|
|
275
|
+
assert data['type'] == 'OpaqueConstruction', \
|
|
276
|
+
'Expected OpaqueConstruction. Got {}.'.format(data['type'])
|
|
277
|
+
mat_layers = cls._old_schema_materials(data) if 'layers' in data else \
|
|
278
|
+
[dict_to_material(mat) for mat in data['materials']]
|
|
279
|
+
new_obj = cls(data['identifier'], mat_layers)
|
|
280
|
+
if 'display_name' in data and data['display_name'] is not None:
|
|
281
|
+
new_obj.display_name = data['display_name']
|
|
282
|
+
if 'user_data' in data and data['user_data'] is not None:
|
|
283
|
+
new_obj.user_data = data['user_data']
|
|
284
|
+
if 'properties' in data and data['properties'] is not None:
|
|
285
|
+
new_obj.properties._load_extension_attr_from_dict(data['properties'])
|
|
286
|
+
return new_obj
|
|
287
|
+
|
|
288
|
+
@classmethod
|
|
289
|
+
def from_dict_abridged(cls, data, materials):
|
|
290
|
+
"""Create a OpaqueConstruction from an abridged dictionary.
|
|
291
|
+
|
|
292
|
+
Args:
|
|
293
|
+
data: An OpaqueConstructionAbridged dictionary.
|
|
294
|
+
materials: A dictionary with identifiers of materials as keys and Python
|
|
295
|
+
material objects as values.
|
|
296
|
+
|
|
297
|
+
.. code-block:: python
|
|
298
|
+
|
|
299
|
+
{
|
|
300
|
+
"type": 'OpaqueConstructionAbridged',
|
|
301
|
+
"identifier": 'Generic Brick Wall R-10',
|
|
302
|
+
"display_name": 'Brick Wall',
|
|
303
|
+
"materials": [], # list of material identifiers (from outside to inside)
|
|
304
|
+
}
|
|
305
|
+
"""
|
|
306
|
+
assert data['type'] == 'OpaqueConstructionAbridged', \
|
|
307
|
+
'Expected OpaqueConstructionAbridged. Got {}.'.format(data['type'])
|
|
308
|
+
# handle old schema definition before May 2021 (used layers instead of materials)
|
|
309
|
+
mat_key = 'layers' if 'layers' in data else 'materials'
|
|
310
|
+
try:
|
|
311
|
+
mat_layers = [materials[mat_id] for mat_id in data[mat_key]]
|
|
312
|
+
except KeyError as e:
|
|
313
|
+
raise ValueError('Failed to find {} in materials.'.format(e))
|
|
314
|
+
new_obj = cls(data['identifier'], mat_layers)
|
|
315
|
+
if 'display_name' in data and data['display_name'] is not None:
|
|
316
|
+
new_obj.display_name = data['display_name']
|
|
317
|
+
if 'user_data' in data and data['user_data'] is not None:
|
|
318
|
+
new_obj.user_data = data['user_data']
|
|
319
|
+
if 'properties' in data and data['properties'] is not None:
|
|
320
|
+
new_obj.properties._load_extension_attr_from_dict(data['properties'])
|
|
321
|
+
return new_obj
|
|
322
|
+
|
|
323
|
+
def to_idf(self):
|
|
324
|
+
"""IDF string representation of construction object.
|
|
325
|
+
|
|
326
|
+
Note that this method only outputs a single string for the construction and,
|
|
327
|
+
to write the full construction into an IDF, the construction's unique_materials
|
|
328
|
+
must also be written.
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
construction_idf -- Text string representation of the construction.
|
|
332
|
+
|
|
333
|
+
.. code-block:: shell
|
|
334
|
+
|
|
335
|
+
Construction, FLOOR38, ! Material layer names follow:
|
|
336
|
+
E5 - ACOUSTIC TILE,
|
|
337
|
+
E4 - CEILING AIRSPACE,
|
|
338
|
+
C12 - 2 IN HW CONCRETE;
|
|
339
|
+
"""
|
|
340
|
+
return self._generate_idf_string('opaque', self.identifier, self.materials)
|
|
341
|
+
|
|
342
|
+
def to_radiance_solar_interior(self, specularity=0.0):
|
|
343
|
+
"""Honeybee Radiance modifier with the interior solar reflectance."""
|
|
344
|
+
return self.materials[-1].to_radiance_solar(specularity)
|
|
345
|
+
|
|
346
|
+
def to_radiance_visible_interior(self, specularity=0.0):
|
|
347
|
+
"""Honeybee Radiance modifier with the interior visible reflectance."""
|
|
348
|
+
return self.materials[-1].to_radiance_visible(specularity)
|
|
349
|
+
|
|
350
|
+
def to_radiance_solar_exterior(self, specularity=0.0):
|
|
351
|
+
"""Honeybee Radiance modifier with the exterior solar reflectance."""
|
|
352
|
+
return self.materials[0].to_radiance_solar(specularity)
|
|
353
|
+
|
|
354
|
+
def to_radiance_visible_exterior(self, specularity=0.0):
|
|
355
|
+
"""Honeybee Radiance modifier with the exterior visible reflectance."""
|
|
356
|
+
return self.materials[0].to_radiance_visible(specularity)
|
|
357
|
+
|
|
358
|
+
def to_dict(self, abridged=False):
|
|
359
|
+
"""Opaque construction dictionary representation.
|
|
360
|
+
|
|
361
|
+
Args:
|
|
362
|
+
abridged: Boolean to note whether the full dictionary describing the
|
|
363
|
+
object should be returned (False) or just an abridged version (True),
|
|
364
|
+
which only specifies the identifiers of material layers. Default: False.
|
|
365
|
+
"""
|
|
366
|
+
base = {'type': 'OpaqueConstruction'} if not \
|
|
367
|
+
abridged else {'type': 'OpaqueConstructionAbridged'}
|
|
368
|
+
base['identifier'] = self.identifier
|
|
369
|
+
base['materials'] = self.layers if abridged else \
|
|
370
|
+
[m.to_dict() for m in self.materials]
|
|
371
|
+
if self._display_name is not None:
|
|
372
|
+
base['display_name'] = self.display_name
|
|
373
|
+
if self._user_data is not None:
|
|
374
|
+
base['user_data'] = self.user_data
|
|
375
|
+
prop_dict = self.properties.to_dict()
|
|
376
|
+
if prop_dict is not None:
|
|
377
|
+
base['properties'] = prop_dict
|
|
378
|
+
return base
|
|
379
|
+
|
|
380
|
+
@staticmethod
|
|
381
|
+
def extract_all_from_idf_file(idf_file):
|
|
382
|
+
"""Extract all OpaqueConstruction objects from an EnergyPlus IDF file.
|
|
383
|
+
|
|
384
|
+
Args:
|
|
385
|
+
idf_file: A path to an IDF file containing objects for opaque
|
|
386
|
+
constructions and corresponding materials.
|
|
387
|
+
|
|
388
|
+
Returns:
|
|
389
|
+
A tuple with two elements
|
|
390
|
+
|
|
391
|
+
- constructions: A list of all OpaqueConstruction objects in the IDF
|
|
392
|
+
file as honeybee_energy OpaqueConstruction objects.
|
|
393
|
+
|
|
394
|
+
- materials: A list of all opaque materials in the IDF file as
|
|
395
|
+
honeybee_energy EnergyMaterial objects.
|
|
396
|
+
"""
|
|
397
|
+
# read the file and remove lines of comments
|
|
398
|
+
file_contents = clean_idf_file_contents(idf_file)
|
|
399
|
+
# extract all of the opaque material objects
|
|
400
|
+
mat_pattern1 = re.compile(r"(?i)(Material,[\s\S]*?;)")
|
|
401
|
+
mat_pattern2 = re.compile(r"(?i)(Material:NoMass,[\s\S]*?;)")
|
|
402
|
+
mat_pattern3 = re.compile(r"(?i)(Material:AirGap,[\s\S]*?;)")
|
|
403
|
+
mat_pattern4 = re.compile(r"(?i)(Material:RoofVegetation,[\s\S]*?;)")
|
|
404
|
+
material_str = mat_pattern1.findall(file_contents) + \
|
|
405
|
+
mat_pattern2.findall(file_contents) + mat_pattern3.findall(file_contents) + \
|
|
406
|
+
mat_pattern4.findall(file_contents)
|
|
407
|
+
materials_dict = OpaqueConstruction._idf_materials_dictionary(material_str)
|
|
408
|
+
materials = list(materials_dict.values())
|
|
409
|
+
# extract all of the construction objects
|
|
410
|
+
constr_pattern = re.compile(r"(?i)(Construction,[\s\S]*?;)")
|
|
411
|
+
constr_props = tuple(parse_idf_string(idf_string) for
|
|
412
|
+
idf_string in constr_pattern.findall(file_contents))
|
|
413
|
+
constructions = []
|
|
414
|
+
for constr in constr_props:
|
|
415
|
+
try:
|
|
416
|
+
constr_mats = [materials_dict[mat.upper()] for mat in constr[1:]]
|
|
417
|
+
constructions.append(OpaqueConstruction(constr[0], constr_mats))
|
|
418
|
+
except (KeyError, AssertionError):
|
|
419
|
+
pass # the construction is probably a window construction
|
|
420
|
+
return constructions, materials
|
|
421
|
+
|
|
422
|
+
@staticmethod
|
|
423
|
+
def _idf_materials_dictionary(ep_mat_strings):
|
|
424
|
+
"""Get a dictionary of opaque EnergyMaterial objects from an IDF string list."""
|
|
425
|
+
materials_dict = {}
|
|
426
|
+
for mat_str in ep_mat_strings:
|
|
427
|
+
mat_str = mat_str.strip()
|
|
428
|
+
if mat_str.startswith('Material,'):
|
|
429
|
+
mat_obj = EnergyMaterial.from_idf(mat_str)
|
|
430
|
+
materials_dict[mat_obj.identifier.upper()] = mat_obj
|
|
431
|
+
elif mat_str.startswith('Material:NoMass,'):
|
|
432
|
+
mat_obj = EnergyMaterialNoMass.from_idf(mat_str)
|
|
433
|
+
materials_dict[mat_obj.identifier.upper()] = mat_obj
|
|
434
|
+
elif mat_str.startswith('Material:AirGap,'):
|
|
435
|
+
mat_obj = EnergyMaterialNoMass.from_idf_air_gap(mat_str)
|
|
436
|
+
materials_dict[mat_obj.identifier.upper()] = mat_obj
|
|
437
|
+
elif mat_str.startswith('Material:RoofVegetation,'):
|
|
438
|
+
mat_obj = EnergyMaterialVegetation.from_idf(mat_str)
|
|
439
|
+
materials_dict[mat_obj.identifier.upper()] = mat_obj
|
|
440
|
+
return materials_dict
|
|
441
|
+
|
|
442
|
+
@staticmethod
|
|
443
|
+
def _old_schema_materials(data):
|
|
444
|
+
"""Get material objects from an old schema definition of OpaqueConstruction.
|
|
445
|
+
|
|
446
|
+
The schema is from before May 2021 and this method should eventually be removed.
|
|
447
|
+
"""
|
|
448
|
+
materials = {}
|
|
449
|
+
for mat in data['materials']:
|
|
450
|
+
materials[mat['identifier']] = dict_to_material(mat)
|
|
451
|
+
try:
|
|
452
|
+
mat_layers = [materials[mat_id] for mat_id in data['layers']]
|
|
453
|
+
except KeyError as e:
|
|
454
|
+
raise ValueError(
|
|
455
|
+
'Failed to find {} in opaque construction materials.'.format(e))
|
|
456
|
+
return mat_layers
|
|
457
|
+
|
|
458
|
+
def __repr__(self):
|
|
459
|
+
"""Represent opaque energy construction."""
|
|
460
|
+
return self._generate_idf_string('opaque', self.identifier, self.materials)
|