Compare commits

...

9 Commits

Author SHA1 Message Date
saeed-rayegan
03ea40cbe3 The fist version of CityBEM workflow is now done. Users can use CityBEM_workflow by passing the city object into it. The command line to call CityBEM under the CityLayer is CityBEM_workflow(city). 2024-07-10 16:50:46 -04:00
saeed-rayegan
86f0dbfe70 The fist version of CityBEM workflow is now done. Users can use CityBEM_workflow by passing the city object into it. The command line to call CityBEM under the CityLayer is CityBEM_workflow(city). 2024-07-10 16:41:32 -04:00
saeed-rayegan
b560287a54 The internal heat gain data for every building is now exported into a text file named Input_internal_heat_gain_CityLayer.txt to be read from CityBEM. 2024-07-08 14:19:14 -04:00
saeed-rayegan
d40e013e74 Merge remote-tracking branch 'origin/saeed_rayegan_test' into saeed_rayegan_test
# Conflicts:
#	scripts/CityBEM_run.py
2024-07-08 14:03:48 -04:00
saeed-rayegan
2d55b710b2 The internal heat gain data for every building is now exported into a text file named Input_internal_heat_gain_CityLayer.txt to be read from CityBEM. 2024-07-08 14:01:26 -04:00
saeed-rayegan
28b4e84b80 The individual building data are extracted from the hub and written in a text file to be used by CityBEM, replacing CityBEM input archetypes. 2024-07-02 14:43:33 -04:00
saeed-rayegan
ec6affa3ad Merge remote-tracking branch 'origin/saeed_rayegan_test' into saeed_rayegan_test
# Conflicts:
#	main.py
#	scripts/CityBEM_run.py
2024-07-02 10:42:54 -04:00
saeed-rayegan
265bb1e759 CityBEM workflow operational 2024-07-02 10:41:43 -04:00
saeed-rayegan
7ee9f03678 CityBEM workflow operational 2024-06-25 15:31:29 -04:00
6 changed files with 562 additions and 20 deletions

View File

@ -2,7 +2,7 @@
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="hub" jdkType="Python SDK" />
<orderEntry type="jdk" jdkName="C:\Users\sr283\miniconda3\envs\Hub" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -1,4 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="hub" project-jdk-type="Python SDK" />
<component name="Black">
<option name="sdkName" value="C:\Users\sr283\miniconda3\envs\Hub" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="C:\Users\sr283\miniconda3\envs\Hub" project-jdk-type="Python SDK" />
</project>

View File

@ -1,16 +1,106 @@
"""
export a city into Stl format
export a city into STL format. (Each building is a solid, suitable for RC models such as CityBEM)
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
Copyright © 2024 Concordia CERC group
Project Coder Saeed Rayegan sr283100@gmail.com
"""
from pathlib import Path
import numpy as np
from scipy.spatial import Delaunay
from hub.exports.formats.triangular import Triangular
class Stl(Triangular):
class Stl:
"""
Export to STL
Export to stl format
"""
def __init__(self, city, path):
super().__init__(city, path, 'stl', write_mode='wb')
self._city = city
self._path = path
self._export()
def _triangulate_stl(self, points_2d, height):
#This function requires a set of 2D points for triangulation
# Assuming vertices is a NumPy array
tri = Delaunay(points_2d)
triangles2D = points_2d[tri.simplices]
triangles3D = []
# Iterate through each triangle in triangles2D
for triangle in triangles2D:
# Extract the existing x and y coordinates
x1, y1 = triangle[0]
x2, y2 = triangle[1]
x3, y3 = triangle[2]
# Create a 3D point with the specified height
point3D=[[x1, height, y1],[x2, height, y2],[x3, height, y3]]
# Append the 3D points to the triangle list
triangles3D.append(point3D)
return triangles3D
def _ground(self, coordinate):
x = coordinate[0] - self._city.lower_corner[0]
y = coordinate[1] - self._city.lower_corner[1]
z = coordinate[2] - self._city.lower_corner[2]
return x, y, z
def _to_vertex_stl(self, coordinate):
x, y, z = self._ground(coordinate)
return [x, z, -y] # Return as a list # to match opengl expectations (check it later)
def _to_normal_vertex_stl(self, coordinates):
ground_vertex = []
for coordinate in coordinates:
x, y, z = self._ground(coordinate)
ground_vertex.append(np.array([x, y, z]))
# recalculate the normal to get grounded values
edge_1 = ground_vertex[1] - ground_vertex[0]
edge_2 = ground_vertex[2] - ground_vertex[0]
normal = np.cross(edge_1, edge_2)
normal = normal / np.linalg.norm(normal)
# Convert normal to list for easier handling in the write operation
return normal.tolist()
def _export(self):
if self._city.name is None:
self._city.name = 'unknown_city'
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:
for building in self._city.buildings:
stl.write(f"solid building{building.name}\n")
for surface in building.surfaces:
vertices = []
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
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:
# this surface is the floor
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]]]
# 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
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")
stl.write(f"endsolid building{building.name}\n")

