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,1078 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""Methods to write to idf."""
|
|
3
|
+
from .config import folders
|
|
4
|
+
|
|
5
|
+
from ladybug_geometry.geometry3d import Face3D
|
|
6
|
+
from honeybee.room import Room
|
|
7
|
+
from honeybee.face import Face
|
|
8
|
+
from honeybee.boundarycondition import Outdoors, Surface, Ground, boundary_conditions
|
|
9
|
+
from honeybee.facetype import Wall, Floor, RoofCeiling, AirBoundary
|
|
10
|
+
from honeybee.units import parse_distance_string, conversion_factor_to_meters
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
from itertools import izip as zip # python 2
|
|
14
|
+
except ImportError:
|
|
15
|
+
xrange = range # python 3
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def generate_idf_string(object_type, values, comments=None):
|
|
19
|
+
"""Get an IDF string representation of an EnergyPlus object.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
object_type: Text representing the expected start of the IDF object.
|
|
23
|
+
(ie. WindowMaterial:Glazing).
|
|
24
|
+
values: A list of values associated with the EnergyPlus object in the
|
|
25
|
+
order that they are supposed to be written to IDF format.
|
|
26
|
+
comments: A list of text comments with the same length as the values.
|
|
27
|
+
If None, no comments will be written into the object.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
ep_str -- Am EnergyPlus IDF string representing a single object.
|
|
31
|
+
"""
|
|
32
|
+
if comments is not None:
|
|
33
|
+
space_count = tuple((25 - len(str(n))) for n in values)
|
|
34
|
+
spaces = tuple(s_c * ' ' if s_c > 0 else ' ' for s_c in space_count)
|
|
35
|
+
body_str = '\n '.join('{},{}!- {}'.format(val, spc, com) for val, spc, com in
|
|
36
|
+
zip(values[:-1], spaces[:-1], comments[:-1]))
|
|
37
|
+
ep_str = '{},\n {}'.format(object_type, body_str)
|
|
38
|
+
if len(values) == 1: # ensure we don't have an extra line break
|
|
39
|
+
ep_str = ''.join(
|
|
40
|
+
(ep_str, '{};{}!- {}'.format(values[-1], spaces[-1], comments[-1])))
|
|
41
|
+
else: # include an extra line break
|
|
42
|
+
end_str = '\n {};{}!- {}'.format(values[-1], spaces[-1], comments[-1]) \
|
|
43
|
+
if comments[-1] != '' else '\n {};'.format(values[-1])
|
|
44
|
+
ep_str = ''.join((ep_str, end_str))
|
|
45
|
+
else:
|
|
46
|
+
body_str = '\n '.join('{},'.format(val) for val in values[:-1])
|
|
47
|
+
ep_str = '{},\n {}'.format(object_type, body_str)
|
|
48
|
+
if len(values) == 1: # ensure we don't have an extra line break
|
|
49
|
+
ep_str = ''.join((ep_str, '{};'.format(values[-1])))
|
|
50
|
+
else: # include an extra line break
|
|
51
|
+
ep_str = ''.join((ep_str, '\n {};'.format(values[-1])))
|
|
52
|
+
return ep_str
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def shade_mesh_to_idf(shade_mesh):
|
|
56
|
+
"""Generate an IDF string representation of a ShadeMesh.
|
|
57
|
+
|
|
58
|
+
Note that the resulting string will possess both the Shading object
|
|
59
|
+
as well as a ShadingProperty:Reflectance if the Shade's construction
|
|
60
|
+
is not in line with the EnergyPlus default of 0.2 reflectance.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
shade_mesh: A honeybee ShadeMesh for which an IDF representation
|
|
64
|
+
will be returned.
|
|
65
|
+
"""
|
|
66
|
+
trans_sched = shade_mesh.properties.energy.transmittance_schedule.identifier if \
|
|
67
|
+
shade_mesh.properties.energy.transmittance_schedule is not None else ''
|
|
68
|
+
all_shd_str = []
|
|
69
|
+
for i, shade in enumerate(shade_mesh.geometry.face_vertices):
|
|
70
|
+
# process the geometry to get upper-left vertices
|
|
71
|
+
shade_face = Face3D(shade)
|
|
72
|
+
ul_verts = shade_face.upper_left_counter_clockwise_vertices
|
|
73
|
+
|
|
74
|
+
# create the Shading:Detailed IDF string
|
|
75
|
+
values = (
|
|
76
|
+
'{}_{}'.format(shade_mesh.identifier, i),
|
|
77
|
+
trans_sched,
|
|
78
|
+
len(ul_verts),
|
|
79
|
+
',\n '.join('%.3f, %.3f, %.3f' % (v.x, v.y, v.z) for v in ul_verts)
|
|
80
|
+
)
|
|
81
|
+
comments = (
|
|
82
|
+
'name',
|
|
83
|
+
'transmittance schedule',
|
|
84
|
+
'number of vertices',
|
|
85
|
+
''
|
|
86
|
+
)
|
|
87
|
+
shade_str = generate_idf_string('Shading:Building:Detailed', values, comments)
|
|
88
|
+
all_shd_str.append(shade_str)
|
|
89
|
+
|
|
90
|
+
# create the ShadingProperty:Reflectance if construction is not default
|
|
91
|
+
construction = shade_mesh.properties.energy.construction
|
|
92
|
+
if not construction.is_default:
|
|
93
|
+
values = (
|
|
94
|
+
shade_mesh.identifier,
|
|
95
|
+
construction.solar_reflectance,
|
|
96
|
+
construction.visible_reflectance
|
|
97
|
+
)
|
|
98
|
+
comments = (
|
|
99
|
+
'shading surface name',
|
|
100
|
+
'diffuse solar reflectance',
|
|
101
|
+
'diffuse visible reflectance'
|
|
102
|
+
)
|
|
103
|
+
if construction.is_specular:
|
|
104
|
+
values = values + (1, construction.identifier)
|
|
105
|
+
comments = comments + ('glazed fraction', 'glazing construction')
|
|
106
|
+
constr_str = generate_idf_string(
|
|
107
|
+
'ShadingProperty:Reflectance', values, comments)
|
|
108
|
+
all_shd_str.append(constr_str)
|
|
109
|
+
|
|
110
|
+
return '\n\n'.join(all_shd_str)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def shade_to_idf(shade):
|
|
114
|
+
"""Generate an IDF string representation of a Shade.
|
|
115
|
+
|
|
116
|
+
Note that the resulting string will possess both the Shading object
|
|
117
|
+
as well as a ShadingProperty:Reflectance if the Shade's construction
|
|
118
|
+
is not in line with the EnergyPlus default of 0.2 reflectance.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
shade: A honeybee Shade for which an IDF representation will be returned.
|
|
122
|
+
"""
|
|
123
|
+
# create the Shading:Detailed IDF string
|
|
124
|
+
trans_sched = shade.properties.energy.transmittance_schedule.identifier if \
|
|
125
|
+
shade.properties.energy.transmittance_schedule is not None else ''
|
|
126
|
+
ul_verts = shade.upper_left_vertices
|
|
127
|
+
if shade.has_parent and not isinstance(shade.parent, Room):
|
|
128
|
+
if isinstance(shade.parent, Face):
|
|
129
|
+
base_srf = shade.parent.identifier
|
|
130
|
+
else: # aperture or door for parent
|
|
131
|
+
try:
|
|
132
|
+
base_srf = shade.parent.parent.identifier
|
|
133
|
+
except AttributeError:
|
|
134
|
+
base_srf = 'unknown' # aperture without a parent (not simulate-able)
|
|
135
|
+
values = (
|
|
136
|
+
shade.identifier,
|
|
137
|
+
base_srf,
|
|
138
|
+
trans_sched,
|
|
139
|
+
len(shade.vertices),
|
|
140
|
+
',\n '.join('%.3f, %.3f, %.3f' % (v.x, v.y, v.z) for v in ul_verts)
|
|
141
|
+
)
|
|
142
|
+
comments = (
|
|
143
|
+
'name',
|
|
144
|
+
'base surface',
|
|
145
|
+
'transmittance schedule',
|
|
146
|
+
'number of vertices',
|
|
147
|
+
''
|
|
148
|
+
)
|
|
149
|
+
shade_str = generate_idf_string('Shading:Zone:Detailed', values, comments)
|
|
150
|
+
else: # orphaned shade
|
|
151
|
+
values = (
|
|
152
|
+
shade.identifier,
|
|
153
|
+
trans_sched,
|
|
154
|
+
len(shade.vertices),
|
|
155
|
+
',\n '.join('%.3f, %.3f, %.3f' % (v.x, v.y, v.z) for v in ul_verts)
|
|
156
|
+
)
|
|
157
|
+
comments = (
|
|
158
|
+
'name',
|
|
159
|
+
'transmittance schedule',
|
|
160
|
+
'number of vertices',
|
|
161
|
+
''
|
|
162
|
+
)
|
|
163
|
+
shade_str = generate_idf_string('Shading:Building:Detailed', values, comments)
|
|
164
|
+
|
|
165
|
+
# create the ShadingProperty:Reflectance IDF string if construction is not default
|
|
166
|
+
construction = shade.properties.energy.construction
|
|
167
|
+
if construction.is_default:
|
|
168
|
+
return shade_str
|
|
169
|
+
else:
|
|
170
|
+
values = (
|
|
171
|
+
shade.identifier,
|
|
172
|
+
construction.solar_reflectance,
|
|
173
|
+
construction.visible_reflectance
|
|
174
|
+
)
|
|
175
|
+
comments = (
|
|
176
|
+
'shading surface name',
|
|
177
|
+
'diffuse solar reflectance',
|
|
178
|
+
'diffuse visible reflectance'
|
|
179
|
+
)
|
|
180
|
+
if construction.is_specular:
|
|
181
|
+
values = values + (1, construction.identifier)
|
|
182
|
+
comments = comments + ('glazed fraction of surface', 'glazing construction')
|
|
183
|
+
constr_str = generate_idf_string('ShadingProperty:Reflectance', values, comments)
|
|
184
|
+
return '\n\n'.join((shade_str, constr_str))
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def door_to_idf(door):
|
|
188
|
+
"""Generate an IDF string representation of a Door.
|
|
189
|
+
|
|
190
|
+
Note that the resulting string does not include full construction definitions
|
|
191
|
+
but it will include a WindowShadingControl definition if a WindowConstructionShade
|
|
192
|
+
is assigned to the door. It will also include a ventilation object if the door
|
|
193
|
+
has a VentilationOpening object assigned to it.
|
|
194
|
+
|
|
195
|
+
Also note that shades assigned to the Door are not included in the resulting
|
|
196
|
+
string. To write these objects into a final string, you must loop through the
|
|
197
|
+
Door.shades, and call the to.idf method on each one.
|
|
198
|
+
|
|
199
|
+
If the input door is orphaned, the resulting string will possess both the
|
|
200
|
+
Shading object as well as a ShadingProperty:Reflectance that aligns with the
|
|
201
|
+
Doors's exterior construction properties. However, a transmittance schedule
|
|
202
|
+
that matches the transmittance of the window construction will only be
|
|
203
|
+
referenced and not included in the resulting string. All transmittance schedules
|
|
204
|
+
follow the format of 'Constant %.3f Transmittance'.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
door: A honeybee Door for which an IDF representation will be returned.
|
|
208
|
+
"""
|
|
209
|
+
# IF ORPHANED: write the door as a shade
|
|
210
|
+
if not door.has_parent:
|
|
211
|
+
# create the Shading:Detailed IDF string
|
|
212
|
+
cns = door.properties.energy.construction
|
|
213
|
+
trans_sch = 'Constant %.3f Transmittance' % cns.solar_transmittance \
|
|
214
|
+
if door.is_glass else ''
|
|
215
|
+
verts = door.upper_left_vertices
|
|
216
|
+
verts_str = ',\n '.join('%.3f, %.3f, %.3f' % (v.x, v.y, v.z) for v in verts)
|
|
217
|
+
values = (door.identifier, trans_sch, len(verts), verts_str)
|
|
218
|
+
comments = ('name', 'transmittance schedule', 'number of vertices', '')
|
|
219
|
+
shade_str = generate_idf_string('Shading:Building:Detailed', values, comments)
|
|
220
|
+
|
|
221
|
+
# create the ShadingProperty:Reflectance
|
|
222
|
+
comments = (
|
|
223
|
+
'shade surface name', 'diffuse solar reflectance', 'diffuse visible reflectance')
|
|
224
|
+
if door.is_glass:
|
|
225
|
+
values = (door.identifier, 0.2, 0.2, 1, cns.identifier)
|
|
226
|
+
comments = comments + ('glazed fraction of surface', 'glazing construction')
|
|
227
|
+
else:
|
|
228
|
+
values = (door.identifier, cns.outside_solar_reflectance,
|
|
229
|
+
cns.outside_visible_reflectance)
|
|
230
|
+
constr_str = generate_idf_string('ShadingProperty:Reflectance', values, comments)
|
|
231
|
+
return '\n\n'.join((shade_str, constr_str))
|
|
232
|
+
|
|
233
|
+
# IF CHILD: write the door as a fenestration surface
|
|
234
|
+
# set defaults for missing fields
|
|
235
|
+
door_bc_obj = door.boundary_condition.boundary_condition_object if \
|
|
236
|
+
isinstance(door.boundary_condition, Surface) else ''
|
|
237
|
+
construction = door.properties.energy.construction
|
|
238
|
+
frame_name = construction.frame.identifier if construction.has_frame else ''
|
|
239
|
+
if construction.has_shade:
|
|
240
|
+
constr_name = construction.window_construction.identifier
|
|
241
|
+
elif construction.is_dynamic:
|
|
242
|
+
constr_name = '{}State0'.format(construction.constructions[0].identifier)
|
|
243
|
+
else:
|
|
244
|
+
constr_name = construction.identifier
|
|
245
|
+
if door.has_parent:
|
|
246
|
+
parent_face = door.parent.identifier
|
|
247
|
+
parent_room = door.parent.parent.identifier if door.parent.has_parent \
|
|
248
|
+
else 'unknown'
|
|
249
|
+
else:
|
|
250
|
+
parent_room = parent_face = 'unknown'
|
|
251
|
+
|
|
252
|
+
# create the fenestration surface string
|
|
253
|
+
ul_verts = door.upper_left_vertices
|
|
254
|
+
values = (
|
|
255
|
+
door.identifier,
|
|
256
|
+
'Door' if not door.is_glass else 'GlassDoor',
|
|
257
|
+
constr_name,
|
|
258
|
+
parent_face,
|
|
259
|
+
door_bc_obj,
|
|
260
|
+
door.boundary_condition.view_factor,
|
|
261
|
+
frame_name,
|
|
262
|
+
'1',
|
|
263
|
+
len(door.vertices),
|
|
264
|
+
',\n '.join('%.3f, %.3f, %.3f' % (v.x, v.y, v.z) for v in ul_verts)
|
|
265
|
+
)
|
|
266
|
+
comments = (
|
|
267
|
+
'name',
|
|
268
|
+
'surface type',
|
|
269
|
+
'construction name',
|
|
270
|
+
'building surface name',
|
|
271
|
+
'boundary condition object',
|
|
272
|
+
'view factor to ground',
|
|
273
|
+
'frame and divider name',
|
|
274
|
+
'multiplier',
|
|
275
|
+
'number of vertices',
|
|
276
|
+
''
|
|
277
|
+
)
|
|
278
|
+
fen_str = generate_idf_string('FenestrationSurface:Detailed', values, comments)
|
|
279
|
+
|
|
280
|
+
# create the WindowShadingControl object if it is needed
|
|
281
|
+
if construction.has_shade:
|
|
282
|
+
shd_prop_str = construction.to_shading_control_idf(door.identifier, parent_room)
|
|
283
|
+
fen_str = '\n\n'.join((fen_str, shd_prop_str))
|
|
284
|
+
|
|
285
|
+
# create the VentilationOpening object if it is needed
|
|
286
|
+
if door.properties.energy.vent_opening is not None:
|
|
287
|
+
try:
|
|
288
|
+
vent_str = door.properties.energy.vent_opening.to_idf()
|
|
289
|
+
fen_str = '\n\n'.join((fen_str, vent_str))
|
|
290
|
+
except AssertionError: # door does not have a parent room
|
|
291
|
+
pass
|
|
292
|
+
return fen_str
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def aperture_to_idf(aperture):
|
|
296
|
+
"""Generate an IDF string representation of an Aperture.
|
|
297
|
+
|
|
298
|
+
Note that the resulting string does not include full construction definitions
|
|
299
|
+
but it will include a WindowShadingControl definition if a WindowConstructionShade
|
|
300
|
+
is assigned to the aperture. It will also include a ventilation object if the
|
|
301
|
+
aperture has a VentilationOpening object assigned to it.
|
|
302
|
+
|
|
303
|
+
Also note that shades assigned to the Aperture are not included in the resulting
|
|
304
|
+
string. To write these objects into a final string, you must loop through the
|
|
305
|
+
Aperture.shades, and call the to.idf method on each one.
|
|
306
|
+
|
|
307
|
+
If the input aperture is orphaned, the resulting string will possess both the
|
|
308
|
+
Shading object as well as a ShadingProperty:Reflectance that aligns with the
|
|
309
|
+
Aperture's exterior construction properties. However, a transmittance schedule
|
|
310
|
+
that matches the transmittance of the window construction will only be
|
|
311
|
+
referenced and not included in the resulting string. All transmittance schedules
|
|
312
|
+
follow the format of 'Constant %.3f Transmittance'.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
aperture: A honeybee Aperture for which an IDF representation will be returned.
|
|
316
|
+
"""
|
|
317
|
+
# IF ORPHANED: write the aperture as a shade
|
|
318
|
+
if not aperture.has_parent:
|
|
319
|
+
# create the Shading:Detailed IDF string
|
|
320
|
+
cns = aperture.properties.energy.construction
|
|
321
|
+
trans_sch = 'Constant %.3f Transmittance' % cns.solar_transmittance
|
|
322
|
+
verts = aperture.upper_left_vertices
|
|
323
|
+
verts_str = ',\n '.join('%.3f, %.3f, %.3f' % (v.x, v.y, v.z) for v in verts)
|
|
324
|
+
values = (aperture.identifier, trans_sch, len(verts), verts_str)
|
|
325
|
+
comments = ('name', 'transmittance schedule', 'number of vertices', '')
|
|
326
|
+
shade_str = generate_idf_string('Shading:Building:Detailed', values, comments)
|
|
327
|
+
|
|
328
|
+
# create the ShadingProperty:Reflectance
|
|
329
|
+
values = (aperture.identifier, 0.2, 0.2, 1, cns.identifier)
|
|
330
|
+
comments = (
|
|
331
|
+
'shade surface name', 'diffuse solar reflectance', 'diffuse visible reflectance',
|
|
332
|
+
'glazed fraction of surface', 'glazing construction'
|
|
333
|
+
)
|
|
334
|
+
constr_str = generate_idf_string('ShadingProperty:Reflectance', values, comments)
|
|
335
|
+
return '\n\n'.join((shade_str, constr_str))
|
|
336
|
+
|
|
337
|
+
# IF CHILD: write the aperture as a fenestration surface
|
|
338
|
+
# set defaults for missing fields
|
|
339
|
+
ap_bc_obj = aperture.boundary_condition.boundary_condition_object if \
|
|
340
|
+
isinstance(aperture.boundary_condition, Surface) else ''
|
|
341
|
+
construction = aperture.properties.energy.construction
|
|
342
|
+
frame_name = construction.frame.identifier if construction.has_frame else ''
|
|
343
|
+
if construction.has_shade:
|
|
344
|
+
constr_name = construction.window_construction.identifier
|
|
345
|
+
elif construction.is_dynamic:
|
|
346
|
+
constr_name = '{}State0'.format(construction.constructions[0].identifier)
|
|
347
|
+
else:
|
|
348
|
+
constr_name = construction.identifier
|
|
349
|
+
if aperture.has_parent:
|
|
350
|
+
parent_face = aperture.parent.identifier
|
|
351
|
+
parent_room = aperture.parent.parent.identifier if aperture.parent.has_parent \
|
|
352
|
+
else 'unknown'
|
|
353
|
+
else:
|
|
354
|
+
parent_room = parent_face = 'unknown'
|
|
355
|
+
|
|
356
|
+
# create the fenestration surface string
|
|
357
|
+
ul_verts = aperture.upper_left_vertices
|
|
358
|
+
values = (
|
|
359
|
+
aperture.identifier,
|
|
360
|
+
'Window',
|
|
361
|
+
constr_name,
|
|
362
|
+
parent_face,
|
|
363
|
+
ap_bc_obj,
|
|
364
|
+
aperture.boundary_condition.view_factor,
|
|
365
|
+
frame_name,
|
|
366
|
+
'1',
|
|
367
|
+
len(aperture.vertices),
|
|
368
|
+
',\n '.join('%.3f, %.3f, %.3f' % (v.x, v.y, v.z) for v in ul_verts)
|
|
369
|
+
)
|
|
370
|
+
comments = (
|
|
371
|
+
'name',
|
|
372
|
+
'surface type',
|
|
373
|
+
'construction name',
|
|
374
|
+
'building surface name',
|
|
375
|
+
'boundary condition object',
|
|
376
|
+
'view factor to ground',
|
|
377
|
+
'frame and divider name',
|
|
378
|
+
'multiplier',
|
|
379
|
+
'number of vertices',
|
|
380
|
+
''
|
|
381
|
+
)
|
|
382
|
+
fen_str = generate_idf_string('FenestrationSurface:Detailed', values, comments)
|
|
383
|
+
|
|
384
|
+
# create the WindowShadingControl object if it is needed
|
|
385
|
+
if construction.has_shade:
|
|
386
|
+
shd_prop_str = construction.to_shading_control_idf(
|
|
387
|
+
aperture.identifier, parent_room)
|
|
388
|
+
fen_str = '\n\n'.join((fen_str, shd_prop_str))
|
|
389
|
+
|
|
390
|
+
# create the VentilationOpening object if it is needed
|
|
391
|
+
if aperture.properties.energy.vent_opening is not None:
|
|
392
|
+
try:
|
|
393
|
+
vent_str = aperture.properties.energy.vent_opening.to_idf()
|
|
394
|
+
fen_str = '\n\n'.join((fen_str, vent_str))
|
|
395
|
+
except AssertionError: # aperture does not have a parent room
|
|
396
|
+
pass
|
|
397
|
+
return fen_str
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def face_to_idf(face):
|
|
401
|
+
"""Generate an IDF string representation of a Face.
|
|
402
|
+
|
|
403
|
+
Note that the resulting string does not include full construction definitions.
|
|
404
|
+
|
|
405
|
+
Also note that this does not include any of the shades assigned to the Face
|
|
406
|
+
in the resulting string. Nor does it include the strings for the
|
|
407
|
+
apertures or doors. To write these objects into a final string, you must
|
|
408
|
+
loop through the Face.shades, Face.apertures, and Face.doors and call the
|
|
409
|
+
to.idf method on each one.
|
|
410
|
+
|
|
411
|
+
If the input face is orphaned, the resulting string will possess both the
|
|
412
|
+
Shading object as well as a ShadingProperty:Reflectance that aligns with
|
|
413
|
+
the Face's exterior construction properties. Furthermore, any child
|
|
414
|
+
apertures of doors in the face will also be included as shading geometries.
|
|
415
|
+
|
|
416
|
+
Args:
|
|
417
|
+
face: A honeybee Face for which an IDF representation will be returned.
|
|
418
|
+
"""
|
|
419
|
+
# IF ORPHANED: write the face as a shade
|
|
420
|
+
if not face.has_parent:
|
|
421
|
+
# create the Shading:Detailed IDF string
|
|
422
|
+
verts = face.punched_geometry.upper_left_counter_clockwise_vertices
|
|
423
|
+
verts_str = ',\n '.join('%.3f, %.3f, %.3f' % (v.x, v.y, v.z) for v in verts)
|
|
424
|
+
values = (face.identifier, '', len(verts), verts_str)
|
|
425
|
+
comments = ('name', 'transmittance schedule', 'number of vertices', '')
|
|
426
|
+
shade_str = generate_idf_string('Shading:Building:Detailed', values, comments)
|
|
427
|
+
|
|
428
|
+
# create the ShadingProperty:Reflectance IDF string
|
|
429
|
+
cns = face.properties.energy.construction
|
|
430
|
+
values = (
|
|
431
|
+
face.identifier, cns.outside_solar_reflectance, cns.outside_visible_reflectance)
|
|
432
|
+
comments = (
|
|
433
|
+
'shade surface name', 'diffuse solar reflectance', 'diffuse visible reflectance')
|
|
434
|
+
constr_str = generate_idf_string('ShadingProperty:Reflectance', values, comments)
|
|
435
|
+
|
|
436
|
+
# translate any child apertures or doors
|
|
437
|
+
face_str = [shade_str, constr_str]
|
|
438
|
+
for ap in face.apertures:
|
|
439
|
+
ap._parent = None # remove parent to translate as orphaned
|
|
440
|
+
face_str.append(aperture_to_idf(ap))
|
|
441
|
+
ap._parent = face # put back the parent
|
|
442
|
+
for dr in face.doors:
|
|
443
|
+
dr._parent = None # remove parent to translate as orphaned
|
|
444
|
+
face_str.append(door_to_idf(dr))
|
|
445
|
+
dr._parent = face # put back the parent
|
|
446
|
+
return '\n\n'.join(face_str)
|
|
447
|
+
|
|
448
|
+
# IF CHILD: write the aperture as a fenestration surface
|
|
449
|
+
# select the correct face type
|
|
450
|
+
if isinstance(face.type, AirBoundary):
|
|
451
|
+
face_type = 'Wall' # air boundaries are not a Surface type in EnergyPlus
|
|
452
|
+
elif isinstance(face.type, RoofCeiling):
|
|
453
|
+
if face.altitude < 0:
|
|
454
|
+
face_type = 'Wall' # ensure E+ does not try to flip the Face
|
|
455
|
+
elif isinstance(face.boundary_condition, (Outdoors, Ground)):
|
|
456
|
+
face_type = 'Roof' # E+ distinguishes between Roof and Ceiling
|
|
457
|
+
else:
|
|
458
|
+
face_type = 'Ceiling'
|
|
459
|
+
elif isinstance(face.type, Floor) and face.altitude > 0:
|
|
460
|
+
face_type = 'Wall' # ensure E+ does not try to flip the Face
|
|
461
|
+
else:
|
|
462
|
+
face_type = face.type.name
|
|
463
|
+
# select the correct boundary condition
|
|
464
|
+
bc_name, append_txt = face.boundary_condition.name, None
|
|
465
|
+
if isinstance(face.boundary_condition, Surface):
|
|
466
|
+
face_bc_obj = face.boundary_condition.boundary_condition_object
|
|
467
|
+
elif face.boundary_condition.name == 'OtherSideTemperature':
|
|
468
|
+
face_bc_obj = '{}_OtherTemp'.format(face.identifier)
|
|
469
|
+
append_txt = face.boundary_condition.to_idf(face_bc_obj)
|
|
470
|
+
bc_name = 'OtherSideCoefficients'
|
|
471
|
+
else:
|
|
472
|
+
face_bc_obj = ''
|
|
473
|
+
# process the geometry correctly if it has holes
|
|
474
|
+
ul_verts = face.upper_left_vertices
|
|
475
|
+
if face.geometry.has_holes and isinstance(face.boundary_condition, Surface):
|
|
476
|
+
# check if the first vertex is the upper-left vertex
|
|
477
|
+
pt1, found_i = ul_verts[0], False
|
|
478
|
+
for pt in ul_verts[1:]:
|
|
479
|
+
if pt == pt1:
|
|
480
|
+
found_i = True
|
|
481
|
+
break
|
|
482
|
+
if found_i: # reorder the vertices to have boundary first
|
|
483
|
+
ul_verts = reversed(ul_verts)
|
|
484
|
+
# assemble the values and the comments
|
|
485
|
+
if face.has_parent:
|
|
486
|
+
if face.parent.identifier == face.parent.zone:
|
|
487
|
+
zone_name, space_name = face.parent.zone, ''
|
|
488
|
+
else:
|
|
489
|
+
zone_name, space_name = face.parent.zone, face.parent.identifier
|
|
490
|
+
else:
|
|
491
|
+
zone_name, space_name = 'unknown', ''
|
|
492
|
+
values = (
|
|
493
|
+
face.identifier,
|
|
494
|
+
face_type,
|
|
495
|
+
face.properties.energy.construction.identifier,
|
|
496
|
+
zone_name,
|
|
497
|
+
space_name,
|
|
498
|
+
bc_name,
|
|
499
|
+
face_bc_obj,
|
|
500
|
+
face.boundary_condition.sun_exposure_idf,
|
|
501
|
+
face.boundary_condition.wind_exposure_idf,
|
|
502
|
+
face.boundary_condition.view_factor,
|
|
503
|
+
len(face.vertices),
|
|
504
|
+
',\n '.join('%.3f, %.3f, %.3f' % (v.x, v.y, v.z) for v in ul_verts)
|
|
505
|
+
)
|
|
506
|
+
comments = (
|
|
507
|
+
'name',
|
|
508
|
+
'surface type',
|
|
509
|
+
'construction name',
|
|
510
|
+
'zone name',
|
|
511
|
+
'space name',
|
|
512
|
+
'boundary condition',
|
|
513
|
+
'boundary condition object',
|
|
514
|
+
'sun exposure',
|
|
515
|
+
'wind exposure',
|
|
516
|
+
'view factor to ground',
|
|
517
|
+
'number of vertices',
|
|
518
|
+
''
|
|
519
|
+
)
|
|
520
|
+
face_idf = generate_idf_string('BuildingSurface:Detailed', values, comments)
|
|
521
|
+
return face_idf if not append_txt else face_idf + append_txt
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
def room_to_idf(room):
|
|
525
|
+
"""Generate an IDF string representation of a Room.
|
|
526
|
+
|
|
527
|
+
The resulting string will include all internal gain definitions for the Room
|
|
528
|
+
(people, lights, equipment, process) and the infiltration definition. It will
|
|
529
|
+
also include internal masses, ventilation fans, and daylight controls. However,
|
|
530
|
+
complete schedule definitions assigned to these load objects are excluded.
|
|
531
|
+
|
|
532
|
+
If the room's zone name is the same as the room identifier, the resulting IDF
|
|
533
|
+
string will be for an EnergyPlus Zone and it will include ventilation
|
|
534
|
+
requirements and thermostat objects. Otherwise, the IDF string will be for
|
|
535
|
+
a Space with ventilation and thermostats excluded (with the assumption
|
|
536
|
+
that these objects are to be written separately with the parent Zone).
|
|
537
|
+
|
|
538
|
+
The Room's HVAC is always excluded in the string returned from this method
|
|
539
|
+
regardless of whether the room represents an entire zone or an individual
|
|
540
|
+
space within a larger zone.
|
|
541
|
+
|
|
542
|
+
Also note that this method does not write any of the geometry of the Room
|
|
543
|
+
into the resulting string. To represent the Room geometry, you must loop
|
|
544
|
+
through the Room.shades and Room.faces and call the to.idf method on
|
|
545
|
+
each one. Note that you will likely also need to call to.idf on the
|
|
546
|
+
apertures, doors and shades of each face as well as the shades on each
|
|
547
|
+
aperture.
|
|
548
|
+
|
|
549
|
+
Args:
|
|
550
|
+
room: A honeybee Room for which an IDF representation will be returned.
|
|
551
|
+
"""
|
|
552
|
+
# clean the room name so that it can be written into a comment
|
|
553
|
+
clean_name = room.display_name.replace('\n', '')
|
|
554
|
+
|
|
555
|
+
if room.identifier == room.zone: # write the zone definition
|
|
556
|
+
is_zone = True
|
|
557
|
+
room_str = ['!- ________ZONE:{}________\n'.format(clean_name)]
|
|
558
|
+
ceil_height = room.geometry.max.z - room.geometry.min.z
|
|
559
|
+
include_floor = 'No' if room.exclude_floor_area else 'Yes'
|
|
560
|
+
zone_values = (room.identifier, '', '', '', '', '', room.multiplier,
|
|
561
|
+
ceil_height, room.volume, room.floor_area, '', '', include_floor)
|
|
562
|
+
zone_comments = ('name', 'north', 'x', 'y', 'z', 'type', 'multiplier',
|
|
563
|
+
'ceiling height', 'volume', 'floor area', 'inside convection',
|
|
564
|
+
'outside convection', 'include floor area')
|
|
565
|
+
room_str.append(generate_idf_string('Zone', zone_values, zone_comments))
|
|
566
|
+
else: # write the space definition
|
|
567
|
+
is_zone = False
|
|
568
|
+
room_str = ['!- ________SPACE:{}________\n'.format(clean_name)]
|
|
569
|
+
ceil_height = room.geometry.max.z - room.geometry.min.z
|
|
570
|
+
space_values = (room.identifier, room.zone,
|
|
571
|
+
ceil_height, room.volume, room.floor_area)
|
|
572
|
+
space_comments = ('name', 'zone name', 'ceiling height', 'volume', 'floor area')
|
|
573
|
+
room_str.append(generate_idf_string('Space', space_values, space_comments))
|
|
574
|
+
|
|
575
|
+
# write the load definitions
|
|
576
|
+
people = room.properties.energy.people
|
|
577
|
+
lighting = room.properties.energy.lighting
|
|
578
|
+
electric_equipment = room.properties.energy.electric_equipment
|
|
579
|
+
gas_equipment = room.properties.energy.gas_equipment
|
|
580
|
+
shw = room.properties.energy.service_hot_water
|
|
581
|
+
infiltration = room.properties.energy.infiltration
|
|
582
|
+
ventilation = room.properties.energy.ventilation
|
|
583
|
+
|
|
584
|
+
if people is not None:
|
|
585
|
+
room_str.append(people.to_idf(room.identifier))
|
|
586
|
+
if lighting is not None:
|
|
587
|
+
room_str.append(lighting.to_idf(room.identifier))
|
|
588
|
+
if electric_equipment is not None:
|
|
589
|
+
room_str.append(electric_equipment.to_idf(room.identifier))
|
|
590
|
+
if gas_equipment is not None:
|
|
591
|
+
room_str.append(gas_equipment.to_idf(room.identifier))
|
|
592
|
+
if shw is not None:
|
|
593
|
+
shw_str, shw_sch = shw.to_idf(room)
|
|
594
|
+
room_str.append(shw_str)
|
|
595
|
+
room_str.extend(shw_sch)
|
|
596
|
+
if infiltration is not None:
|
|
597
|
+
room_str.append(infiltration.to_idf(room.identifier))
|
|
598
|
+
|
|
599
|
+
# write the ventilation and thermostat
|
|
600
|
+
if is_zone:
|
|
601
|
+
if ventilation is not None:
|
|
602
|
+
room_str.append(ventilation.to_idf(room.identifier))
|
|
603
|
+
if room.properties.energy.is_conditioned and \
|
|
604
|
+
room.properties.energy.setpoint is not None:
|
|
605
|
+
room_str.append(room.properties.energy.setpoint.to_idf(room.identifier))
|
|
606
|
+
|
|
607
|
+
# write any ventilation fan definitions
|
|
608
|
+
for fan in room.properties.energy._fans:
|
|
609
|
+
room_str.append(fan.to_idf(room.identifier))
|
|
610
|
+
|
|
611
|
+
# write the daylighting control
|
|
612
|
+
if room.properties.energy.daylighting_control is not None:
|
|
613
|
+
room_str.extend(room.properties.energy.daylighting_control.to_idf())
|
|
614
|
+
|
|
615
|
+
# write any process load definitions
|
|
616
|
+
for p_load in room.properties.energy._process_loads:
|
|
617
|
+
room_str.append(p_load.to_idf(room.identifier))
|
|
618
|
+
|
|
619
|
+
# write any internal mass definitions
|
|
620
|
+
for int_mass in room.properties.energy._internal_masses:
|
|
621
|
+
room_str.append(int_mass.to_idf(room.identifier, is_zone))
|
|
622
|
+
|
|
623
|
+
return '\n\n'.join(room_str)
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
def model_to_idf(
|
|
627
|
+
model, schedule_directory=None, use_ideal_air_equivalent=True,
|
|
628
|
+
patch_missing_adjacencies=False
|
|
629
|
+
):
|
|
630
|
+
r"""Generate an IDF string representation of a Model.
|
|
631
|
+
|
|
632
|
+
The resulting string will include all geometry (Rooms, Faces, Shades, Apertures,
|
|
633
|
+
Doors), all fully-detailed constructions + materials, all fully-detailed
|
|
634
|
+
schedules, and the room properties (loads, thermostats with setpoints, and HVAC).
|
|
635
|
+
|
|
636
|
+
Essentially, the string includes everything needed to simulate the model
|
|
637
|
+
except the simulation parameters. So joining this string with the output of
|
|
638
|
+
SimulationParameter.to_idf() should create a simulate-able IDF.
|
|
639
|
+
|
|
640
|
+
Args:
|
|
641
|
+
model: A honeybee Model for which an IDF representation will be returned.
|
|
642
|
+
schedule_directory: An optional file directory to which all file-based
|
|
643
|
+
schedules should be written to. If None, all ScheduleFixedIntervals
|
|
644
|
+
will be translated to Schedule:Compact and written fully into the
|
|
645
|
+
IDF string instead of to Schedule:File. (Default: None).
|
|
646
|
+
use_ideal_air_equivalent: Boolean to note whether any detailed HVAC system
|
|
647
|
+
templates should be converted to an equivalent IdealAirSystem upon export.
|
|
648
|
+
If False and the Model contains detailed systems, a ValueError will
|
|
649
|
+
be raised since this method does not support the translation of
|
|
650
|
+
detailed systems. (Default:True).
|
|
651
|
+
patch_missing_adjacencies: Boolean to note whether any missing adjacencies
|
|
652
|
+
in the model should be replaced with Adiabatic boundary conditions.
|
|
653
|
+
This is useful when the input model is only a portion of a much
|
|
654
|
+
larger model. (Default: False).
|
|
655
|
+
|
|
656
|
+
Usage:
|
|
657
|
+
|
|
658
|
+
.. code-block:: python
|
|
659
|
+
|
|
660
|
+
import os
|
|
661
|
+
from ladybug.futil import write_to_file
|
|
662
|
+
from honeybee.model import Model
|
|
663
|
+
from honeybee.room import Room
|
|
664
|
+
from honeybee.config import folders
|
|
665
|
+
from honeybee_energy.lib.programtypes import office_program
|
|
666
|
+
from honeybee_energy.hvac.idealair import IdealAirSystem
|
|
667
|
+
from honeybee_energy.simulation.parameter import SimulationParameter
|
|
668
|
+
|
|
669
|
+
# Get input Model
|
|
670
|
+
room = Room.from_box('Tiny House Zone', 5, 10, 3)
|
|
671
|
+
room.properties.energy.program_type = office_program
|
|
672
|
+
room.properties.energy.add_default_ideal_air()
|
|
673
|
+
model = Model('Tiny House', [room])
|
|
674
|
+
|
|
675
|
+
# Get the input SimulationParameter
|
|
676
|
+
sim_par = SimulationParameter()
|
|
677
|
+
sim_par.output.add_zone_energy_use()
|
|
678
|
+
ddy_file = 'C:/EnergyPlusV9-0-1/WeatherData/USA_CO_Golden-NREL.724666_TMY3.ddy'
|
|
679
|
+
sim_par.sizing_parameter.add_from_ddy_996_004(ddy_file)
|
|
680
|
+
|
|
681
|
+
# create the IDF string for simulation parameters and model
|
|
682
|
+
idf_str = '\n\n'.join((sim_par.to_idf(), model.to.idf(model)))
|
|
683
|
+
|
|
684
|
+
# write the final string into an IDF
|
|
685
|
+
idf = os.path.join(folders.default_simulation_folder, 'test_file', 'in.idf')
|
|
686
|
+
write_to_file(idf, idf_str, True)
|
|
687
|
+
"""
|
|
688
|
+
# duplicate model to avoid mutating it as we edit it for energy simulation
|
|
689
|
+
original_model = model
|
|
690
|
+
model = model.duplicate()
|
|
691
|
+
# scale the model if the units are not meters
|
|
692
|
+
if model.units != 'Meters':
|
|
693
|
+
model.convert_to_units('Meters')
|
|
694
|
+
# remove degenerate geometry within native E+ tolerance of 0.01 meters
|
|
695
|
+
try:
|
|
696
|
+
model.remove_degenerate_geometry(0.01)
|
|
697
|
+
except ValueError:
|
|
698
|
+
error = 'Failed to remove degenerate Rooms.\nYour Model units system is: {}. ' \
|
|
699
|
+
'Is this correct?'.format(original_model.units)
|
|
700
|
+
raise ValueError(error)
|
|
701
|
+
|
|
702
|
+
# convert model to simple ventilation and Ideal Air Systems
|
|
703
|
+
model.properties.energy.ventilation_simulation_control.vent_control_type = \
|
|
704
|
+
'SingleZone'
|
|
705
|
+
if use_ideal_air_equivalent:
|
|
706
|
+
for room in model.rooms:
|
|
707
|
+
room.properties.energy.assign_ideal_air_equivalent()
|
|
708
|
+
|
|
709
|
+
# patch missing adjacencies
|
|
710
|
+
if patch_missing_adjacencies:
|
|
711
|
+
model.properties.energy.missing_adjacencies_to_adiabatic()
|
|
712
|
+
|
|
713
|
+
# resolve the properties across zones
|
|
714
|
+
single_zones, zone_dict = model.properties.energy.resolve_zones()
|
|
715
|
+
|
|
716
|
+
# write the building object into the string
|
|
717
|
+
model_str = ['!- =======================================\n'
|
|
718
|
+
'!- ================ MODEL ================\n'
|
|
719
|
+
'!- =======================================\n']
|
|
720
|
+
|
|
721
|
+
# write all of the schedules and type limits
|
|
722
|
+
sched_strs = []
|
|
723
|
+
type_limits = []
|
|
724
|
+
used_day_sched_ids, used_day_count = {}, 1
|
|
725
|
+
always_on_included = False
|
|
726
|
+
all_scheds = model.properties.energy.schedules + \
|
|
727
|
+
model.properties.energy.orphaned_trans_schedules
|
|
728
|
+
for sched in all_scheds:
|
|
729
|
+
if sched.identifier == 'Always On':
|
|
730
|
+
always_on_included = True
|
|
731
|
+
try: # ScheduleRuleset
|
|
732
|
+
year_schedule, week_schedules = sched.to_idf()
|
|
733
|
+
if week_schedules is None: # ScheduleConstant
|
|
734
|
+
sched_strs.append(year_schedule)
|
|
735
|
+
else: # ScheduleYear
|
|
736
|
+
# check that day schedules aren't referenced by other model schedules
|
|
737
|
+
day_scheds = []
|
|
738
|
+
for day in sched.day_schedules:
|
|
739
|
+
if day.identifier not in used_day_sched_ids:
|
|
740
|
+
day_scheds.append(day.to_idf(sched.schedule_type_limit))
|
|
741
|
+
used_day_sched_ids[day.identifier] = day
|
|
742
|
+
elif day != used_day_sched_ids[day.identifier]:
|
|
743
|
+
new_day = day.duplicate()
|
|
744
|
+
new_day.identifier = 'Schedule Day {}'.format(used_day_count)
|
|
745
|
+
day_scheds.append(new_day.to_idf(sched.schedule_type_limit))
|
|
746
|
+
for i, week_sch in enumerate(week_schedules):
|
|
747
|
+
week_schedules[i] = \
|
|
748
|
+
week_sch.replace(day.identifier, new_day.identifier)
|
|
749
|
+
used_day_count += 1
|
|
750
|
+
sched_strs.extend([year_schedule] + week_schedules + day_scheds)
|
|
751
|
+
except TypeError: # ScheduleFixedInterval
|
|
752
|
+
if schedule_directory is None:
|
|
753
|
+
sched_strs.append(sched.to_idf_compact())
|
|
754
|
+
else:
|
|
755
|
+
sched_strs.append(sched.to_idf(schedule_directory))
|
|
756
|
+
t_lim = sched.schedule_type_limit
|
|
757
|
+
if t_lim is not None and not _instance_in_array(t_lim, type_limits):
|
|
758
|
+
type_limits.append(t_lim)
|
|
759
|
+
if not always_on_included:
|
|
760
|
+
always_schedule, _ = model.properties.energy._always_on_schedule().to_idf()
|
|
761
|
+
sched_strs.append(always_schedule)
|
|
762
|
+
model_str.append('!- ========= SCHEDULE TYPE LIMITS =========\n')
|
|
763
|
+
model_str.extend([type_limit.to_idf() for type_limit in set(type_limits)])
|
|
764
|
+
model_str.append('!- ============== SCHEDULES ==============\n')
|
|
765
|
+
model_str.extend(sched_strs)
|
|
766
|
+
|
|
767
|
+
# get the default generic construction set
|
|
768
|
+
# must be imported here to avoid circular imports
|
|
769
|
+
from .lib.constructionsets import generic_construction_set
|
|
770
|
+
|
|
771
|
+
# write all of the materials and constructions
|
|
772
|
+
materials = []
|
|
773
|
+
construction_strs = []
|
|
774
|
+
dynamic_cons = []
|
|
775
|
+
all_constrs = model.properties.energy.constructions + \
|
|
776
|
+
generic_construction_set.constructions_unique
|
|
777
|
+
for constr in set(all_constrs):
|
|
778
|
+
try:
|
|
779
|
+
materials.extend(constr.materials)
|
|
780
|
+
construction_strs.append(constr.to_idf())
|
|
781
|
+
if constr.has_frame:
|
|
782
|
+
materials.append(constr.frame)
|
|
783
|
+
if constr.has_shade:
|
|
784
|
+
if constr.window_construction in all_constrs:
|
|
785
|
+
construction_strs.pop(-1) # avoid duplicate specification
|
|
786
|
+
if constr.is_switchable_glazing:
|
|
787
|
+
materials.append(constr.switched_glass_material)
|
|
788
|
+
construction_strs.append(constr.to_shaded_idf())
|
|
789
|
+
elif constr.is_dynamic:
|
|
790
|
+
dynamic_cons.append(constr)
|
|
791
|
+
except AttributeError:
|
|
792
|
+
try: # AirBoundaryConstruction or ShadeConstruction
|
|
793
|
+
construction_strs.append(constr.to_idf()) # AirBoundaryConstruction
|
|
794
|
+
except TypeError:
|
|
795
|
+
pass # ShadeConstruction; no need to write it
|
|
796
|
+
model_str.append('!- ============== MATERIALS ==============\n')
|
|
797
|
+
model_str.extend([mat.to_idf() for mat in set(materials)])
|
|
798
|
+
model_str.append('!- ============ CONSTRUCTIONS ============\n')
|
|
799
|
+
model_str.extend(construction_strs)
|
|
800
|
+
|
|
801
|
+
# write all of the HVAC systems for zones
|
|
802
|
+
model_str.append('!- ============ HVAC SYSTEMS ============\n')
|
|
803
|
+
for zone_id, zone_data in zone_dict.items():
|
|
804
|
+
rooms, z_prop, set_pt, vent = zone_data
|
|
805
|
+
mult, ceil_hgt, vol, flr_area, inc_flr = z_prop
|
|
806
|
+
model_str.append('!- ________ZONE:{}________\n'.format(zone_id))
|
|
807
|
+
zone_values = (zone_id, '', '', '', '', '', mult,
|
|
808
|
+
ceil_hgt, vol, flr_area, '', '', inc_flr)
|
|
809
|
+
zone_comments = ('name', 'north', 'x', 'y', 'z', 'type', 'multiplier',
|
|
810
|
+
'ceiling height', 'volume', 'floor area', 'inside convection',
|
|
811
|
+
'outside convection', 'include floor area')
|
|
812
|
+
model_str.append(generate_idf_string('Zone', zone_values, zone_comments))
|
|
813
|
+
if vent is not None:
|
|
814
|
+
model_str.append(vent.to_idf(zone_id))
|
|
815
|
+
hvacs = [r.properties.energy.hvac for r in rooms
|
|
816
|
+
if r.properties.energy.hvac is not None]
|
|
817
|
+
if set_pt is not None and len(hvacs) != 0:
|
|
818
|
+
model_str.append(set_pt.to_idf(zone_id))
|
|
819
|
+
try:
|
|
820
|
+
model_str.append(hvacs[0].to_idf_zone(zone_id, set_pt, vent))
|
|
821
|
+
except AttributeError:
|
|
822
|
+
raise TypeError(
|
|
823
|
+
'HVAC system type "{}" does not support direct translation to IDF.\n'
|
|
824
|
+
'Use the export to OpenStudio workflow instead.'.format(
|
|
825
|
+
room.properties.energy.hvac.__class__.__name__))
|
|
826
|
+
# write all of the HVAC systems for individual rooms not using zones
|
|
827
|
+
for room in single_zones:
|
|
828
|
+
if room.properties.energy.hvac is not None \
|
|
829
|
+
and room.properties.energy.setpoint is not None:
|
|
830
|
+
try:
|
|
831
|
+
model_str.append(room.properties.energy.hvac.to_idf(room))
|
|
832
|
+
except AttributeError:
|
|
833
|
+
raise TypeError(
|
|
834
|
+
'HVAC system type "{}" does not support direct translation to IDF.\n'
|
|
835
|
+
'Use the export to OpenStudio workflow instead.'.format(
|
|
836
|
+
room.properties.energy.hvac.__class__.__name__))
|
|
837
|
+
|
|
838
|
+
# get the default air boundary construction
|
|
839
|
+
# must be imported here to avoid circular imports
|
|
840
|
+
from .lib.constructions import air_boundary
|
|
841
|
+
|
|
842
|
+
# write all of the room geometry
|
|
843
|
+
model_str.append('!- ============ ROOM GEOMETRY ============\n')
|
|
844
|
+
sf_objs = []
|
|
845
|
+
found_ab = []
|
|
846
|
+
for room in model.rooms:
|
|
847
|
+
model_str.append(room.to.idf(room))
|
|
848
|
+
for face in room.faces:
|
|
849
|
+
model_str.append(face.to.idf(face))
|
|
850
|
+
if isinstance(face.type, AirBoundary): # write the air mixing objects
|
|
851
|
+
air_constr = face.properties.energy.construction
|
|
852
|
+
try:
|
|
853
|
+
if face.identifier not in found_ab:
|
|
854
|
+
adj_face = face.boundary_condition.boundary_condition_object
|
|
855
|
+
adj_room = face.boundary_condition.boundary_condition_objects[-1]
|
|
856
|
+
try:
|
|
857
|
+
model_str.append(
|
|
858
|
+
air_constr.to_cross_mixing_idf(face, adj_room))
|
|
859
|
+
except AttributeError: # opaque construction for air boundary
|
|
860
|
+
model_str.append(
|
|
861
|
+
air_boundary.to_cross_mixing_idf(face, adj_room))
|
|
862
|
+
found_ab.append(adj_face)
|
|
863
|
+
except AttributeError as e:
|
|
864
|
+
raise ValueError(
|
|
865
|
+
'Face "{}" is an Air Boundary but lacks a Surface boundary '
|
|
866
|
+
'condition.\n{}'.format(face.full_id, e))
|
|
867
|
+
for ap in face.apertures:
|
|
868
|
+
if len(ap.geometry) <= 4: # ignore apertures to be triangulated
|
|
869
|
+
model_str.append(ap.to.idf(ap))
|
|
870
|
+
sf_objs.append(ap)
|
|
871
|
+
for shade in ap.outdoor_shades:
|
|
872
|
+
model_str.append(shade.to.idf(shade))
|
|
873
|
+
for dr in face.doors:
|
|
874
|
+
if len(dr.geometry) <= 4: # ignore doors to be triangulated
|
|
875
|
+
model_str.append(dr.to.idf(dr))
|
|
876
|
+
sf_objs.append(dr)
|
|
877
|
+
for shade in dr.outdoor_shades:
|
|
878
|
+
model_str.append(shade.to.idf(shade))
|
|
879
|
+
for shade in face.outdoor_shades:
|
|
880
|
+
model_str.append(shade.to.idf(shade))
|
|
881
|
+
for shade in room.outdoor_shades:
|
|
882
|
+
model_str.append(shade.to.idf(shade))
|
|
883
|
+
|
|
884
|
+
# triangulate any apertures or doors with more than 4 vertices
|
|
885
|
+
tri_apertures, _ = model.triangulated_apertures()
|
|
886
|
+
for tri_aps in tri_apertures:
|
|
887
|
+
for i, ap in enumerate(tri_aps):
|
|
888
|
+
if i != 0:
|
|
889
|
+
ap.properties.energy.vent_opening = None
|
|
890
|
+
model_str.append(ap.to.idf(ap))
|
|
891
|
+
sf_objs.append(ap)
|
|
892
|
+
tri_doors, _ = model.triangulated_doors()
|
|
893
|
+
for tri_drs in tri_doors:
|
|
894
|
+
for i, dr in enumerate(tri_drs):
|
|
895
|
+
if i != 0:
|
|
896
|
+
dr.properties.energy.vent_opening = None
|
|
897
|
+
model_str.append(dr.to.idf(dr))
|
|
898
|
+
sf_objs.append(dr)
|
|
899
|
+
|
|
900
|
+
# write all context shade geometry
|
|
901
|
+
model_str.append('!- ========== CONTEXT GEOMETRY ==========\n')
|
|
902
|
+
pv_objects = []
|
|
903
|
+
for shade in model.orphaned_shades:
|
|
904
|
+
model_str.append(shade.to.idf(shade))
|
|
905
|
+
if shade.properties.energy.pv_properties is not None:
|
|
906
|
+
pv_objects.append(shade)
|
|
907
|
+
for shade_mesh in model.shade_meshes:
|
|
908
|
+
model_str.append(shade_mesh.to.idf(shade_mesh))
|
|
909
|
+
for face in model.orphaned_faces:
|
|
910
|
+
model_str.append(face_to_idf(face))
|
|
911
|
+
for ap in model.orphaned_apertures:
|
|
912
|
+
model_str.append(aperture_to_idf(ap))
|
|
913
|
+
for dr in model.orphaned_doors:
|
|
914
|
+
model_str.append(door_to_idf(dr))
|
|
915
|
+
|
|
916
|
+
# write any EMS programs for dynamic constructions
|
|
917
|
+
if len(dynamic_cons) != 0:
|
|
918
|
+
model_str.append('!- ========== EMS PROGRAMS ==========\n')
|
|
919
|
+
dyn_dict = {}
|
|
920
|
+
for sf in sf_objs:
|
|
921
|
+
con = sf.properties.energy.construction
|
|
922
|
+
try:
|
|
923
|
+
dyn_dict[con.identifier].append(sf.identifier)
|
|
924
|
+
except KeyError:
|
|
925
|
+
dyn_dict[con.identifier] = [sf.identifier]
|
|
926
|
+
for con in dynamic_cons:
|
|
927
|
+
model_str.append(con.to_program_idf(dyn_dict[con.identifier]))
|
|
928
|
+
model_str.append(dynamic_cons[0].idf_program_manager(dynamic_cons))
|
|
929
|
+
|
|
930
|
+
# write any generator objects that were discovered in the model
|
|
931
|
+
if len(pv_objects) != 0:
|
|
932
|
+
model_str.append('!- ========== PHOTOVOLTAIC GENERATORS ==========\n')
|
|
933
|
+
for shade in pv_objects:
|
|
934
|
+
model_str.append(shade.properties.energy.pv_properties.to_idf(shade))
|
|
935
|
+
model_str.extend(model.properties.energy.electric_load_center.to_idf(pv_objects))
|
|
936
|
+
|
|
937
|
+
return '\n\n'.join(model_str)
|
|
938
|
+
|
|
939
|
+
|
|
940
|
+
def energyplus_idf_version(version_array=None):
|
|
941
|
+
"""Get IDF text for the version of EnergyPlus.
|
|
942
|
+
|
|
943
|
+
This will match the version of EnergyPlus found in the config if it it exists.
|
|
944
|
+
It will be None otherwise.
|
|
945
|
+
|
|
946
|
+
Args:
|
|
947
|
+
version_array: An array of up to 3 integers for the version of EnergyPlus
|
|
948
|
+
for which an IDF string should be generated. If None, the energyplus_version
|
|
949
|
+
from the config will be used if it exists.
|
|
950
|
+
"""
|
|
951
|
+
if version_array:
|
|
952
|
+
ver_str = '.'.join((str(d) for d in version_array))
|
|
953
|
+
return generate_idf_string('Version', [ver_str], ['version identifier'])
|
|
954
|
+
elif folders.energyplus_version:
|
|
955
|
+
ver_str = '.'.join((str(d) for d in folders.energyplus_version))
|
|
956
|
+
return generate_idf_string('Version', [ver_str], ['version identifier'])
|
|
957
|
+
return None
|
|
958
|
+
|
|
959
|
+
|
|
960
|
+
def _instance_in_array(object_instance, object_array):
|
|
961
|
+
"""Check if a specific object instance is already in an array.
|
|
962
|
+
|
|
963
|
+
This can be much faster than `if object_instance in object_array`
|
|
964
|
+
when you expect to be testing a lot of the same instance of an object for
|
|
965
|
+
inclusion in an array since the builtin method uses an == operator to
|
|
966
|
+
test inclusion.
|
|
967
|
+
"""
|
|
968
|
+
for val in object_array:
|
|
969
|
+
if val is object_instance:
|
|
970
|
+
return True
|
|
971
|
+
return False
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
def _preprocess_model_for_trace(
|
|
975
|
+
model, single_window=True, rect_sub_distance='0.15m',
|
|
976
|
+
frame_merge_distance='0.2m'):
|
|
977
|
+
"""Pre-process a Honeybee Model to be written to TRANE TRACE as a gbXML.
|
|
978
|
+
|
|
979
|
+
Args:
|
|
980
|
+
model: A Honeybee Model to be converted to a TRACE-compatible gbXML.
|
|
981
|
+
single_window: A boolean for whether all windows within walls should be
|
|
982
|
+
converted to a single window with an area that matches the original
|
|
983
|
+
geometry. (Default: True).
|
|
984
|
+
rect_sub_distance: Text string of a number for the resolution at which
|
|
985
|
+
non-rectangular Apertures will be subdivided into smaller rectangular
|
|
986
|
+
units. This is required as TRACE 3D plus cannot model non-rectangular
|
|
987
|
+
geometries. This can include the units of the distance (eg. 0.5ft) or,
|
|
988
|
+
if no units are provided, the value will be interpreted in the
|
|
989
|
+
honeybee model units. (Default: 0.15m).
|
|
990
|
+
frame_merge_distance: Text string of a number for the maximum distance
|
|
991
|
+
between non-rectangular Apertures at which point the Apertures will
|
|
992
|
+
be merged into a single rectangular geometry. This is often helpful
|
|
993
|
+
when there are several triangular Apertures that together make a
|
|
994
|
+
rectangle when they are merged across their frames. This can include
|
|
995
|
+
the units of the distance (eg. 0.5ft) or, if no units are provided,
|
|
996
|
+
the value will be interpreted in the honeybee model units. (Default: 0.2m).
|
|
997
|
+
|
|
998
|
+
Returns:
|
|
999
|
+
The input Model modified such that it can import to TRACE as a gbXML
|
|
1000
|
+
without issues.
|
|
1001
|
+
"""
|
|
1002
|
+
# make sure there are rooms and remove all shades and orphaned objects
|
|
1003
|
+
assert len(model.rooms) != 0, \
|
|
1004
|
+
'Model contains no Rooms and therefore cannot be simulated in TRACE.'
|
|
1005
|
+
model.remove_all_shades()
|
|
1006
|
+
model.remove_faces()
|
|
1007
|
+
model.remove_apertures()
|
|
1008
|
+
model.remove_doors()
|
|
1009
|
+
|
|
1010
|
+
# remove degenerate geometry within native E+ tolerance of 0.01 meters
|
|
1011
|
+
original_units = model.units
|
|
1012
|
+
model.convert_to_units('Meters')
|
|
1013
|
+
try:
|
|
1014
|
+
model.remove_degenerate_geometry(0.01)
|
|
1015
|
+
except ValueError:
|
|
1016
|
+
error = 'Failed to remove degenerate Rooms.\nYour Model units system is: {}. ' \
|
|
1017
|
+
'Is this correct?'.format(original_units)
|
|
1018
|
+
raise ValueError(error)
|
|
1019
|
+
rect_sub_distance = parse_distance_string(rect_sub_distance, original_units)
|
|
1020
|
+
frame_merge_distance = parse_distance_string(frame_merge_distance, original_units)
|
|
1021
|
+
if original_units != 'Meters':
|
|
1022
|
+
c_factor = conversion_factor_to_meters(original_units)
|
|
1023
|
+
rect_sub_distance = rect_sub_distance * c_factor
|
|
1024
|
+
frame_merge_distance = frame_merge_distance * c_factor
|
|
1025
|
+
|
|
1026
|
+
# remove all interior windows in the model
|
|
1027
|
+
for room in model.rooms:
|
|
1028
|
+
for face in room.faces:
|
|
1029
|
+
if isinstance(face.boundary_condition, Surface):
|
|
1030
|
+
face.remove_sub_faces()
|
|
1031
|
+
|
|
1032
|
+
# convert all rooms to extrusions and patch the resulting missing adjacencies
|
|
1033
|
+
model.rooms_to_extrusions()
|
|
1034
|
+
model.properties.energy.missing_adjacencies_to_adiabatic()
|
|
1035
|
+
|
|
1036
|
+
# convert windows in walls to a single geometry
|
|
1037
|
+
if single_window:
|
|
1038
|
+
for room in model.rooms:
|
|
1039
|
+
for face in room.faces:
|
|
1040
|
+
if isinstance(face.type, Wall) and face.has_sub_faces:
|
|
1041
|
+
face.boundary_condition = boundary_conditions.outdoors
|
|
1042
|
+
face.apertures_by_ratio(face.aperture_ratio, 0.01, rect_split=False)
|
|
1043
|
+
|
|
1044
|
+
# convert all of the Aperture geometries to rectangles so they can be translated
|
|
1045
|
+
model.rectangularize_apertures(
|
|
1046
|
+
subdivision_distance=rect_sub_distance, max_separation=frame_merge_distance,
|
|
1047
|
+
merge_all=True, resolve_adjacency=False
|
|
1048
|
+
)
|
|
1049
|
+
|
|
1050
|
+
# if there are still multiple windows in a given Face, ensure they do not touch
|
|
1051
|
+
for room in model.rooms:
|
|
1052
|
+
for face in room.faces:
|
|
1053
|
+
if len(face.apertures) > 1:
|
|
1054
|
+
face.offset_aperture_edges(-0.01, 0.01)
|
|
1055
|
+
|
|
1056
|
+
# re-solve adjacency given that all of the previous operations have messed with it
|
|
1057
|
+
model.solve_adjacency(merge_coplanar=True, intersect=True, overwrite=True)
|
|
1058
|
+
|
|
1059
|
+
# reset all display_names so that they are unique (derived from reset identifiers)
|
|
1060
|
+
model.reset_ids() # sets the identifiers based on the display_name
|
|
1061
|
+
for room in model.rooms:
|
|
1062
|
+
room.display_name = room.identifier.replace('_', ' ')
|
|
1063
|
+
if room.story is not None and room.story.startswith('-'):
|
|
1064
|
+
room.story = 'neg{}'.format(room.story[1:])
|
|
1065
|
+
|
|
1066
|
+
# remove the HVAC from any Rooms lacking setpoints
|
|
1067
|
+
rem_msgs = model.properties.energy.remove_hvac_from_no_setpoints()
|
|
1068
|
+
if len(rem_msgs) != 0:
|
|
1069
|
+
print('\n'.join(rem_msgs))
|
|
1070
|
+
|
|
1071
|
+
# rename all face geometry so that it is easy to identify in TRACE 700
|
|
1072
|
+
for room in model.rooms:
|
|
1073
|
+
room.rename_faces_by_attribute()
|
|
1074
|
+
room.rename_apertures_by_attribute()
|
|
1075
|
+
room.rename_doors_by_attribute()
|
|
1076
|
+
model.reset_ids()
|
|
1077
|
+
|
|
1078
|
+
return model
|