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