From 85d3e0c7dc63909d8d0e1130cee19e20356f9b77 Mon Sep 17 00:00:00 2001 From: Pilar Date: Mon, 28 Nov 2022 15:25:50 -0500 Subject: [PATCH] checked results to validate model and some modifications according to that --- loads_calculation.py | 77 ++++++++++--------- main.py | 87 --------------------- peak_loads.py | 104 ++++++++++++++++++++++++++ unittests/test_peak_loads_workflow.py | 102 +++++++++++++++++++++++++ unittests/tests_outputs/.gitignore | 4 + 5 files changed, 252 insertions(+), 122 deletions(-) delete mode 100644 main.py create mode 100644 peak_loads.py create mode 100644 unittests/test_peak_loads_workflow.py create mode 100644 unittests/tests_outputs/.gitignore diff --git a/loads_calculation.py b/loads_calculation.py index 0b8dbb7..0274921 100644 --- a/loads_calculation.py +++ b/loads_calculation.py @@ -38,7 +38,6 @@ class LoadsCalculation: * (internal_temperature - external_temperature) load_transmitted_opaque += thermal_zone.additional_thermal_bridge_u_value * thermal_zone.footprint_area \ * (internal_temperature - ambient_temperature) - load_transmitted = load_transmitted_opaque + load_transmitted_transparent return load_transmitted @@ -47,11 +46,11 @@ class LoadsCalculation: load_renovation_sensible = 0 for usage in thermal_zone.usage_zones: load_renovation_sensible += AIR_DENSITY * AIR_HEAT_CAPACITY * usage.mechanical_air_change \ - * thermal_zone.volume * cte.HOUR_TO_MINUTES * cte.MINUTES_TO_SECONDS \ + * thermal_zone.volume / cte.HOUR_TO_MINUTES / cte.MINUTES_TO_SECONDS \ * (internal_temperature - ambient_temperature) - load_infiltration_sensible = AIR_DENSITY * AIR_HEAT_CAPACITY * thermal_zone.infiltration_rate_system_off \ - * thermal_zone.volume * cte.HOUR_TO_MINUTES * cte.MINUTES_TO_SECONDS \ + load_infiltration_sensible = AIR_DENSITY * AIR_HEAT_CAPACITY * float(thermal_zone.infiltration_rate_system_off) \ + * thermal_zone.volume / cte.HOUR_TO_MINUTES / cte.MINUTES_TO_SECONDS \ * (internal_temperature - ambient_temperature) load_ventilation = load_renovation_sensible + load_infiltration_sensible @@ -60,57 +59,65 @@ class LoadsCalculation: def get_heating_transmitted_load(self, ambient_temperature, ground_temperature): heating_load_transmitted = 0 - for thermal_zone in self._building.thermal_zones: - internal_temperature = thermal_zone.thermal_control.mean_heating_set_point - heating_load_transmitted += self._get_load_transmitted(thermal_zone, internal_temperature, ambient_temperature, - ground_temperature) + for internal_zone in self._building.internal_zones: + for thermal_zone in internal_zone.thermal_zones: + internal_temperature = thermal_zone.thermal_control.mean_heating_set_point + heating_load_transmitted += self._get_load_transmitted(thermal_zone, internal_temperature, ambient_temperature, + ground_temperature) return heating_load_transmitted def get_cooling_transmitted_load(self, ambient_temperature, ground_temperature): cooling_load_transmitted = 0 - for thermal_zone in self._building.thermal_zones: - internal_temperature = thermal_zone.thermal_control.mean_cooling_set_point - cooling_load_transmitted += self._get_load_transmitted(thermal_zone, internal_temperature, ambient_temperature, - ground_temperature) + for internal_zone in self._building.internal_zones: + for thermal_zone in internal_zone.thermal_zones: + internal_temperature = thermal_zone.thermal_control.mean_cooling_set_point + cooling_load_transmitted += self._get_load_transmitted(thermal_zone, internal_temperature, ambient_temperature, + ground_temperature) return cooling_load_transmitted def get_heating_ventilation_load_sensible(self, ambient_temperature): heating_ventilation_load = 0 - for thermal_zone in self._building.thermal_zones: - internal_temperature = thermal_zone.thermal_control.mean_heating_set_point - heating_ventilation_load += self._get_load_ventilation(thermal_zone, internal_temperature, ambient_temperature) + for internal_zone in self._building.internal_zones: + for thermal_zone in internal_zone.thermal_zones: + internal_temperature = thermal_zone.thermal_control.mean_heating_set_point + heating_ventilation_load += self._get_load_ventilation(thermal_zone, internal_temperature, ambient_temperature) return heating_ventilation_load def get_cooling_ventilation_load_sensible(self, ambient_temperature): cooling_ventilation_load = 0 - for thermal_zone in self._building.thermal_zones: - internal_temperature = thermal_zone.thermal_control.mean_cooling_set_point - cooling_ventilation_load += self._get_load_ventilation(thermal_zone, internal_temperature, ambient_temperature) + for internal_zone in self._building.internal_zones: + for thermal_zone in internal_zone.thermal_zones: + internal_temperature = thermal_zone.thermal_control.mean_cooling_set_point + cooling_ventilation_load += self._get_load_ventilation(thermal_zone, internal_temperature, ambient_temperature) return cooling_ventilation_load def get_internal_load_sensible(self): cooling_load_occupancy_sensible = 0 cooling_load_lighting = 0 cooling_load_equipment_sensible = 0 - for thermal_zone in self._building.thermal_zones: - cooling_load_occupancy_sensible += (thermal_zone.occupancy.sensible_convective_internal_gain - + thermal_zone.occupancy.sensible_radiative_internal_gain) \ - * thermal_zone.footprint_area - cooling_load_lighting += (thermal_zone.lighting.density * thermal_zone.lighting.convective_fraction - + thermal_zone.lighting.density * thermal_zone.lighting.radiative_fraction) \ - * thermal_zone.footprint_area - cooling_load_equipment_sensible += (thermal_zone.appliances.density * thermal_zone.appliances.convective_fraction - + thermal_zone.appliances.density * thermal_zone.appliances.radiative_fraction) \ - * thermal_zone.footprint_area + for internal_zone in self._building.internal_zones: + for thermal_zone in internal_zone.thermal_zones: + cooling_load_occupancy_sensible += (thermal_zone.occupancy.sensible_convective_internal_gain + + thermal_zone.occupancy.sensible_radiative_internal_gain) \ + * thermal_zone.footprint_area + cooling_load_lighting += (thermal_zone.lighting.density * thermal_zone.lighting.convective_fraction + + thermal_zone.lighting.density * thermal_zone.lighting.radiative_fraction) \ + * thermal_zone.footprint_area + cooling_load_equipment_sensible += (thermal_zone.appliances.density * thermal_zone.appliances.convective_fraction + + thermal_zone.appliances.density * thermal_zone.appliances.radiative_fraction) \ + * thermal_zone.footprint_area internal_load = cooling_load_occupancy_sensible + cooling_load_lighting + cooling_load_equipment_sensible return internal_load - def get_radiation_load(self, hour): + def get_radiation_load(self, irradiance_format, hour): cooling_load_radiation = 0 - for thermal_zone in self._building.thermal_zones: - for thermal_boundary in thermal_zone.thermal_boundaries: - radiation = thermal_boundary.parent_surface.radiation[hour] - for thermal_opening in thermal_boundary.thermal_openings: - cooling_load_radiation += thermal_opening.area * (1 - thermal_opening.frame_ratio) * thermal_opening.g_value \ - * radiation + for internal_zone in self._building.internal_zones: + for thermal_zone in internal_zone.thermal_zones: + for thermal_boundary in thermal_zone.thermal_boundaries: + for thermal_opening in thermal_boundary.thermal_openings: + radiation = thermal_boundary.parent_surface.global_irradiance[cte.HOUR][irradiance_format][hour] + print(radiation) + print(thermal_opening.g_value, thermal_opening.area) + cooling_load_radiation += thermal_opening.area * (1 - thermal_opening.frame_ratio) * thermal_opening.g_value \ + * radiation return cooling_load_radiation diff --git a/main.py b/main.py deleted file mode 100644 index 617fe5e..0000000 --- a/main.py +++ /dev/null @@ -1,87 +0,0 @@ -""" -Peak loads calculation workflow -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Concordia CERC group -Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca -""" - -import glob -import os -import sys -from pathlib import Path - -import helpers.constants as cte -from imports.geometry_factory import GeometryFactory -from imports.construction_factory import ConstructionFactory -from imports.usage_factory import UsageFactory -from loads_calculation import LoadsCalculation - -try: - gml = '' - for argument_tuple in sys.argv[1:]: - print(argument_tuple) - argument = argument_tuple.split(' ') - option = argument[0] - value = argument[1] - if option == '-g': - gml = value - out_path = (Path(__file__).parent.parent / 'out_files') - files = glob.glob(f'{out_path}/*') - for file in files: - if file != '.gitignore': - os.remove(file) - - print('[simulation start]') - city = GeometryFactory('citygml', gml).city - print(f'city created from {gml}') - for building in city.buildings: - building.year_of_construction = 2006 - if building.function is None: - building.function = 'large office' - ConstructionFactory('nrel', city).enrich() - print('enrich constructions... done') - UsageFactory('comnet', city).enrich() - print('enrich usage... done') - - print('calculating:') - - weather_format = 'epw' - - for building in city.buildings: - ambient_temperature = building.external_temperature[cte.HOUR][[weather_format]] - ground_temperature = 0 - heating_ambient_temperature = 100 - cooling_ambient_temperature = -100 - heating_calculation_hour = -1 - cooling_calculation_hour = -1 - for hour, temperature in enumerate(ambient_temperature): - ground_temperature += temperature / 8760 - if temperature < heating_ambient_temperature: - heating_ambient_temperature = temperature - heating_calculation_hour = hour - if temperature > cooling_ambient_temperature: - cooling_ambient_temperature = temperature - cooling_calculation_hour = hour - - loads = LoadsCalculation(building) - heating_load_transmitted = loads.get_heating_transmitted_load(heating_ambient_temperature, ground_temperature) - heating_load_ventilation_sensible = loads.get_heating_ventilation_load_sensible(heating_ambient_temperature) - heating_load_ventilation_latent = 0 - heating_load = heating_load_transmitted + heating_load_ventilation_sensible + heating_load_ventilation_latent - - cooling_load_transmitted = loads.get_cooling_transmitted_load(cooling_ambient_temperature, ground_temperature) - cooling_load_renovation_sensible = loads.get_cooling_ventilation_load_sensible(cooling_ambient_temperature) - cooling_load_internal_gains_sensible = loads.get_internal_load_sensible() - cooling_load_radiation = loads.get_radiation_load(cooling_calculation_hour) - cooling_load_sensible = cooling_load_transmitted + cooling_load_renovation_sensible + cooling_load_radiation \ - + cooling_load_internal_gains_sensible - cooling_load_latent = 0 - cooling_load = cooling_load_sensible + cooling_load_latent - - print('[calculation end]') - -except Exception as ex: - print(ex) - print('error: ', ex) - print('[simulation abort]') -sys.stdout.flush() diff --git a/peak_loads.py b/peak_loads.py new file mode 100644 index 0000000..9fbbca6 --- /dev/null +++ b/peak_loads.py @@ -0,0 +1,104 @@ +""" +Peak loads calculation workflow +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Concordia CERC group +Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" + +from pathlib import Path + +import helpers.constants as cte +from loads_calculation import LoadsCalculation + + +class PeakLoads: + def __init__(self, city, path, weather_format, irradiance_format): + self._city = city + self._path = path + self._weather_format = weather_format + self._irradiance_format = irradiance_format + self._results = [] + + self._sanity_check() + self._workflow() + + def _sanity_check(self): + levels_of_detail = self._city.level_of_detail + if levels_of_detail.geometry is None: + raise Exception(f'Level of detail of geometry not assigned') + if levels_of_detail.geometry < 1: + raise Exception(f'Level of detail of geometry = {levels_of_detail.geometry}. Required minimum level 1') + if levels_of_detail.construction is None: + raise Exception(f'Level of detail of construction not assigned') + if levels_of_detail.construction < 1: + raise Exception(f'Level of detail of construction = {levels_of_detail.construction}. Required minimum level 1') + if levels_of_detail.usage is None: + raise Exception(f'Level of detail of usage not assigned') + if levels_of_detail.usage < 1: + raise Exception(f'Level of detail of usage = {levels_of_detail.usage}. Required minimum level 1') + for building in self._city.buildings: + if cte.HOUR not in building.external_temperature: + raise Exception(f'Building {building.name} does not have external temperature assigned') + for surface in building.surfaces: + if surface.type != cte.GROUND: + if cte.HOUR not in surface.global_irradiance: + raise Exception(f'Building {building.name} does not have global irradiance on surfaces assigned') + + def _workflow(self): + for building in self._city.buildings: + ambient_temperature = building.external_temperature[cte.HOUR][self._weather_format] + ground_temperature = 0 + heating_ambient_temperature = 100 + cooling_ambient_temperature = -100 + heating_calculation_hour = -1 + cooling_calculation_hour = -1 + for hour, temperature in enumerate(ambient_temperature): + ground_temperature += temperature / 8760 + if temperature < heating_ambient_temperature: + heating_ambient_temperature = temperature + heating_calculation_hour = hour + if temperature > cooling_ambient_temperature: + cooling_ambient_temperature = temperature + cooling_calculation_hour = hour + + loads = LoadsCalculation(building) + heating_load_transmitted = loads.get_heating_transmitted_load(heating_ambient_temperature, ground_temperature) + heating_load_ventilation_sensible = loads.get_heating_ventilation_load_sensible(heating_ambient_temperature) + heating_load_ventilation_latent = 0 + heating_load = heating_load_transmitted + heating_load_ventilation_sensible + heating_load_ventilation_latent + + cooling_load_transmitted = loads.get_cooling_transmitted_load(cooling_ambient_temperature, ground_temperature) + cooling_load_renovation_sensible = loads.get_cooling_ventilation_load_sensible(cooling_ambient_temperature) + cooling_load_internal_gains_sensible = loads.get_internal_load_sensible() + cooling_load_radiation = loads.get_radiation_load(self._irradiance_format, cooling_calculation_hour) + cooling_load_sensible = cooling_load_transmitted + cooling_load_renovation_sensible - cooling_load_radiation \ + - cooling_load_internal_gains_sensible + + cooling_load_latent = 0 + cooling_load = cooling_load_sensible + cooling_load_latent + self._results.append([building.name, heating_load, cooling_load]) + self._print_results() + + def _print_results(self): + print_results = 'Peak loads in W' + for results in self._results: + print_results += '\n' + print_results += f'{results[0]}, {results[1]}, {results[2]}\n' + + file = 'city name: ' + self._city.name + '\n' + for building in self._city.buildings: + file += '\n' + file += 'name: ' + building.name + '\n' + file += 'year of construction: ' + str(building.year_of_construction) + '\n' + file += 'function: ' + building.function + '\n' + file += 'floor area: ' + str(building.internal_zones[0].area) + '\n' + file += 'storeys: ' + str(building.storeys_above_ground) + '\n' + file += 'heated_volume: ' + str(0.85 * building.volume) + '\n' + file += 'volume: ' + str(building.volume) + '\n' + + full_path_results = Path(self._path / 'peak_loads.csv').resolve() + with open(full_path_results, 'w') as results_file: + results_file.write(print_results) + full_path_metadata = Path(self._path / 'metadata.csv').resolve() + with open(full_path_metadata, 'w') as metadata_file: + metadata_file.write(file) diff --git a/unittests/test_peak_loads_workflow.py b/unittests/test_peak_loads_workflow.py new file mode 100644 index 0000000..878ce2f --- /dev/null +++ b/unittests/test_peak_loads_workflow.py @@ -0,0 +1,102 @@ +""" +TestPeakLoadsWorkflow test +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Concordia CERC group +Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" + +from pathlib import Path +from unittest import TestCase +import pandas as pd +import helpers.constants as cte +from helpers.monthly_values import MonthlyValues +from imports.geometry_factory import GeometryFactory +from imports.construction_factory import ConstructionFactory +from imports.usage_factory import UsageFactory +from imports.weather_factory import WeatherFactory +from peak_loads import PeakLoads + + +class TestPeakLoadsWorkflow(TestCase): + """ + TestPeakLoadsWorkflow class + """ + def setUp(self) -> None: + """ + Test setup + :return: None + """ + self._city = None + self._complete_city = None + self._example_path = (Path(__file__).parent / 'tests_data').resolve() + self._output_path = (Path(__file__).parent / 'tests_outputs').resolve() + + def _get_citygml(self, file): + file_path = (self._example_path / file).resolve() + self._city = GeometryFactory('citygml', path=file_path).city + self.assertIsNotNone(self._city, 'city is none') + return self._city + + @property + def _read_sra_file(self) -> []: + path = (self._example_path / "one_building_in_kelowna_sra_SW.out").resolve() + _results = pd.read_csv(path, sep='\s+', header=0) + id_building = '' + header_building = [] + _radiation = [] + for column in _results.columns.values: + if id_building != column.split(':')[1]: + id_building = column.split(':')[1] + if len(header_building) > 0: + _radiation.append(pd.concat([MonthlyValues().month_hour, _results[header_building]], axis=1)) + header_building = [column] + else: + header_building.append(column) + _radiation.append(pd.concat([MonthlyValues().month_hour, _results[header_building]], axis=1)) + return _radiation + + def _set_irradiance_surfaces(self, city, irradiance_format): + """ + saves in building surfaces the correspondent irradiance at different time-scales depending on the mode + if building is None, it saves all buildings' surfaces in file, if building is specified, it saves only that + specific building values + :parameter city: city + :return: none + """ + for radiation in self._read_sra_file: + city_object_name = radiation.columns.values.tolist()[1].split(':')[1] + building = city.city_object(city_object_name) + for column in radiation.columns.values: + if column == cte.MONTH: + continue + header_id = column + surface_id = header_id.split(':')[2] + surface = building.surface_by_id(surface_id) + new_value = pd.DataFrame(radiation[[header_id]].to_numpy(), columns=[irradiance_format]) + surface.global_irradiance[cte.HOUR] = new_value + + def _enrich_city(self, city, weather_file, weather_format, irradiance_format, construction_format, usage_format): + WeatherFactory(weather_format, city, file_name=weather_file).enrich() + self._set_irradiance_surfaces(city, irradiance_format) + + for building in city.buildings: + building.year_of_construction = 2006 + if building.function is None: + building.function = cte.LARGE_OFFICE + + ConstructionFactory(construction_format, city).enrich() + UsageFactory(usage_format, city).enrich() + + def test_workflow(self): + outputs_path = (Path(__file__).parent / 'tests_outputs').resolve() + + gml_file = 'one_building_in_kelowna.gml' + city = self._get_citygml(gml_file) + + weather_file = 'CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw' + weather_format = 'epw' + irradiance_format = 'sra' + construction_format = 'nrel' + usage_format = 'comnet' + self._enrich_city(city, weather_file, weather_format, irradiance_format, construction_format, usage_format) + PeakLoads(city, outputs_path, weather_format, irradiance_format) diff --git a/unittests/tests_outputs/.gitignore b/unittests/tests_outputs/.gitignore new file mode 100644 index 0000000..86d0cb2 --- /dev/null +++ b/unittests/tests_outputs/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file