topologicpy 0.5.9__py3-none-any.whl → 6.0.0__py3-none-any.whl

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