A function within CityBEM_workflow is designed to handle user inputs related to the SRA model. If preferred, CityBEM can also read radiation data generated by SRA. Additionally, it's important to note that while the hub uses surface IDs, CityBEM operates on a triangular basis. Therefore, a file named buildingSurfaceId is generated, which maps each triangle to its corresponding surface by assigning surface numbers to each triangle in sequence. This enables CityBEM to accurately link each triangle to its associated surface.

This commit is contained in:
Saeed Rayegan 2024-11-08 11:35:49 -05:00
parent 308c3a3e60
commit cc7e9aa911
5 changed files with 69 additions and 42 deletions

View File

@ -69,38 +69,47 @@ class Stl:
stl_name = f'{self._city.name}.stl'
stl_file_path = (Path(self._path).resolve() / stl_name).resolve()
with open(stl_file_path, 'w', encoding='utf-8') as stl:
with open(stl_file_path.parent/'buildingSurfaceId.txt', 'w') as textfile:
for building in self._city.buildings:
stl.write(f"solid building{building.name}\n")
# write the building id in the buildingSurfaceId.txt file
textfile.write(building.name + " ")
for surface in building.surfaces:
surfaceID=surface.id
vertices = []
normal = self._to_normal_vertex_stl(surface.perimeter_polygon.coordinates) #the normal vector should be calculated for every surface
normal = self._to_normal_vertex_stl(
surface.perimeter_polygon.coordinates) # the normal vector should be calculated for every surface
for coordinate in surface.perimeter_polygon.coordinates:
vertex = self._to_vertex_stl(coordinate)
if vertex not in vertices:
vertices.append(vertex)
vertices = np.array(vertices)
#After collecting the unique vertices of a surface, there is a need to identify if it is located on the roof, floor, or side walls
roofStatus=1 #multiplication of the height of all vertices in a surface
heightSum=0 #summation of the height of all vertices in a surface
# After collecting the unique vertices of a surface, there is a need to identify if it is located on the roof, floor, or side walls
roofStatus = 1 # multiplication of the height of all vertices in a surface
heightSum = 0 # summation of the height of all vertices in a surface
for vertex in vertices:
roofStatus *= vertex[1]
heightSum += vertex[1]
if roofStatus>0:
#this surface is the roof (first and third elements of vertices should be passed to the triangulation function)
triangles=self._triangulate_stl(vertices[:, [0, 2]], vertices[0][1])
elif roofStatus==0 and heightSum==0:
if roofStatus > 0:
# this surface is the roof (first and third elements of vertices should be passed to the triangulation function)
triangles = self._triangulate_stl(vertices[:, [0, 2]], vertices[0][1])
elif roofStatus == 0 and heightSum == 0:
# this surface is the floor
triangles=self._triangulate_stl(vertices[:, [0, 2]], vertices[0][1])
elif roofStatus==0 and heightSum>0:
triangles = self._triangulate_stl(vertices[:, [0, 2]], vertices[0][1])
elif roofStatus == 0 and heightSum > 0:
# this surface is a vertical wall (no need for triangulation as it can be done manually)
triangles = [[vertices[0],vertices[1],vertices[2]], [vertices[2], vertices[3], vertices[0]]]
triangles = [[vertices[0], vertices[1], vertices[2]], [vertices[2], vertices[3], vertices[0]]]
# write the facets (triangles) in the stl file
for triangle in triangles:
stl.write(f"facet normal {normal[0]} {normal[2]} {normal[1]}\n") #following the idea that y axis is the height
#write the id of the surface (the triangles that belong to one surface should have similar id). Note that surface id is number like 2,3,4,
textfile.write(surfaceID + " ")
stl.write(
f"facet normal {normal[0]} {normal[2]} {normal[1]}\n") # following the idea that y axis is the height
stl.write(" outer loop\n")
for vertex in triangle:
stl.write(f" vertex {vertex[0]} {vertex[1]} {vertex[2]}\n")
stl.write(" endloop\n")
stl.write("endfacet\n")
textfile.write("\n") # End the line after all surface IDs are written for a given building
stl.write(f"endsolid building{building.name}\n")

14
main.py
View File

