diff --git a/helpers/enrich_city.py b/helpers/enrich_city.py deleted file mode 100644 index 7e0f47cf..00000000 --- a/helpers/enrich_city.py +++ /dev/null @@ -1,104 +0,0 @@ -""" -Enrich city -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 imports.construction_factory import ConstructionFactory -from imports.usage_factory import UsageFactory -from imports.schedules_factory import SchedulesFactory - - -class EnrichCity: - """ - Enrich city - """ - - def __init__(self, city): - self._city = city - self._enriched_city = None - self._errors = [] - - @property - def errors(self) -> [str]: - """ - Error list - """ - return self._errors - - def enriched_city(self, construction_format=None, usage_format=None, schedules_format=None, - pickle_construction=False, pickle_usage=False, pickle_schedules=False): - """ - Enrich the city with the given formats - :return: City - """ - if self._enriched_city is None: - self._errors = [] - print('original:', len(self._city.buildings)) - if not pickle_construction: - if construction_format is not None: - self._enriched_city = self._construction(construction_format) - if len(self._errors) != 0: - return self._enriched_city - if not pickle_usage: - if usage_format is not None: - self._enriched_city = self._usage(usage_format) - if len(self._errors) != 0: - return self._enriched_city - if not pickle_schedules: - if schedules_format is not None: - self._enriched_city = self._schedules(schedules_format) - if len(self._errors) != 0: - return self._enriched_city - self._enriched_city = self._city - return self._enriched_city - - def _construction(self, construction_format): - ConstructionFactory(construction_format, self._city).enrich() - - for building in self._city.buildings: - # infiltration_rate_system_off is a mandatory parameter. - # If it is not returned, extract the building from the calculation list - if len(building.thermal_zones) == 0: - self._city.remove_city_object(building) - elif building.thermal_zones[0].infiltration_rate_system_off is None: - self._city.remove_city_object(building) - if self._city.buildings is None: - self._errors.append('no archetype found per construction') - self._enriched_city = self._city - return self._enriched_city - print('enriched with construction:', len(self._city.buildings)) - return self._city - - def _usage(self, usage_format): - UsageFactory(usage_format, self._city).enrich() - for building in self._city.buildings: - # At least one thermal zone must be created. - # If it is not created, extract the building from the calculation list - if len(building.usage_zones) <= 0: - self._city.remove_city_object(building) - if self._city.buildings is None: - self._errors.append('no archetype found per usage') - self._enriched_city = self._city - return self._enriched_city - print('enriched with usage:', len(self._city.buildings)) - return self._city - - def _schedules(self, schedules_format): - SchedulesFactory(schedules_format, self._city).enrich() - for building in self._city.buildings: - counter_schedules = 0 - for usage_zone in building.usage_zones: - # At least one schedule must be created at each thermal zone. - # If it is not created, extract the building from the calculation list - if len(usage_zone.schedules) > 0: - counter_schedules += 1 - if counter_schedules < len(building.usage_zones): - self._city.remove_city_object(building) - if self._city.buildings is None: - self._errors.append('no archetype found per usage') - self._enriched_city = self._city - return self._enriched_city - print('enriched with occupancy:', len(self._city.buildings)) - return self._city diff --git a/imports/schedules/comnet_schedules_parameters.py b/imports/schedules/comnet_schedules_parameters.py deleted file mode 100644 index deaca3b0..00000000 --- a/imports/schedules/comnet_schedules_parameters.py +++ /dev/null @@ -1,57 +0,0 @@ -""" -Schedules retrieve the specific usage schedules module for the given standard -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Concordia CERC group -Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca -Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca -""" -import pandas as pd -from imports.schedules.helpers.schedules_helper import SchedulesHelper -from city_model_structure.attributes.schedule import Schedule -import helpers.constants as cte - - -class ComnetSchedules: - """ - Comnet based schedules - """ - def __init__(self, city, base_path): - self._city = city - self._comnet_schedules_path = base_path / 'comnet_archetypes.xlsx' - xls = pd.ExcelFile(self._comnet_schedules_path) - for building in city.buildings: - for usage_zone in building.usage_zones: - schedules = [] - usage_schedules = pd.read_excel(xls, - sheet_name=SchedulesHelper.comnet_from_usage(usage_zone.usage), - skiprows=[0, 1, 2, 3], nrows=39, usecols="A:AA") - number_of_schedule_types = 13 - schedules_per_schedule_type = 3 - day_types = dict({'week_day': 0, 'saturday': 1, 'sunday': 2}) - for schedule_types in range(0, number_of_schedule_types): - name = '' - data_type = '' - for schedule_day in range(0, schedules_per_schedule_type): - schedule = Schedule() - schedule.time_step = cte.HOUR - schedule.time_range = cte.DAY - row_cells = usage_schedules.iloc[schedules_per_schedule_type*schedule_types + schedule_day] - if schedule_day == day_types['week_day']: - name = row_cells[0] - data_type = row_cells[1] - schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY] - elif schedule_day == day_types['saturday']: - schedule.day_types = [cte.SATURDAY] - else: - schedule.day_types = [cte.SUNDAY] - schedule.type = name - schedule.data_type = SchedulesHelper.data_type_from_comnet(data_type) - if schedule.data_type == cte.ANY_NUMBER: - values = [] - for cell in row_cells[schedules_per_schedule_type:].to_numpy(): - values.append((float(cell) - 32.) * 5 / 9) - schedule.values = values - else: - schedule.values = row_cells[schedules_per_schedule_type:].to_numpy() - schedules.append(schedule) - usage_zone.schedules = schedules diff --git a/imports/schedules/doe_idf.py b/imports/schedules/doe_idf.py deleted file mode 100644 index eeb72168..00000000 --- a/imports/schedules/doe_idf.py +++ /dev/null @@ -1,149 +0,0 @@ -""" -Building test -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Concordia CERC group -Project Coder Guille Gutierrez Morote Guillermo.GutierrezMorote@concordia.ca -Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca -""" - -import pandas as pd -import parseidf -import xmltodict -from imports.schedules.helpers.schedules_helper import SchedulesHelper -from city_model_structure.attributes.schedule import Schedule -from city_model_structure.building_demand.occupancy import Occupancy -from city_model_structure.building_demand.lighting import Lighting -from city_model_structure.building_demand.thermal_control import ThermalControl -import helpers.constants as cte - - -class DoeIdf: - """ - Idf factory to import schedules into the data model - """ - # todo: shouldn't this be in the schedule helper class???? - idf_schedule_to_standard_schedule = {'BLDG_LIGHT_SCH': cte.LIGHTING, - 'BLDG_OCC_SCH_wo_SB': cte.OCCUPANCY, - 'BLDG_EQUIP_SCH': cte.EQUIPMENT, - 'ACTIVITY_SCH': cte.ACTIVITY, - 'INFIL_QUARTER_ON_SCH': cte.INFILTRATION} - - # todo: @Guille -> in idf the types can be written in capital letters or low case, but both should be accepted. - # How is that solved?? Of course not like this - idf_data_type_to_standard_data_type = {'Fraction': cte.FRACTION, - 'fraction': cte.FRACTION, - 'Any Number': cte.ANY_NUMBER, - 'ON/OFF': cte.ON_OFF, - 'On/Off': cte.ON_OFF, - 'Temperature': cte.TEMPERATURE, - 'Humidity': cte.HUMIDITY, - 'Control Type': cte.CONTROL_TYPE} - - _SCHEDULE_COMPACT_TYPE = 'SCHEDULE:COMPACT' - _SCHEDULE_TYPE_NAME = 1 - _SCHEDULE_TYPE_DATA_TYPE = 2 - - def __init__(self, city, base_path, doe_idf_file): - self._hours = [] - panda_hours = pd.timedelta_range(0, periods=24, freq='H') - for _, hour in enumerate(panda_hours): - self._hours.append(str(hour).replace('0 days ', '').replace(':00:00', ':00')) - self._city = city - - path = str(base_path / doe_idf_file) - with open(path) as xml: - self._schedule_library = xmltodict.parse(xml.read()) - - for building in self._city.buildings: - for internal_zone in building.internal_zones: - for usage_zone in internal_zone.usage_zones: - for schedule_archetype in self._schedule_library['archetypes']['archetypes']: - function = schedule_archetype['@building_type'] - if SchedulesHelper.usage_from_function(function) == usage_zone.usage: - self._idf_schedules_path = (base_path / schedule_archetype['idf']['path']).resolve() - with open(self._idf_schedules_path, 'r') as file: - idf = parseidf.parse(file.read()) - self._load_schedule(idf, usage_zone) - break - - def _load_schedule(self, idf, usage_zone): - schedules_day = {} - schedules = [] - for compact_schedule in idf[self._SCHEDULE_COMPACT_TYPE]: - schedule = Schedule() - schedule.time_step = cte.HOUR - schedule.time_range = cte.DAY - if compact_schedule[self._SCHEDULE_TYPE_NAME] in self.idf_schedule_to_standard_schedule: - schedule.type = self.idf_schedule_to_standard_schedule[compact_schedule[self._SCHEDULE_TYPE_NAME]] - schedule.data_type = self.idf_data_type_to_standard_data_type[compact_schedule[self._SCHEDULE_TYPE_DATA_TYPE]] - else: - continue - - days_index = [] - days_schedules = [] - for position, elements in enumerate(compact_schedule): - element_title = elements.title().replace('For: ', '') - if elements.title() != element_title: - days_index.append(position) - # store a cleaned version of the compact schedule - days_schedules.append(element_title) - days_index.append(len(days_schedules)) - - # save schedule - values = [] - for i, day_index in enumerate(days_index): - if day_index == len(days_schedules): - break - schedules_day[f'{days_schedules[day_index]}'] = [] - hour_index = 0 - for hours_values in range(day_index + 1, days_index[i + 1] - 1, 2): - # Create 24h sequence - for index, hour in enumerate(self._hours[hour_index:]): - hour_formatted = days_schedules[hours_values].replace("Until: ", "") - if len(hour_formatted) == 4: - hour_formatted = f'0{hour_formatted}' - if hour == hour_formatted: - hour_index += index - break - entry = days_schedules[hours_values + 1] - schedules_day[f'{days_schedules[day_index]}'].append(entry) - - for entry in schedules_day[f'{days_schedules[day_index]}']: - values.append(entry) - - schedule.values = values - - if 'Weekdays' in days_schedules[day_index]: - schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY] - - elif 'Alldays' in days_schedules[day_index]: - schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY, cte.SATURDAY, - cte.SUNDAY] - - elif 'Weekends' in days_schedules[day_index]: - # Weekends schedule present so let's re set the values - schedule.day_types = [cte.SATURDAY, cte.SUNDAY] - - elif 'Saturday' in days_schedules[day_index]: - schedule.day_types = [cte.SATURDAY] - - elif 'Sunday' in days_schedules[day_index]: - schedule.day_types = [cte.SUNDAY] - - else: - continue - schedules.append(schedule) - - for schedule in schedules: - if schedule.type == cte.OCCUPANCY: - if usage_zone.occupancy is None: - usage_zone.occupancy = Occupancy() - usage_zone.occupancy.occupancy_schedules = [schedule] - elif schedule.type == cte.LIGHTING: - if usage_zone.lighting is None: - usage_zone.lighting = Lighting() - usage_zone.lighting.schedules = [schedule] - elif schedule.type == cte.HVAC_AVAILABILITY: - if usage_zone.thermal_control is None: - usage_zone.thermal_control = ThermalControl() - usage_zone.thermal_control.hvac_availability_schedules = [schedule] diff --git a/imports/schedules_factory.py b/imports/schedules_factory.py deleted file mode 100644 index 5ce61681..00000000 --- a/imports/schedules_factory.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -SchedulesFactory retrieve the specific schedules module for the given standard -This factory can only be called after calling the usage factory so the usage zones are created. -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Concordia CERC group -Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca -""" - -from pathlib import Path -from imports.schedules.doe_idf import DoeIdf - - -class SchedulesFactory: - """ - SchedulesFactor class - """ - def __init__(self, handler, city, base_path=Path(Path(__file__).parent.parent / 'data/schedules')): - self._handler = '_' + handler.lower().replace(' ', '_') - self._city = city - self._base_path = base_path - for building in city.buildings: - for internal_zone in building.internal_zones: - if len(internal_zone.usage_zones) == 0: - raise Exception('It seems that the schedule factory is being called before the usage factory. ' - 'Please ensure that the usage factory is called first as the usage zones must be ' - 'firstly generated.') - - def _doe_idf(self): - """ - Enrich the city by using DOE IDF schedules as data source - """ - DoeIdf(self._city, self._base_path, 'doe_idf.xml') - - def enrich(self): - """ - Enrich the city given to the class using the given schedule handler - :return: None - """ - getattr(self, self._handler, lambda: None)() diff --git a/imports/usage/comnet_usage_parameters.py b/imports/usage/comnet_usage_parameters.py index 8ad563f9..72eeefd6 100644 --- a/imports/usage/comnet_usage_parameters.py +++ b/imports/usage/comnet_usage_parameters.py @@ -14,7 +14,7 @@ import helpers.constants as cte from helpers.configuration_helper import ConfigurationHelper as ch from imports.geometry.helpers.geometry_helper import GeometryHelper from imports.usage.helpers.usage_helper import UsageHelper -from imports.schedules.helpers.schedules_helper import SchedulesHelper +from imports.usage.helpers.schedules_helper import SchedulesHelper from city_model_structure.building_demand.usage_zone import UsageZone from city_model_structure.building_demand.lighting import Lighting from city_model_structure.building_demand.occupancy import Occupancy diff --git a/imports/schedules/helpers/schedules_helper.py b/imports/usage/helpers/schedules_helper.py similarity index 100% rename from imports/schedules/helpers/schedules_helper.py rename to imports/usage/helpers/schedules_helper.py diff --git a/unittests/test_schedules_factory.py b/unittests/test_schedules_factory.py deleted file mode 100644 index 85a3c45d..00000000 --- a/unittests/test_schedules_factory.py +++ /dev/null @@ -1,69 +0,0 @@ -""" -TestSchedulesFactory test and validate the city model structure schedules -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 - -from imports.geometry_factory import GeometryFactory -from imports.usage_factory import UsageFactory -from imports.construction_factory import ConstructionFactory -from imports.schedules_factory import SchedulesFactory -from imports.geometry.helpers.geometry_helper import GeometryHelper - - -class TestSchedulesFactory(TestCase): - """ - TestSchedulesFactory TestCase - """ - - def setUp(self) -> None: - """ - Configure test environment - :return: - """ - self._example_path = (Path(__file__).parent / 'tests_data').resolve() - - def _get_citygml(self, file): - file_path = (self._example_path / file).resolve() - _city = GeometryFactory('citygml', file_path).city - for building in _city.buildings: - building.year_of_construction = 2006 - ConstructionFactory('nrel', _city).enrich() - self.assertIsNotNone(_city, 'city is none') - for building in _city.buildings: - building.function = GeometryHelper.libs_function_from_hft(building.function) - building.year_of_construction = 2005 - UsageFactory('hft', _city).enrich() - return _city - - def test_doe_idf_archetypes(self): - """ - Enrich the city with doe_idf schedule archetypes and verify it - """ - file = (self._example_path / 'C40_Final.gml').resolve() - city = self._get_citygml(file) - occupancy_handler = 'doe_idf' - SchedulesFactory(occupancy_handler, city).enrich() - for building in city.buildings: - for internal_zone in building.internal_zones: - self.assertTrue(len(internal_zone.usage_zones) > 0) - for usage_zone in internal_zone.usage_zones: - self.assertIsNot(len(usage_zone.occupancy.occupancy_schedules), 0, 'no occupancy schedules defined') - for schedule in usage_zone.occupancy.occupancy_schedules: - self.assertIsNotNone(schedule.type) - self.assertIsNotNone(schedule.values) - self.assertIsNotNone(schedule.data_type) - self.assertIsNotNone(schedule.time_step) - self.assertIsNotNone(schedule.time_range) - self.assertIsNotNone(schedule.day_types) - self.assertIsNot(len(usage_zone.lighting.schedules), 0, 'no lighting schedules defined') - for schedule in usage_zone.lighting.schedules: - self.assertIsNotNone(schedule.type) - self.assertIsNotNone(schedule.values) - self.assertIsNotNone(schedule.data_type) - self.assertIsNotNone(schedule.time_step) - self.assertIsNotNone(schedule.time_range) - self.assertIsNotNone(schedule.day_types)