37
main.py
View File

@ -1,6 +1,35 @@
from scripts.geojson_creator import process_geojson
from pathlib import Path
from scripts.ep_run_enrich import energy_plus_workflow
from scripts.CityBEM_run import CityBEM_workflow
from hub.imports.geometry_factory import GeometryFactory
from hub.helpers.dictionaries import Dictionaries
from hub.imports.construction_factory import ConstructionFactory
from hub.imports.usage_factory import UsageFactory
from hub.imports.weather_factory import WeatherFactory
from hub.imports.results_factory import ResultFactory
import hub.helpers.constants as cte
from hub.exports.exports_factory import ExportsFactory
# 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)
file_path = (Path(__file__).parent / 'input_files' / 'output_buildings.geojson')
# Specify the output path for the PDF file
output_path = (Path(__file__).parent / 'out_files').resolve()
# Create city object from GeoJSON file
city = GeometryFactory('geojson',
path=file_path,
height_field='height',
year_of_construction_field='year_of_construction',
function_field='function',
function_to_hub=Dictionaries().montreal_function_to_hub_function).city
# Enrich city data
ConstructionFactory('nrcan', city).enrich()
UsageFactory('nrcan', city).enrich()
WeatherFactory('epw', city).enrich()
CityBEM_workflow(city) #run the city using a fast City-Building Energy Model, CityBEM
energy_plus_workflow(city)
print ("test done")

391
scripts/CityBEM_run.py Normal file
View File