@ -17,7 +17,10 @@ import subprocess
# Specify the GeoJSON file path
# geojson_file = process_geojson(a=-73.5681295982132, b=45.49218262677643, c=-73.5681295982132, d=45.51218262677643,
# e=-73.5881295982132, f=45.49218262677643,g=-73.5881295982132, h=45.51218262677643)
geojson_file = process_geojson(x=-73.5681295982132, y=45.49218262677643, diff=0.0001)
geojson_file = process_geojson(x=-73.57711768607801, y=45.495098237967, diff=0.0001)
#-73.63805499801724; 45.415970792153075
#x=-73.5681295982132, y=45.49218262677643
file_path = (Path(__file__).parent / 'input_files' / 'output_buildings.geojson')
# Specify the output path for the PDF file
@ -35,13 +38,12 @@ city = GeometryFactory('geojson',
ConstructionFactory('nrcan', city).enrich()
UsageFactory('nrcan', city).enrich()
WeatherFactory('epw', city).enrich()
ExportsFactory('stl', city, output_path/'CityBEM_input_output').export()
#ExportsFactory('sra', city, tmp_folder).export()
#sra_path = (tmp_folder / f'{city.name}_sra.xml').resolve()
ExportsFactory('sra', city, tmp_folder).export()
sra_path = (tmp_folder / f'{city.name}_sra.xml').resolve()
#subprocess.run(['sra', str(sra_path)])
#ResultFactory('sra', city, tmp_folder).enrich()
CityBEM_workflow(city) #run the city using a fast City-Building Energy Model, CityBEM
CityBEM_workflow(city,SRA_radiation_status=True, SRA_path=tmp_folder) #run the city using a fast City-Building Energy Model, CityBEM
#energy_plus_workflow(city)
print ("test done")

View File

@ -0,0 +1,3 @@
cityName: Montreal
SRA_radiation_status: True
SRA_path: C:\Users\User\PycharmProjects\CityBEM-CityLayers-SaeedRayegan\tmp

View File

@ -0,0 +1 @@
177628 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13

View File

@ -11,12 +11,15 @@ from hub.imports.weather.epw_weather_parameters import EpwWeatherParameters
sys.path.append('./')
def CityBEM_workflow(city):
def CityBEM_workflow(city, SRA_radiation_status=False, SRA_path=None):
"""
Main function to run the CityBEM under the CityLayer's hub.
:Note: City object contains necessary attributes for the CityBEM workflow.
The first final version is created at 10/07/2024
Note:CityBEM includes a built-in function to calculate solar radiation on building facades.
However, SRA can account for shading effects from nearby buildings, both direct and diffuse. If the user opts to use SRA, CityBEM will read the radiation data generated by SRA.
"""
#general output path for the CityLayer's hub
out_path = Path(__file__).parent.parent / 'out_files'
@ -31,7 +34,8 @@ def CityBEM_workflow(city):
#load the geojson file (for now this is necessary, later, it should be removed to extract building usage type code, center lat and lon). Later, these should be added to the building class
with open(file_path, 'r') as f:
geojson_data = json.load(f)
#city name
cityName = city.name
#call functions to provide inputs for CityBEM and finally run CityBEM
export_geometry(city, CityBEM_path)
export_building_info(city, CityBEM_path,geojson_data)
@ -39,6 +43,7 @@ def CityBEM_workflow(city):
export_comprehensive_building_data(city, CityBEM_path)
export_indoor_temperature_setpoint_data(city, CityBEM_path)
export_internal_heat_gain_data(city, CityBEM_path)
write_user_setting_for_SRA_radiation_data(CityBEM_path,cityName, SRA_radiation_status, SRA_path)
run_CityBEM(CityBEM_path)
def export_geometry(city, CityBEM_path):
"""
@ -371,6 +376,13 @@ def export_internal_heat_gain_data(city, CityBEM_path):
# write the values to the text file for this building
textfile.write(values_str + '\n')
print("Internal heat gains for every building is successfully exported into a text file named Input_internal_heat_gain_CityLayer.txt")
def write_user_setting_for_SRA_radiation_data(CityBEM_path,cityName, SRA_radiation_status, SRA_path):
# Open the file in write mode and write the settings
with open(CityBEM_path / 'SRA_settings.txt', 'w') as textfile:
textfile.write(f"cityName: {cityName}\n")
textfile.write(f"SRA_radiation_status: {SRA_radiation_status}\n")
textfile.write(f"SRA_path: {SRA_path}\n")
def run_CityBEM(CityBEM_path):
"""
Run the CityBEM executable after all inputs are processed.