topologicpy 0.5.9__py3-none-any.whl → 6.0.0__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.
- topologicpy/Aperture.py +72 -72
- topologicpy/Cell.py +2169 -2169
- topologicpy/CellComplex.py +1137 -1137
- topologicpy/Cluster.py +1288 -1280
- topologicpy/Color.py +423 -423
- topologicpy/Context.py +79 -79
- topologicpy/DGL.py +3213 -3240
- topologicpy/Dictionary.py +698 -698
- topologicpy/Edge.py +1187 -1187
- topologicpy/EnergyModel.py +1180 -1152
- topologicpy/Face.py +2141 -2141
- topologicpy/Graph.py +7768 -7768
- topologicpy/Grid.py +353 -353
- topologicpy/Helper.py +507 -507
- topologicpy/Honeybee.py +461 -461
- topologicpy/Matrix.py +271 -271
- topologicpy/Neo4j.py +521 -521
- topologicpy/Plotly.py +2 -2
- topologicpy/Polyskel.py +541 -541
- topologicpy/Shell.py +1768 -1768
- topologicpy/Speckle.py +508 -508
- topologicpy/Topology.py +7060 -7002
- topologicpy/Vector.py +905 -905
- topologicpy/Vertex.py +1585 -1585
- topologicpy/Wire.py +3050 -3050
- topologicpy/__init__.py +22 -38
- topologicpy/version.py +1 -0
- {topologicpy-0.5.9.dist-info → topologicpy-6.0.0.dist-info}/LICENSE +661 -704
- topologicpy-6.0.0.dist-info/METADATA +751 -0
- topologicpy-6.0.0.dist-info/RECORD +32 -0
- topologicpy/bin/linux/topologic/__init__.py +0 -2
- topologicpy/bin/linux/topologic/libTKBO-6bdf205d.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKBRep-2960a069.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKBool-c44b74bd.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKFillet-9a670ba0.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKG2d-8f31849e.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKG3d-4c6bce57.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKGeomAlgo-26066fd9.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKGeomBase-2116cabe.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKMath-72572fa8.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKMesh-2a060427.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKOffset-6cab68ff.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKPrim-eb1262b3.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKShHealing-e67e5cc7.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKTopAlgo-e4c96c33.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKernel-fb7fe3b7.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libgcc_s-32c1665e.so.1 +0 -0
- topologicpy/bin/linux/topologic/libstdc++-672d7b41.so.6.0.30 +0 -0
- topologicpy/bin/linux/topologic/topologic.cpython-310-x86_64-linux-gnu.so +0 -0
- topologicpy/bin/linux/topologic/topologic.cpython-311-x86_64-linux-gnu.so +0 -0
- topologicpy/bin/linux/topologic/topologic.cpython-38-x86_64-linux-gnu.so +0 -0
- topologicpy/bin/linux/topologic/topologic.cpython-39-x86_64-linux-gnu.so +0 -0
- topologicpy/bin/linux/topologic.libs/libTKBO-6bdf205d.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKBRep-2960a069.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKBool-c44b74bd.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKFillet-9a670ba0.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKG2d-8f31849e.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKG3d-4c6bce57.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKGeomAlgo-26066fd9.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKGeomBase-2116cabe.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKMath-72572fa8.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKMesh-2a060427.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKOffset-6cab68ff.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKPrim-eb1262b3.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKShHealing-e67e5cc7.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKTopAlgo-e4c96c33.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKernel-fb7fe3b7.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libgcc_s-32c1665e.so.1 +0 -0
- topologicpy/bin/linux/topologic.libs/libstdc++-672d7b41.so.6.0.30 +0 -0
- topologicpy/bin/macos/topologic/__init__.py +0 -2
- topologicpy/bin/windows/topologic/TKBO-f6b191de.dll +0 -0
- topologicpy/bin/windows/topologic/TKBRep-e56a600e.dll +0 -0
- topologicpy/bin/windows/topologic/TKBool-7b8d47ae.dll +0 -0
- topologicpy/bin/windows/topologic/TKFillet-0ddbf0a8.dll +0 -0
- topologicpy/bin/windows/topologic/TKG2d-2e2dee3d.dll +0 -0
- topologicpy/bin/windows/topologic/TKG3d-6674513d.dll +0 -0
- topologicpy/bin/windows/topologic/TKGeomAlgo-d240e370.dll +0 -0
- topologicpy/bin/windows/topologic/TKGeomBase-df87aba5.dll +0 -0
- topologicpy/bin/windows/topologic/TKMath-45bd625a.dll +0 -0
- topologicpy/bin/windows/topologic/TKMesh-d6e826b1.dll +0 -0
- topologicpy/bin/windows/topologic/TKOffset-79b9cc94.dll +0 -0
- topologicpy/bin/windows/topologic/TKPrim-aa430a86.dll +0 -0
- topologicpy/bin/windows/topologic/TKShHealing-bb48be89.dll +0 -0
- topologicpy/bin/windows/topologic/TKTopAlgo-7d0d1e22.dll +0 -0
- topologicpy/bin/windows/topologic/TKernel-08c8cfbb.dll +0 -0
- topologicpy/bin/windows/topologic/__init__.py +0 -2
- topologicpy/bin/windows/topologic/topologic.cp310-win_amd64.pyd +0 -0
- topologicpy/bin/windows/topologic/topologic.cp311-win_amd64.pyd +0 -0
- topologicpy/bin/windows/topologic/topologic.cp38-win_amd64.pyd +0 -0
- topologicpy/bin/windows/topologic/topologic.cp39-win_amd64.pyd +0 -0
- topologicpy-0.5.9.dist-info/METADATA +0 -86
- topologicpy-0.5.9.dist-info/RECORD +0 -91
- {topologicpy-0.5.9.dist-info → topologicpy-6.0.0.dist-info}/WHEEL +0 -0
- {topologicpy-0.5.9.dist-info → topologicpy-6.0.0.dist-info}/top_level.txt +0 -0
topologicpy/EnergyModel.py
CHANGED
@@ -1,1152 +1,1180 @@
|
|
1
|
-
# Copyright (C) 2024
|
2
|
-
# Wassim Jabi <wassim.jabi@gmail.com>
|
3
|
-
#
|
4
|
-
# This program is free software: you can redistribute it and/or modify it under
|
5
|
-
# the terms of the GNU Affero General Public License as published by the Free Software
|
6
|
-
# Foundation, either version 3 of the License, or (at your option) any later
|
7
|
-
# version.
|
8
|
-
#
|
9
|
-
# This program is distributed in the hope that it will be useful, but WITHOUT
|
10
|
-
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
11
|
-
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
12
|
-
# details.
|
13
|
-
#
|
14
|
-
# You should have received a copy of the GNU Affero General Public License along with
|
15
|
-
# this program. If not, see <https://www.gnu.org/licenses/>.
|
16
|
-
|
17
|
-
import topologic
|
18
|
-
import shutil
|
19
|
-
import math
|
20
|
-
from collections import OrderedDict
|
21
|
-
import os
|
22
|
-
from os.path import exists
|
23
|
-
from datetime import datetime
|
24
|
-
import warnings
|
25
|
-
|
26
|
-
try:
|
27
|
-
from tqdm.auto import tqdm
|
28
|
-
except:
|
29
|
-
print("EnergyModel - Installing required tqdm library.")
|
30
|
-
try:
|
31
|
-
os.system("pip install tqdm")
|
32
|
-
except:
|
33
|
-
os.system("pip install tqdm --user")
|
34
|
-
try:
|
35
|
-
from tqdm.auto import tqdm
|
36
|
-
print("EnergyModel - tqdm library installed correctly.")
|
37
|
-
except:
|
38
|
-
warnings.warn("EnergyModel - Error: Could not import tqdm.")
|
39
|
-
|
40
|
-
class EnergyModel:
|
41
|
-
'''
|
42
|
-
@staticmethod
|
43
|
-
def ByOSMFile(file):
|
44
|
-
"""
|
45
|
-
Creates an EnergyModel from the input OSM file path.
|
46
|
-
|
47
|
-
Parameters
|
48
|
-
----------
|
49
|
-
path : string
|
50
|
-
The path to the input .OSM file.
|
51
|
-
|
52
|
-
Returns
|
53
|
-
-------
|
54
|
-
openstudio.openstudiomodelcore.Model
|
55
|
-
The OSM model.
|
56
|
-
|
57
|
-
"""
|
58
|
-
if not file:
|
59
|
-
print("EnergyModel.ByOSMFile - Error: The input path is not valid. Returning None.")
|
60
|
-
return None
|
61
|
-
osModel = file.read()
|
62
|
-
if osModel.isNull():
|
63
|
-
print("EnergyModel.ByOSMFile - Error: The openstudio model is null. Returning None.")
|
64
|
-
return None
|
65
|
-
else:
|
66
|
-
osModel = osModel.get()
|
67
|
-
return osModel
|
68
|
-
'''
|
69
|
-
|
70
|
-
@staticmethod
|
71
|
-
def ByOSMPath(path: str):
|
72
|
-
"""
|
73
|
-
Creates an EnergyModel from the input OSM file path.
|
74
|
-
|
75
|
-
Parameters
|
76
|
-
----------
|
77
|
-
path : string
|
78
|
-
The path to the input .OSM file.
|
79
|
-
|
80
|
-
Returns
|
81
|
-
-------
|
82
|
-
openstudio.openstudiomodelcore.Model
|
83
|
-
The OSM model.
|
84
|
-
|
85
|
-
"""
|
86
|
-
try:
|
87
|
-
import openstudio
|
88
|
-
openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
|
89
|
-
except:
|
90
|
-
print("EnergyModel.ByOSMPath - Information: Installing required openstudio library.")
|
91
|
-
try:
|
92
|
-
os.system("pip install openstudio")
|
93
|
-
except:
|
94
|
-
os.system("pip install openstudio --user")
|
95
|
-
try:
|
96
|
-
import openstudio
|
97
|
-
openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
|
98
|
-
print("EnergyModel.ByOSMPath - Information: openstudio library installed correctly.")
|
99
|
-
except:
|
100
|
-
warnings.warn("EnergyModel - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
|
101
|
-
return None
|
102
|
-
|
103
|
-
if not path:
|
104
|
-
print("EnergyModel.ByOSMPath - Error: The input path is not valid. Returning None.")
|
105
|
-
return None
|
106
|
-
translator = openstudio.osversion.VersionTranslator()
|
107
|
-
osmPath = openstudio.openstudioutilitiescore.toPath(path)
|
108
|
-
osModel = translator.loadModel(osmPath)
|
109
|
-
if osModel.isNull():
|
110
|
-
print("EnergyModel.ByImportedOSM - Error: The openstudio model is null. Returning None.")
|
111
|
-
return None
|
112
|
-
else:
|
113
|
-
osModel = osModel.get()
|
114
|
-
return osModel
|
115
|
-
|
116
|
-
@staticmethod
|
117
|
-
def ByTopology(building : topologic.Topology,
|
118
|
-
shadingSurfaces : topologic.Topology = None,
|
119
|
-
osModelPath : str = None,
|
120
|
-
weatherFilePath : str = None,
|
121
|
-
designDayFilePath : str = None,
|
122
|
-
floorLevels : list = None,
|
123
|
-
buildingName : str = "TopologicBuilding",
|
124
|
-
buildingType : str = "Commercial",
|
125
|
-
northAxis : float = 0.0,
|
126
|
-
glazingRatio : float = 0.0,
|
127
|
-
coolingTemp : float = 25.0,
|
128
|
-
heatingTemp : float = 20.0,
|
129
|
-
defaultSpaceType : str = "189.1-2009 - Office - WholeBuilding - Lg Office - CZ4-8",
|
130
|
-
spaceNameKey : str = "TOPOLOGIC_name",
|
131
|
-
spaceTypeKey : str = "TOPOLOGIC_type"):
|
132
|
-
"""
|
133
|
-
Creates an EnergyModel from the input topology and parameters.
|
134
|
-
|
135
|
-
Parameters
|
136
|
-
----------
|
137
|
-
building : topologic.CellComplex or topologic.Cell
|
138
|
-
The input building topology.
|
139
|
-
shadingSurfaces : topologic.Topology , optional
|
140
|
-
The input topology for shading surfaces. The default is None.
|
141
|
-
osModelPath : str , optional
|
142
|
-
The path to the template OSM file. The default is "./assets/EnergyModel/OSMTemplate-OfficeBuilding-3.5.0.osm".
|
143
|
-
weatherFilePath : str , optional
|
144
|
-
The input energy plus weather (epw) file. The default is "./assets/EnergyModel/GBR_London.Gatwick.037760_IWEC.epw".
|
145
|
-
designDayFilePath : str , optional
|
146
|
-
The input design day (ddy) file path. The default is "./assets/EnergyModel/GBR_London.Gatwick.037760_IWEC.ddy",
|
147
|
-
floorLevels : list , optional
|
148
|
-
The list of floor level Z heights including the lowest most and the highest most levels. If set to None, this method will attempt to
|
149
|
-
find the floor levels from the horizontal faces of the input topology
|
150
|
-
buildingName : str , optional
|
151
|
-
The desired name of the building. The default is "TopologicBuilding".
|
152
|
-
buildingType : str , optional
|
153
|
-
The building type. The default is "Commercial".
|
154
|
-
defaultSpaceType : str , optional
|
155
|
-
The default space type to apply to spaces that do not have a type assigned in their dictionary. The default is "189.1-2009 - Office - WholeBuilding - Lg Office - CZ4-8".
|
156
|
-
northAxis : float , optional
|
157
|
-
The counter-clockwise angle in degrees from the positive Y-axis representing the direction of the north axis. The default is 0.0.
|
158
|
-
glazingRatio : float , optional
|
159
|
-
The glazing ratio (ratio of windows to wall) to use for exterior vertical walls that do not have apertures. If you do not wish to use a glazing ratio, set it to 0. The default is 0.
|
160
|
-
coolingTemp : float , optional
|
161
|
-
The desired temperature in degrees at which the cooling system should activate. The default is 25.0.
|
162
|
-
heatingTemp : float , optional
|
163
|
-
The desired temperature in degrees at which the heating system should activate. The default is 25.0..
|
164
|
-
spaceNameKey : str , optional
|
165
|
-
The dictionary key to use to find the space name value. The default is "Name".
|
166
|
-
spaceTypeKey : str , optional
|
167
|
-
The dictionary key to use to find the space type value. The default is "Type".
|
168
|
-
|
169
|
-
Returns
|
170
|
-
-------
|
171
|
-
openstudio.openstudiomodelcore.Model
|
172
|
-
The created OSM model.
|
173
|
-
|
174
|
-
"""
|
175
|
-
from topologicpy.Face import Face
|
176
|
-
from topologicpy.Cell import Cell
|
177
|
-
from topologicpy.Topology import Topology
|
178
|
-
from topologicpy.Dictionary import Dictionary
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
elif
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
if
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
if
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
if
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
path
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
The
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
The input
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
"""
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
The
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
spaceCell =
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1
|
+
# Copyright (C) 2024
|
2
|
+
# Wassim Jabi <wassim.jabi@gmail.com>
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify it under
|
5
|
+
# the terms of the GNU Affero General Public License as published by the Free Software
|
6
|
+
# Foundation, either version 3 of the License, or (at your option) any later
|
7
|
+
# version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
10
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
11
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
12
|
+
# details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU Affero General Public License along with
|
15
|
+
# this program. If not, see <https://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
import topologic_core as topologic
|
18
|
+
import shutil
|
19
|
+
import math
|
20
|
+
from collections import OrderedDict
|
21
|
+
import os
|
22
|
+
from os.path import exists
|
23
|
+
from datetime import datetime
|
24
|
+
import warnings
|
25
|
+
|
26
|
+
try:
|
27
|
+
from tqdm.auto import tqdm
|
28
|
+
except:
|
29
|
+
print("EnergyModel - Installing required tqdm library.")
|
30
|
+
try:
|
31
|
+
os.system("pip install tqdm")
|
32
|
+
except:
|
33
|
+
os.system("pip install tqdm --user")
|
34
|
+
try:
|
35
|
+
from tqdm.auto import tqdm
|
36
|
+
print("EnergyModel - tqdm library installed correctly.")
|
37
|
+
except:
|
38
|
+
warnings.warn("EnergyModel - Error: Could not import tqdm.")
|
39
|
+
|
40
|
+
class EnergyModel:
|
41
|
+
'''
|
42
|
+
@staticmethod
|
43
|
+
def ByOSMFile(file):
|
44
|
+
"""
|
45
|
+
Creates an EnergyModel from the input OSM file path.
|
46
|
+
|
47
|
+
Parameters
|
48
|
+
----------
|
49
|
+
path : string
|
50
|
+
The path to the input .OSM file.
|
51
|
+
|
52
|
+
Returns
|
53
|
+
-------
|
54
|
+
openstudio.openstudiomodelcore.Model
|
55
|
+
The OSM model.
|
56
|
+
|
57
|
+
"""
|
58
|
+
if not file:
|
59
|
+
print("EnergyModel.ByOSMFile - Error: The input path is not valid. Returning None.")
|
60
|
+
return None
|
61
|
+
osModel = file.read()
|
62
|
+
if osModel.isNull():
|
63
|
+
print("EnergyModel.ByOSMFile - Error: The openstudio model is null. Returning None.")
|
64
|
+
return None
|
65
|
+
else:
|
66
|
+
osModel = osModel.get()
|
67
|
+
return osModel
|
68
|
+
'''
|
69
|
+
|
70
|
+
@staticmethod
|
71
|
+
def ByOSMPath(path: str):
|
72
|
+
"""
|
73
|
+
Creates an EnergyModel from the input OSM file path.
|
74
|
+
|
75
|
+
Parameters
|
76
|
+
----------
|
77
|
+
path : string
|
78
|
+
The path to the input .OSM file.
|
79
|
+
|
80
|
+
Returns
|
81
|
+
-------
|
82
|
+
openstudio.openstudiomodelcore.Model
|
83
|
+
The OSM model.
|
84
|
+
|
85
|
+
"""
|
86
|
+
try:
|
87
|
+
import openstudio
|
88
|
+
openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
|
89
|
+
except:
|
90
|
+
print("EnergyModel.ByOSMPath - Information: Installing required openstudio library.")
|
91
|
+
try:
|
92
|
+
os.system("pip install openstudio")
|
93
|
+
except:
|
94
|
+
os.system("pip install openstudio --user")
|
95
|
+
try:
|
96
|
+
import openstudio
|
97
|
+
openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
|
98
|
+
print("EnergyModel.ByOSMPath - Information: openstudio library installed correctly.")
|
99
|
+
except:
|
100
|
+
warnings.warn("EnergyModel.ByOSMPath - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
|
101
|
+
return None
|
102
|
+
|
103
|
+
if not path:
|
104
|
+
print("EnergyModel.ByOSMPath - Error: The input path is not valid. Returning None.")
|
105
|
+
return None
|
106
|
+
translator = openstudio.osversion.VersionTranslator()
|
107
|
+
osmPath = openstudio.openstudioutilitiescore.toPath(path)
|
108
|
+
osModel = translator.loadModel(osmPath)
|
109
|
+
if osModel.isNull():
|
110
|
+
print("EnergyModel.ByImportedOSM - Error: The openstudio model is null. Returning None.")
|
111
|
+
return None
|
112
|
+
else:
|
113
|
+
osModel = osModel.get()
|
114
|
+
return osModel
|
115
|
+
|
116
|
+
@staticmethod
|
117
|
+
def ByTopology(building : topologic.Topology,
|
118
|
+
shadingSurfaces : topologic.Topology = None,
|
119
|
+
osModelPath : str = None,
|
120
|
+
weatherFilePath : str = None,
|
121
|
+
designDayFilePath : str = None,
|
122
|
+
floorLevels : list = None,
|
123
|
+
buildingName : str = "TopologicBuilding",
|
124
|
+
buildingType : str = "Commercial",
|
125
|
+
northAxis : float = 0.0,
|
126
|
+
glazingRatio : float = 0.0,
|
127
|
+
coolingTemp : float = 25.0,
|
128
|
+
heatingTemp : float = 20.0,
|
129
|
+
defaultSpaceType : str = "189.1-2009 - Office - WholeBuilding - Lg Office - CZ4-8",
|
130
|
+
spaceNameKey : str = "TOPOLOGIC_name",
|
131
|
+
spaceTypeKey : str = "TOPOLOGIC_type"):
|
132
|
+
"""
|
133
|
+
Creates an EnergyModel from the input topology and parameters.
|
134
|
+
|
135
|
+
Parameters
|
136
|
+
----------
|
137
|
+
building : topologic.CellComplex or topologic.Cell
|
138
|
+
The input building topology.
|
139
|
+
shadingSurfaces : topologic.Topology , optional
|
140
|
+
The input topology for shading surfaces. The default is None.
|
141
|
+
osModelPath : str , optional
|
142
|
+
The path to the template OSM file. The default is "./assets/EnergyModel/OSMTemplate-OfficeBuilding-3.5.0.osm".
|
143
|
+
weatherFilePath : str , optional
|
144
|
+
The input energy plus weather (epw) file. The default is "./assets/EnergyModel/GBR_London.Gatwick.037760_IWEC.epw".
|
145
|
+
designDayFilePath : str , optional
|
146
|
+
The input design day (ddy) file path. The default is "./assets/EnergyModel/GBR_London.Gatwick.037760_IWEC.ddy",
|
147
|
+
floorLevels : list , optional
|
148
|
+
The list of floor level Z heights including the lowest most and the highest most levels. If set to None, this method will attempt to
|
149
|
+
find the floor levels from the horizontal faces of the input topology
|
150
|
+
buildingName : str , optional
|
151
|
+
The desired name of the building. The default is "TopologicBuilding".
|
152
|
+
buildingType : str , optional
|
153
|
+
The building type. The default is "Commercial".
|
154
|
+
defaultSpaceType : str , optional
|
155
|
+
The default space type to apply to spaces that do not have a type assigned in their dictionary. The default is "189.1-2009 - Office - WholeBuilding - Lg Office - CZ4-8".
|
156
|
+
northAxis : float , optional
|
157
|
+
The counter-clockwise angle in degrees from the positive Y-axis representing the direction of the north axis. The default is 0.0.
|
158
|
+
glazingRatio : float , optional
|
159
|
+
The glazing ratio (ratio of windows to wall) to use for exterior vertical walls that do not have apertures. If you do not wish to use a glazing ratio, set it to 0. The default is 0.
|
160
|
+
coolingTemp : float , optional
|
161
|
+
The desired temperature in degrees at which the cooling system should activate. The default is 25.0.
|
162
|
+
heatingTemp : float , optional
|
163
|
+
The desired temperature in degrees at which the heating system should activate. The default is 25.0..
|
164
|
+
spaceNameKey : str , optional
|
165
|
+
The dictionary key to use to find the space name value. The default is "Name".
|
166
|
+
spaceTypeKey : str , optional
|
167
|
+
The dictionary key to use to find the space type value. The default is "Type".
|
168
|
+
|
169
|
+
Returns
|
170
|
+
-------
|
171
|
+
openstudio.openstudiomodelcore.Model
|
172
|
+
The created OSM model.
|
173
|
+
|
174
|
+
"""
|
175
|
+
from topologicpy.Face import Face
|
176
|
+
from topologicpy.Cell import Cell
|
177
|
+
from topologicpy.Topology import Topology
|
178
|
+
from topologicpy.Dictionary import Dictionary
|
179
|
+
try:
|
180
|
+
import openstudio
|
181
|
+
openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
|
182
|
+
except:
|
183
|
+
print("EnergyModel.ByTopology - Information: Installing required openstudio library.")
|
184
|
+
try:
|
185
|
+
os.system("pip install openstudio")
|
186
|
+
except:
|
187
|
+
os.system("pip install openstudio --user")
|
188
|
+
try:
|
189
|
+
import openstudio
|
190
|
+
openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
|
191
|
+
print("EnergyModel.ByTopology - Information: openstudio library installed correctly.")
|
192
|
+
except:
|
193
|
+
warnings.warn("EnergyModel.ByTopology - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
|
194
|
+
return None
|
195
|
+
|
196
|
+
def getKeyName(d, keyName):
|
197
|
+
keys = d.Keys()
|
198
|
+
for key in keys:
|
199
|
+
if key.lower() == keyName.lower():
|
200
|
+
return key
|
201
|
+
return None
|
202
|
+
|
203
|
+
def createUniqueName(name, nameList, number):
|
204
|
+
if number > 9999:
|
205
|
+
return name+"_9999"
|
206
|
+
if not (name in nameList):
|
207
|
+
return name
|
208
|
+
elif not ((name+"_"+"{:04d}".format(number)) in nameList):
|
209
|
+
return name+"_"+"{:04d}".format(number)
|
210
|
+
else:
|
211
|
+
return createUniqueName(name,nameList, number+1)
|
212
|
+
|
213
|
+
def getFloorLevels(building):
|
214
|
+
from topologicpy.Vertex import Vertex
|
215
|
+
from topologicpy.Cell import Cell
|
216
|
+
from topologicpy.CellComplex import CellComplex
|
217
|
+
|
218
|
+
if isinstance(building, topologic.CellComplex):
|
219
|
+
d = CellComplex.Decompose(building)
|
220
|
+
bhf = d['bottomHorizontalFaces']
|
221
|
+
ihf = d['internalHorizontalFaces']
|
222
|
+
thf = d ['topHorizontalFaces']
|
223
|
+
hf = bhf+ihf+thf
|
224
|
+
elif isinstance(building, topologic.Cell):
|
225
|
+
d = Cell.Decompose(building)
|
226
|
+
bhf = d['bottomHorizontalFaces']
|
227
|
+
thf = d ['topHorizontalFaces']
|
228
|
+
hf = bhf+thf
|
229
|
+
else:
|
230
|
+
return None
|
231
|
+
floorLevels = [Vertex.Z(Topology.Centroid(f)) for f in hf]
|
232
|
+
floorLevels = list(set(floorLevels))
|
233
|
+
floorLevels.sort()
|
234
|
+
return floorLevels
|
235
|
+
|
236
|
+
if not osModelPath:
|
237
|
+
import os
|
238
|
+
osModelPath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "OSMTemplate-OfficeBuilding-3.5.0.osm")
|
239
|
+
if not weatherFilePath or not designDayFilePath:
|
240
|
+
import os
|
241
|
+
weatherFilePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "GBR_London.Gatwick.037760_IWEC.epw")
|
242
|
+
designDayFilePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "GBR_London.Gatwick.037760_IWEC.ddy")
|
243
|
+
translator = openstudio.osversion.VersionTranslator()
|
244
|
+
osmFile = openstudio.openstudioutilitiescore.toPath(osModelPath)
|
245
|
+
osModel = translator.loadModel(osmFile)
|
246
|
+
if osModel.isNull():
|
247
|
+
print("EnergyModel.ByTopology - Error: The openstudio model is null. Returning None.")
|
248
|
+
return None
|
249
|
+
else:
|
250
|
+
osModel = osModel.get()
|
251
|
+
osEPWFile = openstudio.openstudioutilitiesfiletypes.EpwFile.load(openstudio.toPath(weatherFilePath))
|
252
|
+
if osEPWFile.is_initialized():
|
253
|
+
osEPWFile = osEPWFile.get()
|
254
|
+
openstudio.model.WeatherFile.setWeatherFile(osModel, osEPWFile)
|
255
|
+
ddyModel = openstudio.openstudioenergyplus.loadAndTranslateIdf(openstudio.toPath(designDayFilePath))
|
256
|
+
if ddyModel.is_initialized():
|
257
|
+
ddyModel = ddyModel.get()
|
258
|
+
for ddy in ddyModel.getObjectsByType(openstudio.IddObjectType("OS:SizingPeriod:DesignDay")):
|
259
|
+
osModel.addObject(ddy.clone())
|
260
|
+
else:
|
261
|
+
print("EnergyModel.ByTopology - Error: The ddy file is not initialized. Returning None.")
|
262
|
+
return None
|
263
|
+
osBuilding = osModel.getBuilding()
|
264
|
+
if not floorLevels:
|
265
|
+
floorLevels = getFloorLevels(building)
|
266
|
+
osBuilding.setStandardsNumberOfStories(len(floorLevels) - 1)
|
267
|
+
osBuilding.setNominalFloortoFloorHeight(max(floorLevels) / osBuilding.standardsNumberOfStories().get())
|
268
|
+
osBuilding.setDefaultConstructionSet(osModel.getDefaultConstructionSets()[0])
|
269
|
+
osBuilding.setDefaultScheduleSet(osModel.getDefaultScheduleSets()[0])
|
270
|
+
osBuilding.setName(buildingName)
|
271
|
+
osBuilding.setStandardsBuildingType(buildingType)
|
272
|
+
osBuilding.setSpaceType(osModel.getSpaceTypeByName(defaultSpaceType).get())
|
273
|
+
for storyNumber in range(osBuilding.standardsNumberOfStories().get()):
|
274
|
+
osBuildingStory = openstudio.model.BuildingStory(osModel)
|
275
|
+
osBuildingStory.setName("STORY_" + str(storyNumber))
|
276
|
+
osBuildingStory.setNominalZCoordinate(floorLevels[storyNumber])
|
277
|
+
osBuildingStory.setNominalFloortoFloorHeight(osBuilding.nominalFloortoFloorHeight().get())
|
278
|
+
osBuilding.setNorthAxis(northAxis)
|
279
|
+
heatingScheduleConstant = openstudio.model.ScheduleConstant(osModel)
|
280
|
+
heatingScheduleConstant.setValue(heatingTemp)
|
281
|
+
coolingScheduleConstant = openstudio.model.ScheduleConstant(osModel)
|
282
|
+
coolingScheduleConstant.setValue(coolingTemp)
|
283
|
+
osThermostat = openstudio.model.ThermostatSetpointDualSetpoint(osModel)
|
284
|
+
osThermostat.setHeatingSetpointTemperatureSchedule(heatingScheduleConstant)
|
285
|
+
osThermostat.setCoolingSetpointTemperatureSchedule(coolingScheduleConstant)
|
286
|
+
osBuildingStorys = list(osModel.getBuildingStorys())
|
287
|
+
osBuildingStorys.sort(key=lambda x: x.nominalZCoordinate().get())
|
288
|
+
osSpaces = []
|
289
|
+
spaceNames = []
|
290
|
+
if isinstance(building, topologic.CellComplex):
|
291
|
+
building_cells = Topology.SubTopologies(building, "Cell")
|
292
|
+
elif isinstance(building, topologic.Cell):
|
293
|
+
building_cells = [building]
|
294
|
+
for spaceNumber, buildingCell in enumerate(building_cells):
|
295
|
+
osSpace = openstudio.model.Space(osModel)
|
296
|
+
osSpaceZ = buildingCell.CenterOfMass().Z()
|
297
|
+
osBuildingStory = osBuildingStorys[0]
|
298
|
+
for x in osBuildingStorys:
|
299
|
+
osBuildingStoryZ = x.nominalZCoordinate().get()
|
300
|
+
if osBuildingStoryZ + x.nominalFloortoFloorHeight().get() < osSpaceZ:
|
301
|
+
continue
|
302
|
+
if osBuildingStoryZ < osSpaceZ:
|
303
|
+
osBuildingStory = x
|
304
|
+
break
|
305
|
+
osSpace.setBuildingStory(osBuildingStory)
|
306
|
+
cellDictionary = Topology.Dictionary(buildingCell)
|
307
|
+
if not cellDictionary == None:
|
308
|
+
keys = Dictionary.Keys(cellDictionary)
|
309
|
+
else:
|
310
|
+
keys = []
|
311
|
+
if len(keys) > 0:
|
312
|
+
if spaceTypeKey:
|
313
|
+
keyType = getKeyName(cellDictionary, spaceTypeKey)
|
314
|
+
else:
|
315
|
+
keyType = getKeyName(cellDictionary, 'type')
|
316
|
+
if keyType:
|
317
|
+
osSpaceTypeName = Dictionary.ValueAtKey(cellDictionary,keyType)
|
318
|
+
else:
|
319
|
+
osSpaceTypeName = defaultSpaceType
|
320
|
+
if osSpaceTypeName:
|
321
|
+
sp_ = osModel.getSpaceTypeByName(osSpaceTypeName)
|
322
|
+
if sp_.is_initialized():
|
323
|
+
osSpace.setSpaceType(sp_.get())
|
324
|
+
if spaceNameKey:
|
325
|
+
keyName = getKeyName(cellDictionary, spaceNameKey)
|
326
|
+
|
327
|
+
else:
|
328
|
+
keyName = getKeyName(cellDictionary, 'name')
|
329
|
+
osSpaceName = None
|
330
|
+
if keyName:
|
331
|
+
osSpaceName = createUniqueName(Dictionary.ValueAtKey(cellDictionary,keyName),spaceNames, 1)
|
332
|
+
if osSpaceName:
|
333
|
+
osSpace.setName(osSpaceName)
|
334
|
+
else:
|
335
|
+
osSpaceName = "SPACE_" + "{:04d}".format(spaceNumber)
|
336
|
+
osSpace.setName(osSpaceName)
|
337
|
+
sp_ = osModel.getSpaceTypeByName(defaultSpaceType)
|
338
|
+
if sp_.is_initialized():
|
339
|
+
osSpace.setSpaceType(sp_.get())
|
340
|
+
spaceNames.append(osSpaceName)
|
341
|
+
cellFaces = Topology.SubTopologies(buildingCell, "Face")
|
342
|
+
if cellFaces:
|
343
|
+
for faceNumber, buildingFace in enumerate(cellFaces):
|
344
|
+
osFacePoints = []
|
345
|
+
for vertex in Topology.SubTopologies(buildingFace.ExternalBoundary(), "Vertex"):
|
346
|
+
osFacePoints.append(openstudio.Point3d(vertex.X(), vertex.Y(), vertex.Z()))
|
347
|
+
osSurface = openstudio.model.Surface(osFacePoints, osModel)
|
348
|
+
faceNormal = Face.Normal(buildingFace)
|
349
|
+
osFaceNormal = openstudio.Vector3d(faceNormal[0], faceNormal[1], faceNormal[2])
|
350
|
+
osFaceNormal.normalize()
|
351
|
+
if osFaceNormal.dot(osSurface.outwardNormal()) < 1e-6:
|
352
|
+
osSurface.setVertices(list(reversed(osFacePoints)))
|
353
|
+
osSurface.setSpace(osSpace)
|
354
|
+
faceCells = Topology.AdjacentTopologies(buildingFace, building, topologyType="cell")
|
355
|
+
if len(faceCells) == 1: #Exterior Surfaces
|
356
|
+
osSurface.setOutsideBoundaryCondition("Outdoors")
|
357
|
+
if (math.degrees(math.acos(osSurface.outwardNormal().dot(openstudio.Vector3d(0, 0, 1)))) > 135) or (math.degrees(math.acos(osSurface.outwardNormal().dot(openstudio.Vector3d(0, 0, 1)))) < 45):
|
358
|
+
osSurface.setSurfaceType("RoofCeiling")
|
359
|
+
osSurface.setOutsideBoundaryCondition("Outdoors")
|
360
|
+
osSurface.setName(osSpace.name().get() + "_TopHorizontalSlab_" + str(faceNumber))
|
361
|
+
if max(list(map(lambda vertex: vertex.Z(), Topology.SubTopologies(buildingFace, "Vertex")))) < 1e-6:
|
362
|
+
osSurface.setSurfaceType("Floor")
|
363
|
+
osSurface.setOutsideBoundaryCondition("Ground")
|
364
|
+
osSurface.setName(osSpace.name().get() + "_BottomHorizontalSlab_" + str(faceNumber))
|
365
|
+
else:
|
366
|
+
osSurface.setSurfaceType("Wall")
|
367
|
+
osSurface.setOutsideBoundaryCondition("Outdoors")
|
368
|
+
osSurface.setName(osSpace.name().get() + "_ExternalVerticalFace_" + str(faceNumber))
|
369
|
+
# Check for exterior apertures
|
370
|
+
faceDictionary = buildingFace.GetDictionary()
|
371
|
+
apertures = []
|
372
|
+
_ = buildingFace.Apertures(apertures)
|
373
|
+
if len(apertures) > 0:
|
374
|
+
for aperture in apertures:
|
375
|
+
osSubSurfacePoints = []
|
376
|
+
#apertureFace = TopologySubTopologies.processItem([aperture, topologic.Face])[0]
|
377
|
+
apertureFace = topologic.Aperture.Topology(aperture)
|
378
|
+
for vertex in Topology.SubTopologies(apertureFace.ExternalBoundary(), "Vertex"):
|
379
|
+
osSubSurfacePoints.append(openstudio.Point3d(vertex.X(), vertex.Y(), vertex.Z()))
|
380
|
+
osSubSurface = openstudio.model.SubSurface(osSubSurfacePoints, osModel)
|
381
|
+
apertureFaceNormal = Face.Normal(apertureFace)
|
382
|
+
osSubSurfaceNormal = openstudio.Vector3d(apertureFaceNormal[0], apertureFaceNormal[1], apertureFaceNormal[2])
|
383
|
+
osSubSurfaceNormal.normalize()
|
384
|
+
if osSubSurfaceNormal.dot(osSubSurface.outwardNormal()) < 1e-6:
|
385
|
+
osSubSurface.setVertices(list(reversed(osSubSurfacePoints)))
|
386
|
+
osSubSurface.setSubSurfaceType("FixedWindow")
|
387
|
+
osSubSurface.setSurface(osSurface)
|
388
|
+
else:
|
389
|
+
# Get the dictionary keys
|
390
|
+
keys = faceDictionary.Keys()
|
391
|
+
if ('TOPOLOGIC_glazing_ratio' in keys):
|
392
|
+
faceGlazingRatio = Dictionary.ValueAtKey(faceDictionary,'TOPOLOGIC_glazing_ratio')
|
393
|
+
if faceGlazingRatio and faceGlazingRatio >= 0.01:
|
394
|
+
osSurface.setWindowToWallRatio(faceGlazingRatio)
|
395
|
+
else:
|
396
|
+
if glazingRatio > 0.01: #Glazing ratio must be more than 1% to make any sense.
|
397
|
+
osSurface.setWindowToWallRatio(glazingRatio)
|
398
|
+
else: #Interior Surfaces
|
399
|
+
if (math.degrees(math.acos(osSurface.outwardNormal().dot(openstudio.Vector3d(0, 0, 1)))) > 135):
|
400
|
+
osSurface.setSurfaceType("Floor")
|
401
|
+
osSurface.setName(osSpace.name().get() + "_InternalHorizontalFace_" + str(faceNumber))
|
402
|
+
elif (math.degrees(math.acos(osSurface.outwardNormal().dot(openstudio.Vector3d(0, 0, 1)))) < 40):
|
403
|
+
osSurface.setSurfaceType("RoofCeiling")
|
404
|
+
osSurface.setName(osSpace.name().get() + "_InternalHorizontalFace_" + str(faceNumber))
|
405
|
+
else:
|
406
|
+
osSurface.setSurfaceType("Wall")
|
407
|
+
osSurface.setName(osSpace.name().get() + "_InternalVerticalFace_" + str(faceNumber))
|
408
|
+
# Check for interior apertures
|
409
|
+
faceDictionary = buildingFace.GetDictionary()
|
410
|
+
apertures = []
|
411
|
+
_ = buildingFace.Apertures(apertures)
|
412
|
+
if len(apertures) > 0:
|
413
|
+
for aperture in apertures:
|
414
|
+
osSubSurfacePoints = []
|
415
|
+
#apertureFace = TopologySubTopologies.processItem([aperture, "Face"])[0]
|
416
|
+
apertureFace = topologic.Aperture.Topology(aperture)
|
417
|
+
for vertex in Topology.SubTopologies(apertureFace.ExternalBoundary(), "Vertex"):
|
418
|
+
osSubSurfacePoints.append(openstudio.Point3d(vertex.X(), vertex.Y(), vertex.Z()))
|
419
|
+
osSubSurface = openstudio.model.SubSurface(osSubSurfacePoints, osModel)
|
420
|
+
apertureFaceNormal = Face.Normal(apertureFace)
|
421
|
+
osSubSurfaceNormal = openstudio.Vector3d(apertureFaceNormal[0], apertureFaceNormal[1], apertureFaceNormal[2])
|
422
|
+
osSubSurfaceNormal.normalize()
|
423
|
+
if osSubSurfaceNormal.dot(osSubSurface.outwardNormal()) < 1e-6:
|
424
|
+
osSubSurface.setVertices(list(reversed(osSubSurfacePoints)))
|
425
|
+
osSubSurface.setSubSurfaceType("Door") #We are assuming all interior apertures to be doors
|
426
|
+
osSubSurface.setSurface(osSurface)
|
427
|
+
|
428
|
+
osThermalZone = openstudio.model.ThermalZone(osModel)
|
429
|
+
osThermalZone.setVolume(Cell.Volume(buildingCell))
|
430
|
+
osThermalZone.setName(osSpace.name().get() + "_THERMAL_ZONE")
|
431
|
+
osThermalZone.setUseIdealAirLoads(True)
|
432
|
+
osThermalZone.setVolume(Cell.Volume(buildingCell))
|
433
|
+
osThermalZone.setThermostatSetpointDualSetpoint(osThermostat)
|
434
|
+
osSpace.setThermalZone(osThermalZone)
|
435
|
+
|
436
|
+
for x in osSpaces:
|
437
|
+
if osSpace.boundingBox().intersects(x.boundingBox()):
|
438
|
+
osSpace.matchSurfaces(x)
|
439
|
+
osSpaces.append(osSpace)
|
440
|
+
|
441
|
+
|
442
|
+
if shadingSurfaces:
|
443
|
+
osShadingGroup = openstudio.model.ShadingSurfaceGroup(osModel)
|
444
|
+
for faceIndex, shadingFace in enumerate(Topology.SubTopologies(shadingSurfaces, "Face")):
|
445
|
+
facePoints = []
|
446
|
+
for aVertex in Topology.SubTopologies(shadingFace.ExternalBoundary(), "Vertex"):
|
447
|
+
facePoints.append(openstudio.Point3d(aVertex.X(), aVertex.Y(), aVertex.Z()))
|
448
|
+
aShadingSurface = openstudio.model.ShadingSurface(facePoints, osModel)
|
449
|
+
faceNormal = Face.Normal(shadingFace)
|
450
|
+
osFaceNormal = openstudio.Vector3d(faceNormal[0], faceNormal[1], faceNormal[2])
|
451
|
+
osFaceNormal.normalize()
|
452
|
+
if osFaceNormal.dot(aShadingSurface.outwardNormal()) < 0:
|
453
|
+
aShadingSurface.setVertices(list(reversed(facePoints)))
|
454
|
+
aShadingSurface.setName("SHADINGSURFACE_" + str(faceIndex))
|
455
|
+
aShadingSurface.setShadingSurfaceGroup(osShadingGroup)
|
456
|
+
|
457
|
+
osModel.purgeUnusedResourceObjects()
|
458
|
+
return osModel
|
459
|
+
|
460
|
+
@staticmethod
|
461
|
+
def ColumnNames(model, reportName, tableName):
|
462
|
+
"""
|
463
|
+
Returns the list of column names given an OSM model, report name, and table name.
|
464
|
+
|
465
|
+
Parameters
|
466
|
+
----------
|
467
|
+
model : openstudio.openstudiomodelcore.Model
|
468
|
+
The input OSM model.
|
469
|
+
reportName : str
|
470
|
+
The input report name.
|
471
|
+
tableName : str
|
472
|
+
The input table name.
|
473
|
+
|
474
|
+
Returns
|
475
|
+
-------
|
476
|
+
list
|
477
|
+
the list of column names.
|
478
|
+
|
479
|
+
"""
|
480
|
+
sql = model.sqlFile().get()
|
481
|
+
query = "SELECT ColumnName FROM tabulardatawithstrings WHERE ReportName = '"+reportName+"' AND TableName = '"+tableName+"'"
|
482
|
+
columnNames = sql.execAndReturnVectorOfString(query).get()
|
483
|
+
return list(OrderedDict( (x,1) for x in columnNames ).keys()) #Making a unique list and keeping its order
|
484
|
+
|
485
|
+
@staticmethod
|
486
|
+
def DefaultConstructionSets(model):
|
487
|
+
"""
|
488
|
+
Returns the default construction sets in the input OSM model.
|
489
|
+
|
490
|
+
Parameters
|
491
|
+
----------
|
492
|
+
model : openstudio.openstudiomodelcore.Model
|
493
|
+
The input OSM model.
|
494
|
+
|
495
|
+
Returns
|
496
|
+
-------
|
497
|
+
list
|
498
|
+
The default construction sets.
|
499
|
+
|
500
|
+
"""
|
501
|
+
sets = model.getDefaultConstructionSets()
|
502
|
+
names = []
|
503
|
+
for aSet in sets:
|
504
|
+
names.append(aSet.name().get())
|
505
|
+
return [sets, names]
|
506
|
+
|
507
|
+
@staticmethod
|
508
|
+
def DefaultScheduleSets(model):
|
509
|
+
"""
|
510
|
+
Returns the default schedule sets found in the input OSM model.
|
511
|
+
|
512
|
+
Parameters
|
513
|
+
----------
|
514
|
+
model : openstudio.openstudiomodelcore.Model
|
515
|
+
The input OSM model.
|
516
|
+
|
517
|
+
Returns
|
518
|
+
-------
|
519
|
+
list
|
520
|
+
The list of default schedule sets.
|
521
|
+
|
522
|
+
"""
|
523
|
+
sets = model.getDefaultScheduleSets()
|
524
|
+
names = []
|
525
|
+
for aSet in sets:
|
526
|
+
names.append(aSet.name().get())
|
527
|
+
return [sets, names]
|
528
|
+
|
529
|
+
@staticmethod
|
530
|
+
def ExportToGBXML(model, path, overwrite=False):
|
531
|
+
"""
|
532
|
+
Exports the input OSM model to a GBXML file.
|
533
|
+
|
534
|
+
Parameters
|
535
|
+
----------
|
536
|
+
model : openstudio.openstudiomodelcore.Model
|
537
|
+
The input OSM model.
|
538
|
+
path : str
|
539
|
+
The path for saving the file.
|
540
|
+
overwrite : bool, optional
|
541
|
+
If set to True any file with the same name is over-written. The default is False.
|
542
|
+
|
543
|
+
Returns
|
544
|
+
-------
|
545
|
+
bool
|
546
|
+
True if the file is written successfully. False otherwise.
|
547
|
+
|
548
|
+
"""
|
549
|
+
from os.path import exists
|
550
|
+
try:
|
551
|
+
import openstudio
|
552
|
+
openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
|
553
|
+
except:
|
554
|
+
print("EnergyModel.ExportToGBXML - Information: Installing required openstudio library.")
|
555
|
+
try:
|
556
|
+
os.system("pip install openstudio")
|
557
|
+
except:
|
558
|
+
os.system("pip install openstudio --user")
|
559
|
+
try:
|
560
|
+
import openstudio
|
561
|
+
openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
|
562
|
+
print("EnergyModel.ExportToGBXML - Information: openstudio library installed correctly.")
|
563
|
+
except:
|
564
|
+
warnings.warn("EnergyModel.ExportToGBXML - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
|
565
|
+
return None
|
566
|
+
|
567
|
+
# Make sure the file extension is .xml
|
568
|
+
ext = path[len(path)-4:len(path)]
|
569
|
+
if ext.lower() != ".xml":
|
570
|
+
path = path+".xml"
|
571
|
+
|
572
|
+
if not overwrite and exists(path):
|
573
|
+
print("EnergyModel.ExportToGBXML - Error: a file already exists at the specified path and overwrite is set to False. Returning None.")
|
574
|
+
return None
|
575
|
+
|
576
|
+
return openstudio.gbxml.GbXMLForwardTranslator().modelToGbXML(model, openstudio.openstudioutilitiescore.toPath(path))
|
577
|
+
|
578
|
+
|
579
|
+
@staticmethod
|
580
|
+
def ExportToOSM(model, path, overwrite=False):
|
581
|
+
"""
|
582
|
+
Exports the input OSM model to an OSM file.
|
583
|
+
|
584
|
+
Parameters
|
585
|
+
----------
|
586
|
+
model : openstudio.openstudiomodelcore.Model
|
587
|
+
The input OSM model.
|
588
|
+
path : str
|
589
|
+
The path for saving the file.
|
590
|
+
overwrite : bool, optional
|
591
|
+
If set to True any file with the same name is over-written. The default is False.
|
592
|
+
|
593
|
+
Returns
|
594
|
+
-------
|
595
|
+
bool
|
596
|
+
True if the file is written successfully. False otherwise.
|
597
|
+
|
598
|
+
"""
|
599
|
+
from os.path import exists
|
600
|
+
try:
|
601
|
+
import openstudio
|
602
|
+
openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
|
603
|
+
except:
|
604
|
+
print("EnergyModel.ExportToOSM - Information: Installing required openstudio library.")
|
605
|
+
try:
|
606
|
+
os.system("pip install openstudio")
|
607
|
+
except:
|
608
|
+
os.system("pip install openstudio --user")
|
609
|
+
try:
|
610
|
+
import openstudio
|
611
|
+
openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
|
612
|
+
print("EnergyModel.ExportToOSM - Information: openstudio library installed correctly.")
|
613
|
+
except:
|
614
|
+
warnings.warn("EnergyModel.ExportToOSM - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
|
615
|
+
return None
|
616
|
+
|
617
|
+
# Make sure the file extension is .osm
|
618
|
+
ext = path[len(path)-4:len(path)]
|
619
|
+
if ext.lower() != ".osm":
|
620
|
+
path = path+".osm"
|
621
|
+
|
622
|
+
if not overwrite and exists(path):
|
623
|
+
print("EnergyModel.ExportToOSM - Error: a file already exists at the specified path and overwrite is set to False. Returning None.")
|
624
|
+
return None
|
625
|
+
osCondition = False
|
626
|
+
osPath = openstudio.openstudioutilitiescore.toPath(path)
|
627
|
+
osCondition = model.save(osPath, overwrite)
|
628
|
+
return osCondition
|
629
|
+
|
630
|
+
@staticmethod
|
631
|
+
def GBXMLString(model):
|
632
|
+
"""
|
633
|
+
Returns the GBXML string of the input OSM model.
|
634
|
+
|
635
|
+
Parameters
|
636
|
+
----------
|
637
|
+
model : openstudio.openstudiomodelcore.Model
|
638
|
+
The input OSM model.
|
639
|
+
|
640
|
+
Returns
|
641
|
+
-------
|
642
|
+
str
|
643
|
+
The gbxml string.
|
644
|
+
|
645
|
+
"""
|
646
|
+
try:
|
647
|
+
import openstudio
|
648
|
+
openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
|
649
|
+
except:
|
650
|
+
print("EnergyModel.GBXMLString - Information: Installing required openstudio library.")
|
651
|
+
try:
|
652
|
+
os.system("pip install openstudio")
|
653
|
+
except:
|
654
|
+
os.system("pip install openstudio --user")
|
655
|
+
try:
|
656
|
+
import openstudio
|
657
|
+
openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
|
658
|
+
print("EnergyModel.GBXMLString - Information: openstudio library installed correctly.")
|
659
|
+
except:
|
660
|
+
warnings.warn("EnergyModel.GBXMLString - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
|
661
|
+
return None
|
662
|
+
return openstudio.gbxml.GbXMLForwardTranslator().modelToGbXMLString(model)
|
663
|
+
|
664
|
+
@staticmethod
|
665
|
+
def Query(model,
|
666
|
+
reportName : str = "HVACSizingSummary",
|
667
|
+
reportForString : str = "Entire Facility",
|
668
|
+
tableName : str = "Zone Sensible Cooling",
|
669
|
+
columnName : str = "Calculated Design Load",
|
670
|
+
rowNames : list = [],
|
671
|
+
units : str = "W"):
|
672
|
+
"""
|
673
|
+
Queries the model for values.
|
674
|
+
|
675
|
+
Parameters
|
676
|
+
----------
|
677
|
+
model : openstudio.openstudiomodelcore.Model
|
678
|
+
The input OSM model.
|
679
|
+
reportName : str , optional
|
680
|
+
The input report name. The default is "HVACSizingSummary".
|
681
|
+
reportForString : str, optional
|
682
|
+
The input report for string. The default is "Entire Facility".
|
683
|
+
tableName : str , optional
|
684
|
+
The input table name. The default is "Zone Sensible Cooling".
|
685
|
+
columnName : str , optional
|
686
|
+
The input column name. The default is "Calculated Design Load".
|
687
|
+
rowNames : list , optional
|
688
|
+
The input list of row names. The default is [].
|
689
|
+
units : str , optional
|
690
|
+
The input units. The default is "W".
|
691
|
+
|
692
|
+
Returns
|
693
|
+
-------
|
694
|
+
list
|
695
|
+
The list of values.
|
696
|
+
|
697
|
+
"""
|
698
|
+
|
699
|
+
def doubleValueFromQuery(sqlFile, reportName, reportForString,
|
700
|
+
tableName, columnName, rowName,
|
701
|
+
units):
|
702
|
+
query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='" + reportName + "' AND ReportForString='" + reportForString + "' AND TableName = '" + tableName + "' AND RowName = '" + rowName + "' AND ColumnName= '" + columnName + "' AND Units='" + units + "'";
|
703
|
+
osOptionalDoubleValue = sqlFile.execAndReturnFirstDouble(query)
|
704
|
+
if (osOptionalDoubleValue.is_initialized()):
|
705
|
+
return osOptionalDoubleValue.get()
|
706
|
+
else:
|
707
|
+
return None
|
708
|
+
|
709
|
+
sqlFile = model.sqlFile().get()
|
710
|
+
returnValues = []
|
711
|
+
for rowName in rowNames:
|
712
|
+
returnValues.append(doubleValueFromQuery(sqlFile, reportName, reportForString, tableName, columnName, rowName, units))
|
713
|
+
return returnValues
|
714
|
+
|
715
|
+
@staticmethod
|
716
|
+
def ReportNames(model):
|
717
|
+
"""
|
718
|
+
Returns the report names found in the input OSM model.
|
719
|
+
|
720
|
+
Parameters
|
721
|
+
----------
|
722
|
+
model : openstudio.openstudiomodelcore.Model
|
723
|
+
The input OSM model.
|
724
|
+
|
725
|
+
Returns
|
726
|
+
-------
|
727
|
+
list
|
728
|
+
The list of report names found in the input OSM model.
|
729
|
+
|
730
|
+
"""
|
731
|
+
sql = model.sqlFile().get()
|
732
|
+
reportNames = sql.execAndReturnVectorOfString("SELECT ReportName FROM tabulardatawithstrings").get()
|
733
|
+
return list(OrderedDict( (x,1) for x in reportNames ).keys()) #Making a unique list and keeping its order
|
734
|
+
|
735
|
+
@staticmethod
|
736
|
+
def RowNames(model, reportName, tableName):
|
737
|
+
"""
|
738
|
+
Returns the list of row names given an OSM model, report name, and table name.
|
739
|
+
|
740
|
+
Parameters
|
741
|
+
----------
|
742
|
+
model : openstudio.openstudiomodelcore.Model
|
743
|
+
The input OSM model.
|
744
|
+
reportName : str
|
745
|
+
The input name of the report.
|
746
|
+
tableName : str
|
747
|
+
The input name of the table.
|
748
|
+
|
749
|
+
Returns
|
750
|
+
-------
|
751
|
+
list
|
752
|
+
The list of row names.
|
753
|
+
|
754
|
+
"""
|
755
|
+
sql = model.sqlFile().get()
|
756
|
+
query = "SELECT RowName FROM tabulardatawithstrings WHERE ReportName = '"+reportName+"' AND TableName = '"+tableName+"'"
|
757
|
+
columnNames = sql.execAndReturnVectorOfString(query).get()
|
758
|
+
return list(OrderedDict( (x,1) for x in columnNames ).keys()) #Making a unique list and keeping its order
|
759
|
+
|
760
|
+
@staticmethod
|
761
|
+
def Run(model, weatherFilePath: str = None, osBinaryPath : str = None, outputFolder : str = None, removeFiles : bool = False):
|
762
|
+
"""
|
763
|
+
Runs an energy simulation.
|
764
|
+
|
765
|
+
Parameters
|
766
|
+
----------
|
767
|
+
model : openstudio.openstudiomodelcore.Model
|
768
|
+
The input OSM model.
|
769
|
+
weatherFilePath : str
|
770
|
+
The path to the epw weather file.
|
771
|
+
osBinaryPath : str
|
772
|
+
The path to the openstudio binary.
|
773
|
+
outputFolder : str
|
774
|
+
The path to the output folder.
|
775
|
+
removeFiles : bool , optional
|
776
|
+
If set to True, the working files are removed at the end of the process. The default is False.
|
777
|
+
|
778
|
+
Returns
|
779
|
+
-------
|
780
|
+
model : openstudio.openstudiomodelcore.Model
|
781
|
+
The simulated OSM model.
|
782
|
+
|
783
|
+
"""
|
784
|
+
import os
|
785
|
+
import time
|
786
|
+
try:
|
787
|
+
import openstudio
|
788
|
+
openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
|
789
|
+
except:
|
790
|
+
print("EnergyModel.Run - Information: Installing required openstudio library.")
|
791
|
+
try:
|
792
|
+
os.system("pip install openstudio")
|
793
|
+
except:
|
794
|
+
os.system("pip install openstudio --user")
|
795
|
+
try:
|
796
|
+
import openstudio
|
797
|
+
openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
|
798
|
+
print("EnergyModel.Run - Information: openstudio library installed correctly.")
|
799
|
+
except:
|
800
|
+
warnings.warn("EnergyModel.Run - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
|
801
|
+
return None
|
802
|
+
def deleteOldFiles(path):
|
803
|
+
onemonth = (time.time()) - 30 * 86400
|
804
|
+
try:
|
805
|
+
for filename in os.listdir(path):
|
806
|
+
if os.path.getmtime(os.path.join(path, filename)) < onemonth:
|
807
|
+
if os.path.isfile(os.path.join(path, filename)):
|
808
|
+
os.remove(os.path.join(path, filename))
|
809
|
+
elif os.path.isdir(os.path.join(path, filename)):
|
810
|
+
shutil.rmtree((os.path.join(path, filename)))
|
811
|
+
except:
|
812
|
+
pass
|
813
|
+
if not weatherFilePath:
|
814
|
+
weatherFilePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "GBR_London.Gatwick.037760_IWEC.epw")
|
815
|
+
if removeFiles:
|
816
|
+
deleteOldFiles(outputFolder)
|
817
|
+
pbar = tqdm(desc='Running Simulation', total=100, leave=False)
|
818
|
+
utcnow = datetime.utcnow()
|
819
|
+
timestamp = utcnow.strftime("UTC-%Y-%m-%d-%H-%M-%S")
|
820
|
+
if not outputFolder:
|
821
|
+
home = os.path.expanduser('~')
|
822
|
+
outputFolder = os.path.join(home, "EnergyModels", timestamp)
|
823
|
+
else:
|
824
|
+
outputFolder = os.path.join(outputFolder, timestamp)
|
825
|
+
os.mkdir(outputFolder)
|
826
|
+
pbar.update(10)
|
827
|
+
osmPath = os.path.join(outputFolder, model.getBuilding().name().get() + ".osm")
|
828
|
+
model.save(openstudio.openstudioutilitiescore.toPath(osmPath), True)
|
829
|
+
oswPath = os.path.join(outputFolder, model.getBuilding().name().get() + ".osw")
|
830
|
+
pbar.update(20)
|
831
|
+
#print("oswPath = "+oswPath)
|
832
|
+
workflow = model.workflowJSON()
|
833
|
+
workflow.setSeedFile(openstudio.openstudioutilitiescore.toPath(osmPath))
|
834
|
+
pbar.update(30)
|
835
|
+
#print("Seed File Set")
|
836
|
+
workflow.setWeatherFile(openstudio.openstudioutilitiescore.toPath(weatherFilePath))
|
837
|
+
pbar.update(40)
|
838
|
+
#print("Weather File Set")
|
839
|
+
workflow.saveAs(openstudio.openstudioutilitiescore.toPath(oswPath))
|
840
|
+
pbar.update(50)
|
841
|
+
#print("OSW File Saved")
|
842
|
+
cmd = osBinaryPath+" run -w " + "\"" + oswPath + "\""
|
843
|
+
pbar.update(60)
|
844
|
+
os.system(cmd)
|
845
|
+
#print("Simulation DONE")
|
846
|
+
sqlPath = os.path.join(os.path.join(outputFolder,"run"), "eplusout.sql")
|
847
|
+
pbar.update(100)
|
848
|
+
#print("sqlPath = "+sqlPath)
|
849
|
+
osSqlFile = openstudio.SqlFile(openstudio.openstudioutilitiescore.toPath(sqlPath))
|
850
|
+
model.setSqlFile(osSqlFile)
|
851
|
+
pbar.close()
|
852
|
+
return model
|
853
|
+
|
854
|
+
@staticmethod
|
855
|
+
def SpaceDictionaries(model):
|
856
|
+
"""
|
857
|
+
Return the space dictionaries found in the input OSM model.
|
858
|
+
|
859
|
+
Parameters
|
860
|
+
----------
|
861
|
+
model : openstudio.openstudiomodelcore.Model
|
862
|
+
The input OSM model.
|
863
|
+
|
864
|
+
Returns
|
865
|
+
-------
|
866
|
+
dict
|
867
|
+
The dictionary of space types, names, and colors found in the input OSM model. The dictionary has the following keys:
|
868
|
+
- "types"
|
869
|
+
- "names"
|
870
|
+
- "colors"
|
871
|
+
|
872
|
+
"""
|
873
|
+
types = model.getSpaceTypes()
|
874
|
+
names = []
|
875
|
+
colors = []
|
876
|
+
for aType in types:
|
877
|
+
names.append(aType.name().get())
|
878
|
+
red = aType.renderingColor().get().renderingRedValue()
|
879
|
+
green = aType.renderingColor().get().renderingGreenValue()
|
880
|
+
blue = aType.renderingColor().get().renderingBlueValue()
|
881
|
+
colors.append([red,green,blue])
|
882
|
+
return {'types': types, 'names': names, 'colors': colors}
|
883
|
+
|
884
|
+
@staticmethod
|
885
|
+
def SpaceTypes(model):
|
886
|
+
"""
|
887
|
+
Return the space types found in the input OSM model.
|
888
|
+
|
889
|
+
Parameters
|
890
|
+
----------
|
891
|
+
model : openstudio.openstudiomodelcore.Model
|
892
|
+
The input OSM model.
|
893
|
+
|
894
|
+
Returns
|
895
|
+
-------
|
896
|
+
list
|
897
|
+
The list of space types
|
898
|
+
|
899
|
+
"""
|
900
|
+
return model.getSpaceTypes()
|
901
|
+
|
902
|
+
@staticmethod
|
903
|
+
def SpaceTypeNames(model):
|
904
|
+
"""
|
905
|
+
Return the space type names found in the input OSM model.
|
906
|
+
|
907
|
+
Parameters
|
908
|
+
----------
|
909
|
+
model : openstudio.openstudiomodelcore.Model
|
910
|
+
The input OSM model.
|
911
|
+
|
912
|
+
Returns
|
913
|
+
-------
|
914
|
+
list
|
915
|
+
The list of space type names
|
916
|
+
|
917
|
+
"""
|
918
|
+
types = model.getSpaceTypes()
|
919
|
+
names = []
|
920
|
+
colors = []
|
921
|
+
for aType in types:
|
922
|
+
names.append(aType.name().get())
|
923
|
+
return names
|
924
|
+
|
925
|
+
@staticmethod
|
926
|
+
def SpaceColors(model):
|
927
|
+
"""
|
928
|
+
Return the space colors found in the input OSM model.
|
929
|
+
|
930
|
+
Parameters
|
931
|
+
----------
|
932
|
+
model : openstudio.openstudiomodelcore.Model
|
933
|
+
The input OSM model.
|
934
|
+
|
935
|
+
Returns
|
936
|
+
-------
|
937
|
+
list
|
938
|
+
The list of space colors. Each item is a three-item list representing the red, green, and blue values of the color.
|
939
|
+
|
940
|
+
"""
|
941
|
+
types = model.getSpaceTypes()
|
942
|
+
colors = []
|
943
|
+
for aType in types:
|
944
|
+
red = aType.renderingColor().get().renderingRedValue()
|
945
|
+
green = aType.renderingColor().get().renderingGreenValue()
|
946
|
+
blue = aType.renderingColor().get().renderingBlueValue()
|
947
|
+
colors.append([red,green,blue])
|
948
|
+
return colors
|
949
|
+
|
950
|
+
@staticmethod
|
951
|
+
def SqlFile(model):
|
952
|
+
"""
|
953
|
+
Returns the SQL file found in the input OSM model.
|
954
|
+
|
955
|
+
Parameters
|
956
|
+
----------
|
957
|
+
model : openstudio.openstudiomodelcore.Model
|
958
|
+
The input OSM model.
|
959
|
+
|
960
|
+
Returns
|
961
|
+
-------
|
962
|
+
SQL file
|
963
|
+
The SQL file found in the input OSM model.
|
964
|
+
|
965
|
+
"""
|
966
|
+
return model.sqlFile().get()
|
967
|
+
|
968
|
+
@staticmethod
|
969
|
+
def TableNames(model, reportName):
|
970
|
+
"""
|
971
|
+
Returns the table names found in the input OSM model and report name.
|
972
|
+
|
973
|
+
Parameters
|
974
|
+
----------
|
975
|
+
model : openstudio.openstudiomodelcore.Model
|
976
|
+
The input OSM model.
|
977
|
+
reportName : str
|
978
|
+
The input report name.
|
979
|
+
|
980
|
+
Returns
|
981
|
+
-------
|
982
|
+
list
|
983
|
+
The list of table names found in the input OSM model and report name.
|
984
|
+
|
985
|
+
"""
|
986
|
+
sql = model.sqlFile().get()
|
987
|
+
tableNames = sql.execAndReturnVectorOfString("SELECT TableName FROM tabulardatawithstrings WHERE ReportName='"+reportName+"'").get()
|
988
|
+
return list(OrderedDict( (x,1) for x in tableNames ).keys()) #Making a unique list and keeping its order
|
989
|
+
|
990
|
+
@staticmethod
|
991
|
+
def Topologies(model, tolerance=0.0001):
|
992
|
+
"""
|
993
|
+
Parameters
|
994
|
+
----------
|
995
|
+
model : openstudio.openstudiomodelcore.Model
|
996
|
+
The input OSM model.
|
997
|
+
tolerance : float , optional
|
998
|
+
The desired tolerance. The default is 0.0001.
|
999
|
+
|
1000
|
+
Returns
|
1001
|
+
-------
|
1002
|
+
dict
|
1003
|
+
The dictionary of topologies found in the input OSM model. The keys of the dictionary are:
|
1004
|
+
- "cells"
|
1005
|
+
- "apertures"
|
1006
|
+
- "shadingFaces"
|
1007
|
+
|
1008
|
+
"""
|
1009
|
+
from topologicpy.Edge import Edge
|
1010
|
+
from topologicpy.Wire import Wire
|
1011
|
+
from topologicpy.Face import Face
|
1012
|
+
from topologicpy.Shell import Shell
|
1013
|
+
from topologicpy.Cell import Cell
|
1014
|
+
from topologicpy.Cluster import Cluster
|
1015
|
+
from topologicpy.Dictionary import Dictionary
|
1016
|
+
from topologicpy.Topology import Topology
|
1017
|
+
|
1018
|
+
def surfaceToFace(surface):
|
1019
|
+
surfaceEdges = []
|
1020
|
+
surfaceVertices = surface.vertices()
|
1021
|
+
for i in range(len(surfaceVertices)-1):
|
1022
|
+
sv = topologic.Vertex.ByCoordinates(surfaceVertices[i].x(), surfaceVertices[i].y(), surfaceVertices[i].z())
|
1023
|
+
ev = topologic.Vertex.ByCoordinates(surfaceVertices[i+1].x(), surfaceVertices[i+1].y(), surfaceVertices[i+1].z())
|
1024
|
+
edge = Edge.ByStartVertexEndVertex(sv, ev, tolerance=tolerance, silent=False)
|
1025
|
+
if not edge:
|
1026
|
+
continue
|
1027
|
+
surfaceEdges.append(edge)
|
1028
|
+
sv = topologic.Vertex.ByCoordinates(surfaceVertices[len(surfaceVertices)-1].x(), surfaceVertices[len(surfaceVertices)-1].y(), surfaceVertices[len(surfaceVertices)-1].z())
|
1029
|
+
ev = topologic.Vertex.ByCoordinates(surfaceVertices[0].x(), surfaceVertices[0].y(), surfaceVertices[0].z())
|
1030
|
+
edge = Edge.ByStartVertexEndVertex(sv, ev, tolerance=tolerance, silent=False)
|
1031
|
+
surfaceEdges.append(edge)
|
1032
|
+
surfaceWire = Wire.ByEdges(surfaceEdges, tolerance=tolerance)
|
1033
|
+
internalBoundaries = []
|
1034
|
+
surfaceFace = Face.ByWires(surfaceWire, internalBoundaries, tolerance=tolerance)
|
1035
|
+
return surfaceFace
|
1036
|
+
|
1037
|
+
def addApertures(face, apertures):
|
1038
|
+
usedFaces = []
|
1039
|
+
for aperture in apertures:
|
1040
|
+
cen = aperture.CenterOfMass()
|
1041
|
+
try:
|
1042
|
+
params = face.ParametersAtVertex(cen)
|
1043
|
+
u = params[0]
|
1044
|
+
v = params[1]
|
1045
|
+
w = 0.5
|
1046
|
+
except:
|
1047
|
+
u = 0.5
|
1048
|
+
v = 0.5
|
1049
|
+
w = 0.5
|
1050
|
+
context = topologic.Context.ByTopologyParameters(face, u, v, w)
|
1051
|
+
_ = topologic.Aperture.ByTopologyContext(aperture, context)
|
1052
|
+
return face
|
1053
|
+
spaces = list(model.getSpaces())
|
1054
|
+
|
1055
|
+
vertexIndex = 0
|
1056
|
+
cells = []
|
1057
|
+
apertures = []
|
1058
|
+
shadingFaces = []
|
1059
|
+
shadingSurfaces = list(model.getShadingSurfaces())
|
1060
|
+
|
1061
|
+
for aShadingSurface in shadingSurfaces:
|
1062
|
+
shadingFace = surfaceToFace(aShadingSurface)
|
1063
|
+
if aShadingSurface.shadingSurfaceGroup().is_initialized():
|
1064
|
+
shadingGroup = aShadingSurface.shadingSurfaceGroup().get()
|
1065
|
+
if shadingGroup.space().is_initialized():
|
1066
|
+
space = shadingGroup.space().get()
|
1067
|
+
osTransformation = space.transformation()
|
1068
|
+
osTranslation = osTransformation.translation()
|
1069
|
+
osMatrix = osTransformation.rotationMatrix()
|
1070
|
+
rotation11 = osMatrix[0, 0]
|
1071
|
+
rotation12 = osMatrix[0, 1]
|
1072
|
+
rotation13 = osMatrix[0, 2]
|
1073
|
+
rotation21 = osMatrix[1, 0]
|
1074
|
+
rotation22 = osMatrix[1, 1]
|
1075
|
+
rotation23 = osMatrix[1, 2]
|
1076
|
+
rotation31 = osMatrix[2, 0]
|
1077
|
+
rotation32 = osMatrix[2, 1]
|
1078
|
+
rotation33 = osMatrix[2, 2]
|
1079
|
+
shadingFace = topologic.TopologyUtility.Transform(shadingFace, osTranslation.x(), osTranslation.y(), osTranslation.z(), rotation11, rotation12, rotation13, rotation21, rotation22, rotation23, rotation31, rotation32, rotation33)
|
1080
|
+
shadingFaces.append(shadingFace)
|
1081
|
+
|
1082
|
+
for count, aSpace in enumerate(spaces):
|
1083
|
+
osTransformation = aSpace.transformation()
|
1084
|
+
osTranslation = osTransformation.translation()
|
1085
|
+
osMatrix = osTransformation.rotationMatrix()
|
1086
|
+
rotation11 = osMatrix[0, 0]
|
1087
|
+
rotation12 = osMatrix[0, 1]
|
1088
|
+
rotation13 = osMatrix[0, 2]
|
1089
|
+
rotation21 = osMatrix[1, 0]
|
1090
|
+
rotation22 = osMatrix[1, 1]
|
1091
|
+
rotation23 = osMatrix[1, 2]
|
1092
|
+
rotation31 = osMatrix[2, 0]
|
1093
|
+
rotation32 = osMatrix[2, 1]
|
1094
|
+
rotation33 = osMatrix[2, 2]
|
1095
|
+
spaceFaces = []
|
1096
|
+
surfaces = aSpace.surfaces()
|
1097
|
+
|
1098
|
+
for aSurface in surfaces:
|
1099
|
+
aFace = surfaceToFace(aSurface)
|
1100
|
+
aFace = topologic.TopologyUtility.Transform(aFace, osTranslation.x(), osTranslation.y(), osTranslation.z(), rotation11, rotation12, rotation13, rotation21, rotation22, rotation23, rotation31, rotation32, rotation33)
|
1101
|
+
#aFace.__class__ = topologic.Face
|
1102
|
+
subSurfaces = aSurface.subSurfaces()
|
1103
|
+
for aSubSurface in subSurfaces:
|
1104
|
+
aperture = surfaceToFace(aSubSurface)
|
1105
|
+
aperture = topologic.TopologyUtility.Transform(aperture, osTranslation.x(), osTranslation.y(), osTranslation.z(), rotation11, rotation12, rotation13, rotation21, rotation22, rotation23, rotation31, rotation32, rotation33)
|
1106
|
+
# aperture.__class__ = topologic.Face
|
1107
|
+
apertures.append(aperture)
|
1108
|
+
addApertures(aFace, apertures)
|
1109
|
+
spaceFaces.append(aFace)
|
1110
|
+
spaceFaces = [x for x in spaceFaces if isinstance(x, topologic.Face)]
|
1111
|
+
spaceCell = Cell.ByFaces(spaceFaces, tolerance=tolerance)
|
1112
|
+
if not spaceCell:
|
1113
|
+
spaceCell = Shell.ByFaces(spaceFaces, tolerance=tolerance)
|
1114
|
+
if not isinstance(spaceCell, topologic.Cell):
|
1115
|
+
spaceCell = Cluster.ByTopologies(spaceFaces)
|
1116
|
+
if isinstance(spaceCell, topologic.Topology): #debugging
|
1117
|
+
# Set Dictionary for Cell
|
1118
|
+
keys = []
|
1119
|
+
values = []
|
1120
|
+
|
1121
|
+
keys.append("TOPOLOGIC_id")
|
1122
|
+
keys.append("TOPOLOGIC_name")
|
1123
|
+
keys.append("TOPOLOGIC_type")
|
1124
|
+
keys.append("TOPOLOGIC_color")
|
1125
|
+
spaceID = str(aSpace.handle()).replace('{','').replace('}','')
|
1126
|
+
values.append(spaceID)
|
1127
|
+
values.append(aSpace.name().get())
|
1128
|
+
spaceTypeName = "Unknown"
|
1129
|
+
red = 255
|
1130
|
+
green = 255
|
1131
|
+
blue = 255
|
1132
|
+
|
1133
|
+
if (aSpace.spaceType().is_initialized()):
|
1134
|
+
if(aSpace.spaceType().get().name().is_initialized()):
|
1135
|
+
spaceTypeName = aSpace.spaceType().get().name().get()
|
1136
|
+
if(aSpace.spaceType().get().renderingColor().is_initialized()):
|
1137
|
+
red = aSpace.spaceType().get().renderingColor().get().renderingRedValue()
|
1138
|
+
green = aSpace.spaceType().get().renderingColor().get().renderingGreenValue()
|
1139
|
+
blue = aSpace.spaceType().get().renderingColor().get().renderingBlueValue()
|
1140
|
+
values.append(spaceTypeName)
|
1141
|
+
values.append([red, green, blue])
|
1142
|
+
d = Dictionary.ByKeysValues(keys, values)
|
1143
|
+
spaceCell = Topology.SetDictionary(spaceCell, d)
|
1144
|
+
cells.append(spaceCell)
|
1145
|
+
return {'cells':cells, 'apertures':apertures, 'shadingFaces': shadingFaces}
|
1146
|
+
|
1147
|
+
@staticmethod
|
1148
|
+
def Units(model, reportName, tableName, columnName):
|
1149
|
+
"""
|
1150
|
+
Parameters
|
1151
|
+
----------
|
1152
|
+
model : openstudio.openstudiomodelcore.Model
|
1153
|
+
The input OSM model.
|
1154
|
+
reportName : str
|
1155
|
+
The input report name.
|
1156
|
+
tableName : str
|
1157
|
+
The input table name.
|
1158
|
+
columnName : str
|
1159
|
+
The input column name.
|
1160
|
+
|
1161
|
+
Returns
|
1162
|
+
-------
|
1163
|
+
str
|
1164
|
+
The units string found in the input OSM model, report name, table name, and column name.
|
1165
|
+
|
1166
|
+
"""
|
1167
|
+
# model = item[0]
|
1168
|
+
# reportName = item[1]
|
1169
|
+
# tableName = item[2]
|
1170
|
+
# columnName = item[3]
|
1171
|
+
sql = model.sqlFile().get()
|
1172
|
+
query = "SELECT Units FROM tabulardatawithstrings WHERE ReportName = '"+reportName+"' AND TableName = '"+tableName+"' AND ColumnName = '"+columnName+"'"
|
1173
|
+
units = sql.execAndReturnFirstString(query)
|
1174
|
+
if (units.is_initialized()):
|
1175
|
+
units = units.get()
|
1176
|
+
else:
|
1177
|
+
print("EnergyModel.Units - Error: Could not retrieve the units. Returning None.")
|
1178
|
+
return None
|
1179
|
+
return units
|
1180
|
+
|