@ -0,0 +1,391 @@
import pandas as pd
import sys
import csv
import json
from shapely.geometry import Polygon
from pathlib import Path
import subprocess
from hub.exports.exports_factory import ExportsFactory
from hub.imports.weather.epw_weather_parameters import EpwWeatherParameters
sys.path.append('./')
def CityBEM_workflow(city):
"""
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
"""
#general output path for the CityLayer's hub
out_path = Path(__file__).parent.parent / 'out_files'
#create a directory for running CityBEM under the main out_path
CityBEM_path = out_path / 'CityBEM_input_output'
if not CityBEM_path.exists():
CityBEM_path.mkdir(parents=True, exist_ok=True)
# Define the path to the GeoJSON file
file_path = Path(__file__).parent.parent / 'input_files' / 'output_buildings.geojson'
#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)
#call functions to provide inputs for CityBEM and finally run CityBEM
export_geometry(city, CityBEM_path)
export_building_info(city, CityBEM_path,geojson_data)
export_weather_data(city, CityBEM_path)
export_comprehensive_building_data(city, CityBEM_path)
export_indoor_temperature_setpoint_data(city, CityBEM_path)
export_internal_heat_gain_data(city, CityBEM_path)
run_CityBEM(CityBEM_path)
def export_geometry(city, CityBEM_path):
"""
Export the STL geometry from the hub and rename the exported geometry to a proper name for CityBEM.
:param city: City object containing necessary attributes for the workflow.
:param CityBEM_path: Path where CityBEM input and output files are stored.
"""
ExportsFactory('stl', city, CityBEM_path).export()
hubGeometryName = city.name + '.stl'
#delete old files related to geometry if they exist
CityBEMGeometryPath1 = CityBEM_path / 'Input_City_scale_geometry_CityBEM.stl'
CityBEMGeometryPath2 = CityBEM_path / 'Input_City_scale_geometry_CityBEM.txt' #delete this file to ensure CityBEM generates a new one based on the new input geometry
if CityBEMGeometryPath1.exists():
CityBEMGeometryPath1.unlink()
if CityBEMGeometryPath2.exists():
CityBEMGeometryPath2.unlink()
(CityBEM_path / hubGeometryName).rename(CityBEM_path / CityBEMGeometryPath1)
print("CityBEM input geometry file named Input_City_scale_geometry_CityBEM.stl file has been created successfully")
def get_building_info(geojson_data, building_id):
for feature in geojson_data['features']:
if feature['id'] == building_id:
function_code = feature['properties']['function']
coordinates = feature['geometry']['coordinates'][0]
#calculate the center of the polygon
polygon = Polygon(coordinates)
center = polygon.centroid
return function_code, (center.x, center.y)
return None, None
def export_building_info(city, CityBEM_path, geojson_file):
"""
Generate the input building information file for CityBEM.
:param city: City object containing necessary attributes for the workflow.
:param CityBEM_path: Path where CityBEM input and output files are stored.
"""
buildingInfo_path = CityBEM_path / 'Input_City_scale_building_info.txt'
with open(buildingInfo_path, "w", newline="") as textfile: #here, "w" refers to write mode. This deletes everything inside the file if the file exists.
writer = csv.writer(textfile, delimiter="\t") #use tab delimiter for all CityBEM inputs
writer.writerow(["building_stl", "building_osm", "constructionYear", "codeUsageType", "centerLongitude", "centerLatitude"]) # Header
for building in city.buildings:
function_code, center_coordinates = get_building_info(geojson_file, int (building.name))
row = ["b" + building.name, "999999", str(building.year_of_construction), str(function_code), str(center_coordinates[0]), str(center_coordinates[1])]
#note: based on CityBEM legacy, using a number like "999999" means that the data is not known/available.
writer.writerow(row)
print("CityBEM input file named Input_City_scale_building_info.txt file has been created successfully")
def export_weather_data(city, CityBEM_path):
"""
Generate the input weather data file compatible to CityBEM.
:param city: City object containing necessary attributes for the workflow.
:param CityBEM_path: Path where CityBEM input and output files are stored.
"""
weatherParameters = EpwWeatherParameters(city)._weather_values
weatherParameters = pd.DataFrame(weatherParameters) #transfer the weather data to a DataFrame
with open(CityBEM_path / 'Input_weatherdata.txt', 'w') as textfile:
# write the header information
textfile.write('Weather_timestep(s)\t3600\n')
textfile.write('Weather_columns\t11\n') #so far, 11 columns can be extracted from the epw weather data.
textfile.write('Date\tTime\tGHI\tDNI\tDHI\tTa\tTD\tTG\tRH\tWS\tWD\n')
for _, row in weatherParameters.iterrows():
#form the Date and Time
Date = f"{int(row['year'])}-{int(row['month']):02d}-{int(row['day']):02d}"
Time = f"{int(row['hour']):02d}:{int(row['minute']):02d}"
#retrieve the weather data
GHI = row['global_horizontal_radiation_wh_m2']
DNI = row['direct_normal_radiation_wh_m2']
DHI = row['diffuse_horizontal_radiation_wh_m2']
Ta = row['dry_bulb_temperature_c']
TD = row['dew_point_temperature_c']
TG = row['dry_bulb_temperature_c']
RH = row['relative_humidity_perc']
WS = row['wind_speed_m_s']
WD = row['wind_direction_deg']
#write the data in tab-separated format into the text file
textfile.write(f"{Date}\t{Time}\t{GHI}\t{DNI}\t{DHI}\t{Ta}\t{TD}\t{TG}\t{RH}\t{WS}\t{WD}\n")
print("CityBEM input file named Input_weatherdata.txt file has been created successfully")
def export_comprehensive_building_data(city, CityBEM_path):
"""
Extract and export detailed individual building data from the hub to replace CityBEM input archetypes, including both physical and thermal properties.
:param city: City object containing necessary attributes for the workflow.
:param CityBEM_path: Path where CityBEM input and output files are stored.
"""
with open(CityBEM_path / 'Input_comprehensive_building_data_CityLayer.txt', 'w') as textfile:
writer = csv.writer(textfile, delimiter=',')
header_row="\t".join([
#building general information
"buildingName",
"constructionYear",
"function",
"roofType",
"maxHeight",
"storyHeight",
"storiesAboveGround",
"floorArea",
"volume",
"totalFloorArea",
#roof details
"roofThickness",
"roofExternalH",
"roofInternalH",
"roofUvalue",
"roofLongWaveEmittance",
"roofShortWaveReflectance",
"roofDensity",
"roofSpecificHeat",
"roofWWR",
#floor details
"floorThickness",
"floorExternalH",
"floorInternalH",
"floorUvalue",
"floorLongWaveEmittance",
"floorShortWaveReflectance",
"floorDensity",
"floorSpecificHeat",
"floorWWR",
#wall details
"wallThickness",
"wallExternalH",
"wallInternalH",
"wallUValue",
"wallLongWaveEmittance",
"wallShortWaveReflectance",
"wallDensity",
"wallSpecificHeat",
"wallWWRNorth",
"wallWWREast",
"wallWWRSouth",
"wallWWRWest",
#window details
"windowOverallUValue",
"windowGValue",
"windowFrameRatio",
#building thermal details
"thermalBridgesExtraLoses",
"infiltrationRateOff",
"infiltrationRateOn"
])
textfile.write(header_row + "\n") #write the header row
#extract and write comprehensive building data from the CityLayer's hub
for building in city.buildings:
#data should be appended based on the order of the headers.
row=[]
row.append("b" + building.name)
row.append(building.year_of_construction)
row.append(building.function)
row.append(building.roof_type)
row.append(building.max_height)
row.append(building._storeys_above_ground)
row.append(building.average_storey_height)
row.append(building.floor_area)
row.append(building.volume)
# Initialize boundary rows
row_roof = [None, None, None, None, None]
row_ground = [None, None, None, None, None]
row_wall = [None, None, None, None, None]
wallCount = 0 # so far, the data for one wall represents all the walls
for internal_zone in building.internal_zones:
totalFloorArea = internal_zone.thermal_zones_from_internal_zones[0].total_floor_area
row.append(totalFloorArea) #append the last item in "building general information"
WWR = internal_zone.thermal_archetype.constructions[0].window_ratio #window to wall ratio for the walls
northWWR = float(WWR['north'])/100. #the values from the hub is in percent. The conversion is needed.
eastWWR = float(WWR['east'])/100.
southWWR = float(WWR['south'])/100.
westWWR = float(WWR['west'])/100.
windowOverallUValue = internal_zone.thermal_archetype.constructions[0].window_overall_u_value
windowGValue = internal_zone.thermal_archetype.constructions[0].window_g_value
windowFrameRatio = internal_zone.thermal_archetype.constructions[0].window_frame_ratio
thermalBridgesExtraLoses = internal_zone.thermal_archetype.extra_loses_due_to_thermal_bridges
infiltrationRateOff = internal_zone.thermal_archetype.infiltration_rate_for_ventilation_system_off
infiltrationRateOn = internal_zone.thermal_archetype.infiltration_rate_for_ventilation_system_on
for boundary in internal_zone.thermal_zones_from_internal_zones:
for thermal_boundary in boundary.thermal_boundaries:
if thermal_boundary.type == "Roof":
layers = thermal_boundary.layers #access the roof construction layers
non_zero_layers = [layer for layer in layers if layer.thickness > 0] #filter out layers with zero thickness
total_thickness = thermal_boundary.thickness
if total_thickness > 0:
weighted_density = sum(layer.thickness * layer.density for layer in non_zero_layers) / total_thickness #weighted average represneting the entire layer.
weighted_specific_heat = sum(
layer.thickness * layer.specific_heat for layer in non_zero_layers) / total_thickness
else:
weighted_specific_heat = 0 #handle the case where total_thickness is zero to avoid division by zero
weighted_density = 0
row_roof = [
thermal_boundary.thickness,
thermal_boundary.he,
thermal_boundary.hi,
thermal_boundary.u_value,
thermal_boundary.external_surface.long_wave_emittance,
thermal_boundary.external_surface.short_wave_reflectance,
weighted_density,
weighted_specific_heat,
thermal_boundary.window_ratio
]
elif thermal_boundary.type == "Ground": #ground means floor in CityBEM based on the legacy in CityBEM.
layers = thermal_boundary.layers # access the roof construction layers
non_zero_layers = [layer for layer in layers if layer.thickness > 0] # filter out layers with zero thickness
total_thickness = thermal_boundary.thickness
if total_thickness > 0:
weighted_density = sum(
layer.thickness * layer.density for layer in non_zero_layers) / total_thickness
weighted_specific_heat = sum(
layer.thickness * layer.specific_heat for layer in non_zero_layers) / total_thickness
else:
weighted_specific_heat = 0 # Handle the case where total_thickness is zero to avoid division by zero
weighted_density = 0
row_ground = [
thermal_boundary.thickness,
thermal_boundary.he,
thermal_boundary.hi,
thermal_boundary.u_value,
thermal_boundary.external_surface.long_wave_emittance,
thermal_boundary.external_surface.short_wave_reflectance,
weighted_density,
weighted_specific_heat,
thermal_boundary.window_ratio
]
elif thermal_boundary.type == "Wall" and wallCount == 0:
wallCount += 1 #wall counter. So far, it is assumed that all the walls have a similar properties to be exported to CityBEM, except the WWR
layers = thermal_boundary.layers # access the roof construction layers
non_zero_layers = [layer for layer in layers if
layer.thickness > 0] # filter out layers with zero thickness
total_thickness = thermal_boundary.thickness
if total_thickness > 0:
weighted_density = sum(layer.thickness * layer.density for layer in non_zero_layers) / total_thickness
weighted_specific_heat = sum(
layer.thickness * layer.specific_heat for layer in non_zero_layers) / total_thickness
else:
weighted_specific_heat = 0 # Handle the case where total_thickness is zero to avoid division by zero
weighted_density = 0
row_wall = [
thermal_boundary.thickness,
thermal_boundary.he,
thermal_boundary.hi,
thermal_boundary.u_value,
thermal_boundary.external_surface.long_wave_emittance,
thermal_boundary.external_surface.short_wave_reflectance,
weighted_density,
weighted_specific_heat,
northWWR,
eastWWR,
southWWR,
westWWR
]
row.extend(row_roof)
row.extend(row_ground)
row.extend(row_wall)
#append window details
row.append(windowOverallUValue)
row.append(windowGValue)
row.append(windowFrameRatio)
#append building thermal details
row.append(thermalBridgesExtraLoses)
row.append(infiltrationRateOff)
row.append(infiltrationRateOn)
#convert each item in row to string (if needed) and join with tabs (tab separated data)
row_str = "\t".join(map(str, row))
#write the final row to the text file
textfile.write(row_str + "\n")
print("Individual building data is exported into a file named comprehensive_building_data.txt")
def export_indoor_temperature_setpoint_data(city, CityBEM_path):
"""
Extract and export individual building data on indoor temperature setpoints
:param city: City object containing necessary attributes for the workflow.
:param CityBEM_path: Path where CityBEM input and output files are stored.
"""
#open a text file in write mode (write mode removes the content if there is any)
with open(CityBEM_path /'Input_indoor_setpoint_temperature_CityLayer.txt', 'w') as textfile:
#iterate through each building
for building in city.buildings:
#write the building name
textfile.write("building"+building.name + '\t')
#iterate through each internal zone in the building
for internal_zone in building.internal_zones:
#iterate through each boundary in the internal zone
for boundary in internal_zone.thermal_zones_from_internal_zones:
#gather all indoor setpoint values for both cooling and heating
indoorSetpointValues = []
indoorSetpointValues.extend(boundary.thermal_control.cooling_set_point_schedules[0].values)#cooling on working days
indoorSetpointValues.extend(boundary.thermal_control.cooling_set_point_schedules[1].values)#cooling on Saturday
indoorSetpointValues.extend(boundary.thermal_control.cooling_set_point_schedules[2].values)#cooling on Sunday/holidays
indoorSetpointValues.extend(boundary.thermal_control.heating_set_point_schedules[0].values)#heating on working days
indoorSetpointValues.extend(boundary.thermal_control.heating_set_point_schedules[1].values)#heating on Saturday
indoorSetpointValues.extend(boundary.thermal_control.heating_set_point_schedules[2].values)#heating on Sunday/holidays
#convert values to a tab-separated strings
values_str = '\t'.join(map(str, indoorSetpointValues))
#write the values to the text file for this building
textfile.write(values_str + '\n')
print("Indoor temperature setpoints for every building is successfully exported into a text file named Input_indoor_setpoint_temperature_CityLayer.txt")
def export_internal_heat_gain_data(city, CityBEM_path):
"""
Extract and export individual building data on internal heat gains (occupant, lighting, and equipment)
:param city: City object containing necessary attributes for the workflow.
:param CityBEM_path: Path where CityBEM input and output files are stored.
"""
# open a text file in write mode (write mode removes the content if there is any)
with open(CityBEM_path / 'Input_internal_heat_gain_CityLayer.txt', 'w') as textfile:
# iterate through each building
for building in city.buildings:
# write the building name
textfile.write("building" + building.name + '\t') # (1) building name
# gather all internal heat gains for every building
internalHeatGains = []
# iterate through each internal zone in the building
for internal_zone in building.internal_zones:
# iterate through each internal usage in the internal zone
for usage in internal_zone.usages:
# iterate through internal heat gains
for internalGain in usage.internal_gains: # order: Occupancy, Lighting, and Appliances
internalHeatGains.append(internalGain.average_internal_gain) # (2) average_internal_gain
internalHeatGains.append(internalGain.convective_fraction) # (3) convective_fraction
internalHeatGains.append(internalGain.latent_fraction) # (4) latent_fraction
internalHeatGains.append(internalGain.radiative_fraction) # (5) radiative_fraction
internalHeatGains.extend(internalGain.schedules[0].values) # (6-29) Working day
internalHeatGains.extend(internalGain.schedules[1].values) # (30-54) Saturday
internalHeatGains.extend(internalGain.schedules[2].values) # (55-79)Sunday
# convert values to a tab-separated strings
values_str = '\t'.join(map(str, internalHeatGains))
# 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 run_CityBEM(CityBEM_path):
"""
Run the CityBEM executable after all inputs are processed.
:param CityBEM_path: Path where CityBEM input and output files are stored.
"""
try:
print('CityBEM execution began:')
CityBEM_exe = CityBEM_path / 'CityBEM.exe' # path to the CityBEM executable
# check if the executable file exists
if not CityBEM_exe.exists():
print(f"Error: {CityBEM_exe} does not exist.")
subprocess.run(str(CityBEM_exe), check=True, cwd=str(CityBEM_path)) # execute the CityBEM executable
print("CityBEM executable has finished successfully.")
except Exception as ex:
print(ex)
print('error: ', ex)
print('[CityBEM simulation abort]')
sys.stdout.flush() #print all the running information on the screen

