diff --git a/scripts/CityBEM_run.py b/scripts/CityBEM_run.py index c2e1903c..2468e37c 100644 --- a/scripts/CityBEM_run.py +++ b/scripts/CityBEM_run.py @@ -36,8 +36,6 @@ def CityBEM_workflow(city): 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): """ @@ -67,6 +65,7 @@ def get_building_info(geojson_data, building_id): center = polygon.centroid return function_code, (center.x, center.y) + return None, None def export_building_info(city, CityBEM_path, geojson_file): """ @@ -122,13 +121,13 @@ def export_weather_data(city, CityBEM_path): 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. + Export all other information from buildings (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: + with open(CityBEM_path / 'comprehensive_building_data.csv', 'w', newline='') as textfile: writer = csv.writer(textfile, delimiter=',') - header_row="\t".join([ + header_row=[ #building general information "buildingName", "constructionYear", @@ -145,30 +144,18 @@ def export_comprehensive_building_data(city, CityBEM_path): "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", @@ -181,8 +168,9 @@ def export_comprehensive_building_data(city, CityBEM_path): "thermalBridgesExtraLoses", "infiltrationRateOff", "infiltrationRateOn" - ]) - textfile.write(header_row + "\n") #write the header row + ] + writer.writerow(header_row) #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. @@ -218,72 +206,28 @@ def export_comprehensive_building_data(city, CityBEM_path): 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 + elif thermal_boundary.type == "Ground": 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 + wallCount += 1 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, @@ -300,87 +244,21 @@ def export_comprehensive_building_data(city, CityBEM_path): 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 + writer.writerow(row) - #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. + 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 + 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 + 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)