View File

@ -4,11 +4,40 @@ from shapely import Point
from pathlib import Path
# def process_geojson(a, b, c , d, e, f, g , h):
# selection_box = Polygon([[a, b],
# [c, d],
# [e, f],
# [g, h]])
# geojson_file = Path('./data/collinear_clean 2.geojson').resolve()
# output_file = Path('./input_files/output_buildings.geojson').resolve()
# buildings_in_region = []
#
# with open(geojson_file, 'r') as file:
# city = json.load(file)
# buildings = city['features']
#
# for building in buildings:
# coordinates = building['geometry']['coordinates'][0]
# building_polygon = Polygon(coordinates)
# centroid = Point(building_polygon.centroid)
#
# if centroid.within(selection_box):
# buildings_in_region.append(building)
#
# output_region = {"type": "FeatureCollection",
# "features": buildings_in_region}
#
# with open(output_file, 'w') as file:
# file.write(json.dumps(output_region, indent=2))
#
# return output_file
def process_geojson(x, y, diff):
selection_box = Polygon([[x + diff, y - diff],
[x - diff, y - diff],
[x - diff, y + diff],
[x + diff, y + diff]])
selection_box = Polygon([[x+diff, y+diff],
[x-diff, y+diff],
[x-diff, y-diff],
[x+diff, y-diff]])
geojson_file = Path('./data/collinear_clean 2.geojson').resolve()
output_file = Path('./input_files/output_buildings.geojson').resolve()
buildings_in_region = []
@ -31,4 +60,4 @@ def process_geojson(x, y, diff):
with open(output_file, 'w') as file:
file.write(json.dumps(output_region, indent=2))
return output_file
return output_file