From b34b07b161285f93f58d43f0d34433f9e55cd39d Mon Sep 17 00:00:00 2001 From: Pilar Date: Wed, 7 Dec 2022 10:54:28 -0500 Subject: [PATCH] added nrcan importer usage importer now reads from catalogs --- catalog_factories/data_models/usages/usage.py | 8 +- catalog_factories/usage/nrcan_catalog.py | 18 +- .../building_demand/thermal_zone.py | 54 +++- exports/building_energy/idf.py | 28 +- .../construction/nrel_physics_parameters.py | 11 +- imports/usage/comnet_usage_parameters.py | 215 ++------------ imports/usage/helpers/usage_helper.py | 42 ++- imports/usage/hft_usage_interface.py | 280 ------------------ imports/usage/hft_usage_parameters.py | 42 --- imports/usage/nrcan_usage_parameters.py | 191 ++++++++++++ imports/usage_factory.py | 18 +- unittests/test_construction_factory.py | 2 +- unittests/test_enrichement.py | 4 +- 13 files changed, 341 insertions(+), 572 deletions(-) delete mode 100644 imports/usage/hft_usage_interface.py delete mode 100644 imports/usage/hft_usage_parameters.py create mode 100644 imports/usage/nrcan_usage_parameters.py diff --git a/catalog_factories/data_models/usages/usage.py b/catalog_factories/data_models/usages/usage.py index e83178b8..dc2d7478 100644 --- a/catalog_factories/data_models/usages/usage.py +++ b/catalog_factories/data_models/usages/usage.py @@ -22,7 +22,7 @@ class Usage: lighting, appliances, thermal_control): - self._usage = usage + self._name = usage self._hours_day = hours_day self._days_year = days_year self._mechanical_air_change = mechanical_air_change @@ -34,12 +34,12 @@ class Usage: self._thermal_control = thermal_control @property - def usage(self) -> Union[None, str]: + def name(self) -> Union[None, str]: """ - Get usage zone usage + Get usage zone usage name :return: None or str """ - return self._usage + return self._name @property def hours_day(self) -> Union[None, float]: diff --git a/catalog_factories/usage/nrcan_catalog.py b/catalog_factories/usage/nrcan_catalog.py index 949cb3d2..5842bc3f 100644 --- a/catalog_factories/usage/nrcan_catalog.py +++ b/catalog_factories/usage/nrcan_catalog.py @@ -32,10 +32,6 @@ class NrcanCatalog(Catalog): self._load_schedules() self._content = Content(self._load_archetypes()) - def _calculate_hours_day(self, function): - # todo: pilar need to check how to calculate this value - return 24 - @staticmethod def _extract_schedule(raw): nrcan_schedule_type = raw['category'] @@ -69,8 +65,8 @@ class NrcanCatalog(Catalog): def _load_archetypes(self): usages = [] - usage = self._metadata['nrcan']['standards']['usage'] - url = f'{self._base_url}{usage["space_types_location"]}' + name = self._metadata['nrcan']['standards']['usage'] + url = f'{self._base_url}{name["space_types_location"]}' with urllib.request.urlopen(url) as json_file: space_types = json.load(json_file)['tables']['space_types']['table'] space_types = [st for st in space_types if st['building_type'] == 'Space Function'] @@ -80,8 +76,6 @@ class NrcanCatalog(Catalog): ventilation_rate = space_type['ventilation_per_area'] if ventilation_rate == 0: ventilation_rate = space_type['ventilation_per_person'] - hours_day = self._calculate_hours_day(usage_type) - days_year = 365 occupancy_schedule_name = space_type['occupancy_schedule'] lighting_schedule_name = space_type['lighting_schedule'] appliance_schedule_name = space_type['electric_equipment_schedule'] @@ -97,11 +91,13 @@ class NrcanCatalog(Catalog): occupancy_density = space_type['occupancy_per_area'] lighting_density = space_type['lighting_per_area'] lighting_radiative_fraction = space_type['lighting_fraction_radiant'] + lighting_convective_fraction = 0 if lighting_radiative_fraction is not None: lighting_convective_fraction = 1 - lighting_radiative_fraction lighting_latent_fraction = 0 appliances_density = space_type['electric_equipment_per_area'] appliances_radiative_fraction = space_type['electric_equipment_fraction_radiant'] + appliances_convective_fraction = 0 if appliances_radiative_fraction is not None: appliances_convective_fraction = 1 - appliances_radiative_fraction appliances_latent_fraction = space_type['electric_equipment_fraction_latent'] @@ -131,6 +127,8 @@ class NrcanCatalog(Catalog): None, None, None) + hours_day = None + days_year = None usages.append(Usage(usage_type, hours_day, days_year, @@ -149,7 +147,7 @@ class NrcanCatalog(Catalog): """ _names = {'usages': []} for usage in self._content.usages: - _names['usages'].append(usage.usage) + _names['usages'].append(usage.name) return _names def entries(self, category=None): @@ -165,6 +163,6 @@ class NrcanCatalog(Catalog): :parm: entry name """ for usage in self._content.usages: - if usage.usage.lower() == name.lower(): + if usage.name.lower() == name.lower(): return usage raise IndexError(f"{name} doesn't exists in the catalog") diff --git a/city_model_structure/building_demand/thermal_zone.py b/city_model_structure/building_demand/thermal_zone.py index c09b8acf..57a3f32a 100644 --- a/city_model_structure/building_demand/thermal_zone.py +++ b/city_model_structure/building_demand/thermal_zone.py @@ -26,7 +26,7 @@ class ThermalZone: """ ThermalZone class """ - def __init__(self, thermal_boundaries, parent_internal_zone, volume, footprint_area, usage=None): + def __init__(self, thermal_boundaries, parent_internal_zone, volume, footprint_area, usage_name=None): self._id = None self._parent_internal_zone = parent_internal_zone self._footprint_area = footprint_area @@ -40,9 +40,9 @@ class ThermalZone: self._ordinate_number = None self._view_factors_matrix = None self._total_floor_area = None - self._usage = usage + self._usage_name = usage_name self._usage_from_parent = False - if usage is None: + if usage_name is None: self._usage_from_parent = True self._hours_day = None self._days_year = None @@ -60,14 +60,14 @@ class ThermalZone: if self._usage_from_parent: self._usages = copy.deepcopy(self._parent_internal_zone.usages) else: - values = self._usage.split('_') + values = self._usage_name.split('_') usages = [] for value in values: usages.append(value.split('-')) self._usages = [] for parent_usage in self._parent_internal_zone.usages: for value in usages: - if parent_usage.usage == value[1]: + if parent_usage.name == value[1]: new_usage = copy.deepcopy(parent_usage) new_usage.percentage = float(value[0])/100 self._usages.append(new_usage) @@ -224,19 +224,19 @@ class ThermalZone: self._view_factors_matrix = value @property - def usage(self) -> Union[None, str]: + def usage_name(self) -> Union[None, str]: """ - Get thermal zone usage + Get thermal zone usage name :return: None or str """ if self._usage_from_parent: if self._parent_internal_zone.usages is None: return None - self._usage = '' + self._usage_name = '' for usage_zone in self._parent_internal_zone.usages: - self._usage += str(round(usage_zone.percentage * 100)) + '-' + usage_zone.name + '_' - self._usage = self._usage[:-1] - return self._usage + self._usage_name += str(round(usage_zone.percentage * 100)) + '-' + usage_zone.name + '_' + self._usage_name = self._usage_name[:-1] + return self._usage_name @staticmethod def _get_schedule_of_day(requested_day_type, schedules): @@ -322,7 +322,13 @@ class ThermalZone: if _occupancy_reference.occupancy_schedules is not None: _schedules = [] for i_schedule in range(0, len(_occupancy_reference.occupancy_schedules)): - schedule = copy.deepcopy(_occupancy_reference.occupancy_schedules[i_schedule]) + schedule = Schedule() + schedule.type = _occupancy_reference.occupancy_schedules[i_schedule].type + schedule.day_types = _occupancy_reference.occupancy_schedules[i_schedule].day_types + schedule.data_type = _occupancy_reference.occupancy_schedules[i_schedule].data_type + schedule.time_step = _occupancy_reference.occupancy_schedules[i_schedule].time_step + schedule.time_range = _occupancy_reference.occupancy_schedules[i_schedule].time_range + new_values = [] for i_value in range(0, len(_occupancy_reference.occupancy_schedules[i_schedule].values)): _new_value = 0 @@ -374,7 +380,13 @@ class ThermalZone: if _lighting_reference.schedules is not None: _schedules = [] for i_schedule in range(0, len(_lighting_reference.schedules)): - schedule = copy.deepcopy(_lighting_reference.schedules[i_schedule]) + schedule = Schedule() + schedule.type = _lighting_reference.schedules[i_schedule].type + schedule.day_types = _lighting_reference.schedules[i_schedule].day_types + schedule.data_type = _lighting_reference.schedules[i_schedule].data_type + schedule.time_step = _lighting_reference.schedules[i_schedule].time_step + schedule.time_range = _lighting_reference.schedules[i_schedule].time_range + new_values = [] for i_value in range(0, len(_lighting_reference.schedules[i_schedule].values)): _new_value = 0 @@ -426,7 +438,13 @@ class ThermalZone: if _appliances_reference.schedules is not None: _schedules = [] for i_schedule in range(0, len(_appliances_reference.schedules)): - schedule = copy.deepcopy(_appliances_reference.schedules[i_schedule]) + schedule = Schedule() + schedule.type = _appliances_reference.schedules[i_schedule].type + schedule.day_types = _appliances_reference.schedules[i_schedule].day_types + schedule.data_type = _appliances_reference.schedules[i_schedule].data_type + schedule.time_step = _appliances_reference.schedules[i_schedule].time_step + schedule.time_range = _appliances_reference.schedules[i_schedule].time_range + new_values = [] for i_value in range(0, len(_appliances_reference.schedules[i_schedule].values)): _new_value = 0 @@ -535,7 +553,13 @@ class ThermalZone: _schedules = [] _schedule_type = _types_reference[i_type][1] for i_schedule in range(0, len(_schedule_type)): - schedule = copy.deepcopy(_schedule_type[i_schedule]) + schedule = Schedule() + schedule.type = _schedule_type[i_schedule].type + schedule.day_types = _schedule_type[i_schedule].day_types + schedule.data_type = _schedule_type[i_schedule].data_type + schedule.time_step = _schedule_type[i_schedule].time_step + schedule.time_range = _schedule_type[i_schedule].time_range + new_values = [] for i_value in range(0, len(_schedule_type[i_schedule].values)): _new_value = 0 diff --git a/exports/building_energy/idf.py b/exports/building_energy/idf.py index 43fa23d0..ac4ef398 100644 --- a/exports/building_energy/idf.py +++ b/exports/building_energy/idf.py @@ -207,9 +207,9 @@ class Idf: _schedule.values = _infiltration_values _infiltration_schedules.append(_schedule) for schedule in self._idf.idfobjects[self._HOURLY_SCHEDULE]: - if schedule.Name == f'Infiltration schedules {thermal_zone.usage}': + if schedule.Name == f'Infiltration schedules {thermal_zone.usage_name}': return - return self._add_standard_compact_hourly_schedule(thermal_zone.usage, 'Infiltration', _infiltration_schedules) + return self._add_standard_compact_hourly_schedule(thermal_zone.usage_name, 'Infiltration', _infiltration_schedules) def _add_people_activity_level_schedules(self, thermal_zone): _occ = thermal_zone.occupancy @@ -219,9 +219,9 @@ class Idf: _total_heat = (_occ.sensible_convective_internal_gain + _occ.sensible_radiative_internal_gain + _occ.latent_internal_gain) / _occ.occupancy_density for schedule in self._idf.idfobjects[self._COMPACT_SCHEDULE]: - if schedule.Name == f'Activity Level schedules {thermal_zone.usage}': + if schedule.Name == f'Activity Level schedules {thermal_zone.usage_name}': return - _kwargs = {'Name': f'Activity Level schedules {thermal_zone.usage}', + _kwargs = {'Name': f'Activity Level schedules {thermal_zone.usage_name}', 'Schedule_Type_Limits_Name': self.idf_type_limits[cte.ANY_NUMBER], 'Field_1': 'Through: 12/31', 'Field_2': 'For AllDays', @@ -300,15 +300,15 @@ class Idf: self._add_heating_system(thermal_zone, name) def _add_thermostat(self, thermal_zone): - thermostat_name = f'Thermostat {thermal_zone.usage}' + thermostat_name = f'Thermostat {thermal_zone.usage_name}' for thermostat in self._idf.idfobjects[self._THERMOSTAT]: if thermostat.Name == thermostat_name: return thermostat # todo: change schedules to schedule name and create schedules using the add_schedule function return self._idf.newidfobject(self._THERMOSTAT, Name=thermostat_name, - Heating_Setpoint_Schedule_Name=f'Heating thermostat schedules {thermal_zone.usage}', - Cooling_Setpoint_Schedule_Name=f'Cooling thermostat schedules {thermal_zone.usage}') + Heating_Setpoint_Schedule_Name=f'Heating thermostat schedules {thermal_zone.usage_name}', + Cooling_Setpoint_Schedule_Name=f'Cooling thermostat schedules {thermal_zone.usage_name}') def _add_heating_system(self, thermal_zone, zone_name): for air_system in self._idf.idfobjects[self._IDEAL_LOAD_AIR_SYSTEM]: @@ -317,9 +317,9 @@ class Idf: thermostat = self._add_thermostat(thermal_zone) self._idf.newidfobject(self._IDEAL_LOAD_AIR_SYSTEM, Zone_Name=zone_name, - System_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {thermal_zone.usage}', - Heating_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {thermal_zone.usage}', - Cooling_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {thermal_zone.usage}', + System_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {thermal_zone.usage_name}', + Heating_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {thermal_zone.usage_name}', + Cooling_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {thermal_zone.usage_name}', Template_Thermostat_Name=thermostat.Name) def _add_occupancy(self, thermal_zone, zone_name): @@ -330,11 +330,11 @@ class Idf: self._idf.newidfobject(self._PEOPLE, Name=f'{zone_name}_occupancy', Zone_or_ZoneList_Name=zone_name, - Number_of_People_Schedule_Name=f'Occupancy schedules {thermal_zone.usage}', + Number_of_People_Schedule_Name=f'Occupancy schedules {thermal_zone.usage_name}', Number_of_People_Calculation_Method="People", Number_of_People=number_of_people, Fraction_Radiant=fraction_radiant, - Activity_Level_Schedule_Name=f'Activity Level schedules {thermal_zone.usage}' + Activity_Level_Schedule_Name=f'Activity Level schedules {thermal_zone.usage_name}' ) def _add_infiltration(self, thermal_zone, zone_name): @@ -344,7 +344,7 @@ class Idf: self._idf.newidfobject(self._INFILTRATION, Name=f'{zone_name}_infiltration', Zone_or_ZoneList_Name=zone_name, - Schedule_Name=f'Infiltration schedules {thermal_zone.usage}', + Schedule_Name=f'Infiltration schedules {thermal_zone.usage_name}', Design_Flow_Rate_Calculation_Method='AirChanges/Hour', Air_Changes_per_Hour=thermal_zone.mechanical_air_change ) @@ -387,7 +387,7 @@ class Idf: self._add_vegetation_material(thermal_boundary.parent_surface.vegetation) for thermal_opening in thermal_boundary.thermal_openings: self._add_window_construction_and_material(thermal_opening) - usage = thermal_zone.usage + usage = thermal_zone.usage_name if building.name in self._target_buildings or building.name in self._adjacent_buildings: self._add_infiltration_schedules(thermal_zone) self._add_schedules(usage, 'Occupancy', thermal_zone.occupancy.occupancy_schedules) diff --git a/imports/construction/nrel_physics_parameters.py b/imports/construction/nrel_physics_parameters.py index 22df677f..20576519 100644 --- a/imports/construction/nrel_physics_parameters.py +++ b/imports/construction/nrel_physics_parameters.py @@ -29,12 +29,14 @@ class NrelPhysicsParameters: Returns the city with the construction parameters assigned to the buildings """ city = self._city + nrel_catalog = ConstructionCatalogFactory('nrel').catalog for building in city.buildings: try: - archetype = self._search_archetype(building.function, building.year_of_construction, self._climate_zone) + archetype = self._search_archetype(nrel_catalog, building.function, building.year_of_construction, + self._climate_zone) except KeyError: - sys.stderr.write(f'Building {building.name} has unknown archetype for building function: {building.function} ' - f'and building year of construction: {building.year_of_construction} ' + sys.stderr.write(f'Building {building.name} has unknown construction archetype for building function: ' + f'{building.function} and building year of construction: {building.year_of_construction} ' f'and climate zone reference norm {self._climate_zone}\n') return @@ -62,8 +64,7 @@ class NrelPhysicsParameters: self._calculate_view_factors(thermal_zone) @staticmethod - def _search_archetype(function, year_of_construction, climate_zone): - nrel_catalog = ConstructionCatalogFactory('nrel').catalog + def _search_archetype(nrel_catalog, function, year_of_construction, climate_zone): nrel_archetypes = nrel_catalog.entries('archetypes') for building_archetype in nrel_archetypes: construction_period_limits = building_archetype.construction_period.split(' - ') diff --git a/imports/usage/comnet_usage_parameters.py b/imports/usage/comnet_usage_parameters.py index b2265fe1..c84e9c32 100644 --- a/imports/usage/comnet_usage_parameters.py +++ b/imports/usage/comnet_usage_parameters.py @@ -1,20 +1,15 @@ """ -ComnetUsageParameters model the usage properties +ComnetUsageParameters extracts the usage properties from Comnet catalog and assigns to each building 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 copy import sys -from typing import Dict -import pandas as pd import numpy 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.usage.helpers.schedules_helper import SchedulesHelper from city_model_structure.building_demand.usage import Usage from city_model_structure.building_demand.lighting import Lighting from city_model_structure.building_demand.occupancy import Occupancy @@ -22,6 +17,7 @@ from city_model_structure.building_demand.appliances import Appliances from city_model_structure.building_demand.thermal_control import ThermalControl from city_model_structure.attributes.schedule import Schedule from city_model_structure.building_demand.internal_gain import InternalGain +from catalog_factories.usage_catalog_factory import UsageCatalogFactory class ComnetUsageParameters: @@ -30,162 +26,7 @@ class ComnetUsageParameters: """ def __init__(self, city, base_path): self._city = city - self._base_path = str(base_path / 'comnet_archetypes.xlsx') - self._data = self._read_file() - self._comnet_schedules_path = str(base_path / 'comnet_schedules_archetypes.xlsx') - self._xls = pd.ExcelFile(self._comnet_schedules_path) - - def _read_file(self) -> Dict: - """ - reads xlsx files containing usage information into a dictionary - :return : Dict - """ - number_usage_types = 33 - xl_file = pd.ExcelFile(self._base_path) - file_data = pd.read_excel(xl_file, sheet_name="Modeling Data", usecols="A:AB", skiprows=[0, 1, 2], - nrows=number_usage_types) - - lighting_data = {} - plug_loads_data = {} - occupancy_data = {} - ventilation_rate = {} - water_heating = {} - process_data = {} - schedules_key = {} - - for j in range(0, number_usage_types): - usage_parameters = file_data.iloc[j] - usage_type = usage_parameters[0] - lighting_data[usage_type] = usage_parameters[1:6].values.tolist() - plug_loads_data[usage_type] = usage_parameters[8:13].values.tolist() - occupancy_data[usage_type] = usage_parameters[17:20].values.tolist() - ventilation_rate[usage_type] = usage_parameters[20:21].values.tolist() - water_heating[usage_type] = usage_parameters[23:24].values.tolist() - process_data[usage_type] = usage_parameters[24:26].values.tolist() - schedules_key[usage_type] = usage_parameters[27:28].values.tolist() - - return {'lighting': lighting_data, - 'plug loads': plug_loads_data, - 'occupancy': occupancy_data, - 'ventilation rate': ventilation_rate, - 'water heating': water_heating, - 'process': process_data, - 'schedules_key': schedules_key} - - @staticmethod - def _parse_usage_type(comnet_usage, data, schedules_data): - _usage = Usage() - - # lighting - _lighting = Lighting() - _lighting.latent_fraction = ch().comnet_lighting_latent - _lighting.convective_fraction = ch().comnet_lighting_convective - _lighting.radiative_fraction = ch().comnet_lighting_radiant - _lighting.density = data['lighting'][comnet_usage][4] - - # plug loads - _appliances = None - if data['plug loads'][comnet_usage][0] != 'n.a.': - _appliances = Appliances() - _appliances.latent_fraction = ch().comnet_plugs_latent - _appliances.convective_fraction = ch().comnet_plugs_convective - _appliances.radiative_fraction = ch().comnet_plugs_radiant - _appliances.density = data['plug loads'][comnet_usage][0] - - # occupancy - _occupancy = Occupancy() - value = data['occupancy'][comnet_usage][0] - _occupancy.occupancy_density = 0 - if value != 0: - _occupancy.occupancy_density = 1 / value - - _occupancy.sensible_convective_internal_gain = data['occupancy'][comnet_usage][1] \ - * ch().comnet_occupancy_sensible_convective - _occupancy.sensible_radiative_internal_gain = data['occupancy'][comnet_usage][1] \ - * ch().comnet_occupancy_sensible_radiant - _occupancy.latent_internal_gain = data['occupancy'][comnet_usage][2] - - _usage.mechanical_air_change = data['ventilation rate'][comnet_usage][0] - - schedules_usage = UsageHelper.schedules_key(data['schedules_key'][comnet_usage][0]) - - _extracted_data = pd.read_excel(schedules_data, sheet_name=schedules_usage, usecols="A:AA", skiprows=[0, 1, 2, 3], - nrows=39) - schedules = [] - 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 = _extracted_data.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, cte.HOLIDAY] - _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) - - schedules_types = dict({'Occupancy': 0, 'Lights': 3, 'Receptacle': 6, 'Infiltration': 9, 'HVAC Avail': 12, - 'ClgSetPt': 15, 'HtgSetPt': 18}) - - _schedules = [] - for pointer in range(0, 3): - _schedules.append(schedules[schedules_types['Occupancy']+pointer]) - _occupancy.occupancy_schedules = _schedules - _schedules = [] - for pointer in range(0, 3): - _schedules.append(schedules[schedules_types['Lights']+pointer]) - _lighting.schedules = _schedules - _schedules = [] - for pointer in range(0, 3): - _schedules.append(schedules[schedules_types['Receptacle']+pointer]) - _appliances.schedules = _schedules - - _usage.occupancy = _occupancy - _usage.lighting = _lighting - _usage.appliances = _appliances - - _control = ThermalControl() - _schedules = [] - for pointer in range(0, 3): - _schedules.append(schedules[schedules_types['HtgSetPt']+pointer]) - _control.heating_set_point_schedules = _schedules - _schedules = [] - for pointer in range(0, 3): - _schedules.append(schedules[schedules_types['ClgSetPt']+pointer]) - _control.cooling_set_point_schedules = _schedules - _schedules = [] - for pointer in range(0, 3): - _schedules.append(schedules[schedules_types['HVAC Avail']+pointer]) - _control.hvac_availability_schedules = _schedules - _usage.thermal_control = _control - - return _usage - - def _search_archetypes(self, libs_usage): - for item in self._data['lighting']: - comnet_usage = UsageHelper.comnet_from_libs_usage(libs_usage) - if comnet_usage == item: - usage_archetype = self._parse_usage_type(comnet_usage, self._data, self._xls) - return usage_archetype - return None + self._path = base_path def enrich_buildings(self): """ @@ -193,14 +34,14 @@ class ComnetUsageParameters: :return: """ city = self._city + comnet_catalog = UsageCatalogFactory('comnet').catalog for building in city.buildings: - usage = GeometryHelper.libs_usage_from_libs_function(building.function) + usage_name = UsageHelper.comnet_from_hub_usage(building.function) try: - archetype_usage = self._search_archetypes(usage) + archetype_usage = self._search_archetypes(comnet_catalog, usage_name) except KeyError: - sys.stderr.write(f'Building {building.name} has unknown archetype for building function:' - f' {building.function}, that assigns building usage as ' - f'{GeometryHelper.libs_usage_from_libs_function(building.function)}\n') + sys.stderr.write(f'Building {building.name} has unknown usage archetype for building function:' + f' {building.function}') return for internal_zone in building.internal_zones: @@ -210,46 +51,46 @@ class ComnetUsageParameters: raise Exception('Internal zone volume not defined, ACH cannot be calculated') if internal_zone.area <= 0: raise Exception('Internal zone area is zero, ACH cannot be calculated') - if internal_zone.volume <= 0: - raise Exception('Internal zone volume is zero, ACH cannot be calculated') volume_per_area = internal_zone.volume / internal_zone.area usage_zone = Usage() - usage_zone.name = usage - self._assign_values_usage_zone(usage_zone, archetype_usage, volume_per_area) + usage_zone.name = usage_name + self._assign_values(usage_zone, archetype_usage, volume_per_area) usage_zone.percentage = 1 self._calculate_reduced_values_from_extended_library(usage_zone, archetype_usage) internal_zone.usages = [usage_zone] @staticmethod - def _assign_values_usage_zone(usage_zone, archetype, volume_per_area): + def _search_archetypes(comnet_catalog, usage_name): + comnet_archetypes = comnet_catalog.entries('archetypes').usages + for building_archetype in comnet_archetypes: + if str(usage_name) == str(building_archetype.name): + return building_archetype + raise KeyError('archetype not found') + + @staticmethod + def _assign_values(usage_zone, archetype, volume_per_area): # Due to the fact that python is not a typed language, the wrong object type is assigned to # usage_zone.occupancy when writing usage_zone.occupancy = archetype.occupancy. # Same happens for lighting and appliances. Therefore, this walk around has been done. - usage_zone.mechanical_air_change = archetype.mechanical_air_change * cte.METERS_TO_FEET ** 2 \ - * cte.HOUR_TO_MINUTES / cte.METERS_TO_FEET ** 3 / volume_per_area + usage_zone.mechanical_air_change = archetype.ventilation_rate / volume_per_area \ + * cte.HOUR_TO_MINUTES * cte.MINUTES_TO_SECONDS _occupancy = Occupancy() - _occupancy.occupancy_density = archetype.occupancy.occupancy_density * cte.METERS_TO_FEET**2 - _occupancy.sensible_radiative_internal_gain = archetype.occupancy.sensible_radiative_internal_gain \ - * archetype.occupancy.occupancy_density \ - * cte.METERS_TO_FEET**2 * cte.BTU_H_TO_WATTS - _occupancy.latent_internal_gain = archetype.occupancy.latent_internal_gain \ - * archetype.occupancy.occupancy_density \ - * cte.METERS_TO_FEET**2 * cte.BTU_H_TO_WATTS - _occupancy.sensible_convective_internal_gain = archetype.occupancy.sensible_convective_internal_gain \ - * archetype.occupancy.occupancy_density \ - * cte.METERS_TO_FEET**2 * cte.BTU_H_TO_WATTS - _occupancy.occupancy_schedules = archetype.occupancy.occupancy_schedules + _occupancy.occupancy_density = archetype.occupancy.occupancy_density + _occupancy.sensible_radiative_internal_gain = archetype.occupancy.sensible_radiative_internal_gain + _occupancy.latent_internal_gain = archetype.occupancy.latent_internal_gain + _occupancy.sensible_convective_internal_gain = archetype.occupancy.sensible_convective_internal_gain + _occupancy.occupancy_schedules = archetype.occupancy.schedules usage_zone.occupancy = _occupancy _lighting = Lighting() - _lighting.density = archetype.lighting.density / cte.METERS_TO_FEET ** 2 + _lighting.density = archetype.lighting.density _lighting.convective_fraction = archetype.lighting.convective_fraction _lighting.radiative_fraction = archetype.lighting.radiative_fraction _lighting.latent_fraction = archetype.lighting.latent_fraction _lighting.schedules = archetype.lighting.schedules usage_zone.lighting = _lighting _appliances = Appliances() - _appliances.density = archetype.appliances.density / cte.METERS_TO_FEET ** 2 + _appliances.density = archetype.appliances.density _appliances.convective_fraction = archetype.appliances.convective_fraction _appliances.radiative_fraction = archetype.appliances.radiative_fraction _appliances.latent_fraction = archetype.appliances.latent_fraction diff --git a/imports/usage/helpers/usage_helper.py b/imports/usage/helpers/usage_helper.py index 7ddbe01d..b1ae610a 100644 --- a/imports/usage/helpers/usage_helper.py +++ b/imports/usage/helpers/usage_helper.py @@ -106,7 +106,7 @@ class UsageHelper: 'C-14 Gymnasium': 'C-14 Gymnasium'} @staticmethod - def comnet_from_libs_usage(usage): + def comnet_from_hub_usage(usage): """ Get Comnet usage from the given internal usage key :param usage: str @@ -115,7 +115,7 @@ class UsageHelper: try: return UsageHelper._usage_to_comnet[usage] except KeyError: - sys.stderr.write('Error: keyword not found to translate from libs_usage to comnet usage.\n') + sys.stderr.write('Error: keyword not found to translate from hub_usage to comnet usage.\n') @staticmethod def schedules_key(usage): @@ -129,3 +129,41 @@ class UsageHelper: except KeyError: sys.stderr.write('Error: Comnet keyword not found. An update of the Comnet files might have been ' 'done changing the keywords.\n') + + _usage_to_nrcan = { + cte.RESIDENTIAL: 'Multi-unit residential', + cte.SINGLE_FAMILY_HOUSE: 'Multi-unit residential', + cte.MULTI_FAMILY_HOUSE: 'Multi-unit residential', + cte.EDUCATION: 'School/university', + cte.SCHOOL_WITHOUT_SHOWER: 'School/university', + cte.SCHOOL_WITH_SHOWER: 'School/university', + cte.RETAIL_SHOP_WITHOUT_REFRIGERATED_FOOD: 'Retail', + cte.RETAIL_SHOP_WITH_REFRIGERATED_FOOD: 'Retail', + cte.HOTEL: 'Hotel', + cte.HOTEL_MEDIUM_CLASS: 'Hotel', + cte.DORMITORY: 'Dormitory', + cte.INDUSTRY: 'Manufacturing Facility', + cte.RESTAURANT: 'Dining - family', + cte.HEALTH_CARE: 'Hospital', + cte.RETIREMENT_HOME_OR_ORPHANAGE: 'Multi-unit residential', + cte.OFFICE_AND_ADMINISTRATION: 'Office', + cte.EVENT_LOCATION: 'Convention centre', + cte.HALL: 'Convention centre', + cte.SPORTS_LOCATION: 'Gymnasium', + cte.LABOR: 'Gymnasium', + cte.GREEN_HOUSE: cte.GREEN_HOUSE, + cte.NON_HEATED: cte.NON_HEATED + } + + @staticmethod + def nrcan_from_hub_usage(usage): + """ + Get Nrcan usage from the given internal usage key + :param usage: str + :return: str + """ + try: + return UsageHelper._usage_to_nrcan[usage] + except KeyError: + sys.stderr.write('Error: keyword not found to translate from hub_usage to nrcan usage.\n') + diff --git a/imports/usage/hft_usage_interface.py b/imports/usage/hft_usage_interface.py deleted file mode 100644 index e44832d4..00000000 --- a/imports/usage/hft_usage_interface.py +++ /dev/null @@ -1,280 +0,0 @@ -""" -Hft-based interface, it reads format defined within the CERC team (based on that one used in SimStadt and developed by -the IAF team at hft-Stuttgart) -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 xmltodict -import copy -from city_model_structure.building_demand.usage import Usage -from city_model_structure.building_demand.internal_gain import InternalGain -from city_model_structure.building_demand.occupancy import Occupancy -from city_model_structure.building_demand.appliances import Appliances -from city_model_structure.building_demand.thermal_control import ThermalControl -from city_model_structure.attributes.schedule import Schedule -import helpers.constants as cte -from imports.usage.helpers.usage_helper import UsageHelper - - -class HftUsageInterface: - """ - HftUsageInterface abstract class - """ - - def __init__(self, base_path, usage_file='ca_library_reduced.xml'): - path = str(base_path / usage_file) - self._usage_archetypes = [] - with open(path) as xml: - self._archetypes = xmltodict.parse(xml.read(), force_list=('zoneUsageVariant', 'zoneUsageType')) - for zone_usage_type in self._archetypes['buildingUsageLibrary']['zoneUsageType']: - usage = zone_usage_type['id'] - usage_archetype = self._parse_zone_usage_type(usage, zone_usage_type) - self._usage_archetypes.append(usage_archetype) - if 'zoneUsageVariant' in zone_usage_type: - for usage_zone_variant in zone_usage_type['zoneUsageVariant']: - usage = usage_zone_variant['id'] - usage_archetype_variant = self._parse_zone_usage_variant(usage, usage_archetype, usage_zone_variant) - self._usage_archetypes.append(usage_archetype_variant) - - @staticmethod - def _parse_zone_usage_type(usage, zone_usage_type): - usage_zone_archetype = Usage() - usage_zone_archetype.name = usage - - if 'occupancy' in zone_usage_type: - _occupancy = Occupancy() - _occupancy.occupancy_density = zone_usage_type['occupancy']['occupancyDensity'] #todo: check units - - if 'internGains' in zone_usage_type['occupancy']: - _internal_gain = InternalGain() - _internal_gain.latent_fraction = zone_usage_type['occupancy']['internGains']['latentFraction'] - _internal_gain.convective_fraction = zone_usage_type['occupancy']['internGains']['convectiveFraction'] - _internal_gain.average_internal_gain = zone_usage_type['occupancy']['internGains']['averageInternGainPerSqm'] - _internal_gain.radiative_fraction = zone_usage_type['occupancy']['internGains']['radiantFraction'] - if 'load' in zone_usage_type['occupancy']['internGains']: - _schedule = Schedule() - _schedule.type = 'internal gains load' - _schedule.time_range = cte.DAY - _schedule.time_step = cte.HOUR - _schedule.data_type = cte.ANY_NUMBER - _schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY, cte.SATURDAY, - cte.SUNDAY] - _values = zone_usage_type['occupancy']['internGains']['load']['weekDayProfile']['values'] - while ' ' in _values: - _values = _values.replace(' ', ' ') - _values = _values.split() - _values_float = [] - for _value in _values: - _values_float.append(float(_value)) - _schedule.values = _values_float - _internal_gain.schedules = [_schedule] - - usage_zone_archetype.internal_gains = [_internal_gain] - - usage_zone_archetype.hours_day = zone_usage_type['occupancy']['usageHoursPerDay'] - usage_zone_archetype.days_year = zone_usage_type['occupancy']['usageDaysPerYear'] - usage_zone_archetype.occupancy = _occupancy - - if 'endUses' in zone_usage_type: - _thermal_control = ThermalControl() - if 'space_heating' in zone_usage_type['endUses']: - _thermal_control.mean_heating_set_point = \ - zone_usage_type['endUses']['space_heating']['heatingSetPointTemperature'] - _thermal_control.heating_set_back = zone_usage_type['endUses']['space_heating']['heatingSetBackTemperature'] - if 'schedule' in zone_usage_type['endUses']['space_heating']: - _schedule = Schedule() - _schedule.type = 'heating temperature' - _schedule.time_range = cte.DAY - _schedule.time_step = cte.HOUR - _schedule.data_type = cte.ANY_NUMBER - _schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY, cte.SATURDAY, - cte.SUNDAY] - _values = zone_usage_type['endUses']['space_heating']['schedule']['weekDayProfile']['values'] - while ' ' in _values: - _values = _values.replace(' ', ' ') - _values = _values.split() - _values_float = [] - for _value in _values: - _values_float.append(float(_value)) - _schedule.values = _values_float - _thermal_control.heating_set_point_schedules = [_schedule] - - if 'space_cooling' in zone_usage_type['endUses']: - _thermal_control.mean_cooling_set_point = \ - zone_usage_type['endUses']['space_cooling']['coolingSetPointTemperature'] - if 'schedule' in zone_usage_type['endUses']['space_cooling']: - _schedule = Schedule() - _schedule.type = 'cooling temperature' - _schedule.time_range = cte.DAY - _schedule.time_step = cte.HOUR - _schedule.data_type = cte.ANY_NUMBER - _schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY, cte.SATURDAY, - cte.SUNDAY] - _values = zone_usage_type['endUses']['space_cooling']['schedule']['weekDayProfile']['values'] - while ' ' in _values: - _values = _values.replace(' ', ' ') - _values = _values.split() - _values_float = [] - for _value in _values: - _values_float.append(float(_value)) - _schedule.values = _values_float - _thermal_control.cooling_set_point_schedules = [_schedule] - - usage_zone_archetype.thermal_control = _thermal_control - - if 'ventilation' in zone_usage_type['endUses'] and zone_usage_type['endUses']['ventilation'] is not None: - usage_zone_archetype.mechanical_air_change = \ - zone_usage_type['endUses']['ventilation']['mechanicalAirChangeRate'] - - # todo: not used or assigned anywhere - if 'domestic_hot_water' in zone_usage_type['endUses']: - # liters to cubic meters - dhw_average_volume_pers_day = float( - zone_usage_type['endUses']['domestic_hot_water']['averageVolumePerPersAndDay']) / 1000 - dhw_preparation_temperature = zone_usage_type['endUses']['domestic_hot_water']['preparationTemperature'] - - if 'all_electrical_appliances' in zone_usage_type['endUses']: - if 'averageConsumptionPerSqmAndYear' in zone_usage_type['endUses']['all_electrical_appliances']: - # kWh to J - usage_zone_archetype.electrical_app_average_consumption_sqm_year = \ - float(zone_usage_type['endUses']['all_electrical_appliances']['averageConsumptionPerSqmAndYear']) \ - * cte.KILO_WATTS_HOUR_TO_JULES - - if 'appliance' in zone_usage_type: - _appliances = Appliances() - _appliances.density = zone_usage_type['appliance']['#text'] #todo: check units - - usage_zone_archetype.appliances = _appliances - - return usage_zone_archetype - - @staticmethod - def _parse_zone_usage_variant(usage, usage_zone, usage_zone_variant): - # the variants mimic the inheritance concept from OOP - usage_zone_archetype = copy.deepcopy(usage_zone) - usage_zone_archetype.name = usage - - if 'occupancy' in usage_zone_variant: - _occupancy = Occupancy() - if 'occupancyDensity' in usage_zone_variant['occupancy']: - _occupancy.occupancy_density = usage_zone_variant['occupancy']['occupancyDensity'] # todo: check units - if 'usageHoursPerDay' in usage_zone_variant['occupancy']: - usage_zone_archetype.hours_day = usage_zone_variant['occupancy']['usageHoursPerDay'] - if 'usageDaysPerYear' in usage_zone_variant['occupancy']: - usage_zone_archetype.days_year = usage_zone_variant['occupancy']['usageDaysPerYear'] - usage_zone_archetype.occupancy = _occupancy - - if 'internGains' in usage_zone_variant['occupancy']: - _internal_gain = InternalGain() - if 'latentFraction' in usage_zone_variant['occupancy']['internGains']: - _internal_gain.latent_fraction = usage_zone_variant['occupancy']['internGains']['latentFraction'] - if 'convectiveFraction' in usage_zone_variant['occupancy']['internGains']: - _internal_gain.convective_fraction = usage_zone_variant['occupancy']['internGains']['convectiveFraction'] - if 'averageInternGainPerSqm' in usage_zone_variant['occupancy']['internGains']: - _internal_gain.average_internal_gain = \ - usage_zone_variant['occupancy']['internGains']['averageInternGainPerSqm'] - if 'radiantFraction' in usage_zone_variant['occupancy']['internGains']: - _internal_gain.radiative_fraction = usage_zone_variant['occupancy']['internGains']['radiantFraction'] - if 'load' in usage_zone_variant['occupancy']['internGains']: - _schedule = Schedule() - _schedule.type = 'internal gains load' - _schedule.time_range = cte.DAY - _schedule.time_step = cte.HOUR - _schedule.data_type = cte.ANY_NUMBER - _schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY, cte.SATURDAY, - cte.SUNDAY] - _values = usage_zone_variant['occupancy']['internGains']['load']['weekDayProfile']['values'] - while ' ' in _values: - _values = _values.replace(' ', ' ') - _values = _values.split() - _values_float = [] - for _value in _values: - _values_float.append(float(_value)) - _schedule.values = _values_float - _internal_gain.schedules = [_schedule] - - usage_zone_archetype.internal_gains = [_internal_gain] - - if 'endUses' in usage_zone_variant: - _thermal_control = ThermalControl() - if 'space_heating' in usage_zone_variant['endUses']: - if 'heatingSetPointTemperature' in usage_zone_variant['endUses']['space_heating']: - _thermal_control.mean_heating_set_point = \ - usage_zone_variant['endUses']['space_heating']['heatingSetPointTemperature'] - if 'heatingSetBackTemperature' in usage_zone_variant['endUses']['space_heating']: - _thermal_control.heating_set_back = usage_zone_variant['endUses']['space_heating']['heatingSetBackTemperature'] - if 'schedule' in usage_zone_variant['endUses']['space_heating']: - _schedule = Schedule() - _schedule.type = 'heating temperature' - _schedule.time_range = cte.DAY - _schedule.time_step = cte.HOUR - _schedule.data_type = cte.ANY_NUMBER - _schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY, cte.SATURDAY, - cte.SUNDAY] - _values = usage_zone_variant['endUses']['space_heating']['schedule']['weekDayProfile']['values'] - while ' ' in _values: - _values = _values.replace(' ', ' ') - _values = _values.split() - _values_float = [] - for _value in _values: - _values_float.append(float(_value)) - _schedule.values = _values_float - _thermal_control.heating_set_point_schedules = [_schedule] - - if 'space_cooling' in usage_zone_variant['endUses'] and \ - usage_zone_variant['endUses']['space_cooling'] is not None: - if 'coolingSetPointTemperature' in usage_zone_variant['endUses']['space_cooling']: - _thermal_control.mean_cooling_set_point = \ - usage_zone_variant['endUses']['space_cooling']['coolingSetPointTemperature'] - if 'schedule' in usage_zone_variant['endUses']['space_cooling']: - _schedule = Schedule() - _schedule.type = 'cooling temperature' - _schedule.time_range = cte.DAY - _schedule.time_step = cte.HOUR - _schedule.data_type = cte.ANY_NUMBER - _schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY, cte.SATURDAY, - cte.SUNDAY] - _values = usage_zone_variant['endUses']['space_cooling']['schedule']['weekDayProfile']['values'] - while ' ' in _values: - _values = _values.replace(' ', ' ') - _values = _values.split() - _values_float = [] - for _value in _values: - _values_float.append(float(_value)) - _schedule.values = _values_float - _thermal_control.cooling_set_point_schedules = [_schedule] - - usage_zone_archetype.thermal_control = _thermal_control - - if 'ventilation' in usage_zone_variant['endUses'] and usage_zone_variant['endUses']['ventilation'] is not None: - usage_zone_archetype.mechanical_air_change = \ - usage_zone_variant['endUses']['ventilation']['mechanicalAirChangeRate'] - - if 'appliance' in usage_zone_variant: - _appliances = Appliances() - _appliances.density = usage_zone_variant['appliance']['#text'] # todo: check units - - usage_zone_archetype.appliances = _appliances - - return usage_zone_archetype - - def _search_archetype(self, libs_usage): - building_usage = UsageHelper().hft_from_libs_usage(libs_usage) - for building_archetype in self._usage_archetypes: - if building_archetype.name == building_usage: - return building_archetype - return None - - @staticmethod - def _assign_values(usage, archetype): - usage_zone = Usage() - usage_zone.name = usage - usage_zone.internal_gains = copy.deepcopy(archetype.internal_gains) - usage_zone.mechanical_air_change = archetype.mechanical_air_change - usage_zone.occupancy = copy.deepcopy(archetype.occupancy) - usage_zone.appliances = copy.deepcopy(archetype.appliances) - usage_zone.thermal_control = copy.deepcopy(archetype.thermal_control) - usage_zone.days_year = archetype.days_year - usage_zone.hours_day = archetype.hours_day - return usage_zone diff --git a/imports/usage/hft_usage_parameters.py b/imports/usage/hft_usage_parameters.py deleted file mode 100644 index 3d869242..00000000 --- a/imports/usage/hft_usage_parameters.py +++ /dev/null @@ -1,42 +0,0 @@ -""" -HftUsageParameters model the usage properties -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 sys - -from imports.geometry.helpers.geometry_helper import GeometryHelper -from imports.usage.hft_usage_interface import HftUsageInterface -from imports.usage.helpers.usage_helper import UsageHelper - - -class HftUsageParameters(HftUsageInterface): - """ - HftUsageParameters class - """ - def __init__(self, city, base_path): - super().__init__(base_path, 'de_library.xml') - self._city = city - - def enrich_buildings(self): - """ - Returns the city with the usage parameters assigned to the buildings - :return: - """ - city = self._city - for building in city.buildings: - usage = GeometryHelper().libs_usage_from_libs_function(building.function) - try: - archetype = self._search_archetype(usage) - except KeyError: - sys.stderr.write(f'Building {building.name} has unknown archetype for building function:' - f' {building.function}, that assigns building usage as ' - f'{GeometryHelper().libs_usage_from_libs_function(building.function)}\n') - return - - for internal_zone in building.internal_zones: - libs_usage = GeometryHelper().libs_usage_from_libs_function(building.function) - usage_zone = self._assign_values(UsageHelper().hft_from_libs_usage(libs_usage), archetype) - usage_zone.percentage = 1 - internal_zone.usages = [usage_zone] diff --git a/imports/usage/nrcan_usage_parameters.py b/imports/usage/nrcan_usage_parameters.py new file mode 100644 index 00000000..ea2b1a4b --- /dev/null +++ b/imports/usage/nrcan_usage_parameters.py @@ -0,0 +1,191 @@ +""" +NrcanUsageParameters extracts the usage properties from NRCAN catalog and assigns to each building +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 copy +import sys +import numpy + +import helpers.constants as cte +from imports.usage.helpers.usage_helper import UsageHelper +from city_model_structure.building_demand.usage import Usage +from city_model_structure.building_demand.lighting import Lighting +from city_model_structure.building_demand.occupancy import Occupancy +from city_model_structure.building_demand.appliances import Appliances +from city_model_structure.building_demand.thermal_control import ThermalControl +from city_model_structure.attributes.schedule import Schedule +from city_model_structure.building_demand.internal_gain import InternalGain +from catalog_factories.usage_catalog_factory import UsageCatalogFactory + + +class NrcanUsageParameters: + """ + NrcanUsageParameters class + """ + def __init__(self, city, base_path): + self._city = city + self._path = base_path + + def enrich_buildings(self): + """ + Returns the city with the usage parameters assigned to the buildings + :return: + """ + city = self._city + nrcan_catalog = UsageCatalogFactory('nrcan').catalog + + for building in city.buildings: + usage_name = UsageHelper.nrcan_from_hub_usage(building.function) + try: + archetype_usage = self._search_archetypes(nrcan_catalog, usage_name) + except KeyError: + sys.stderr.write(f'Building {building.name} has unknown usage archetype for building function:' + f' {building.function}') + return + + for internal_zone in building.internal_zones: + if internal_zone.area is None: + raise Exception('Internal zone area not defined, ACH cannot be calculated') + if internal_zone.volume is None: + raise Exception('Internal zone volume not defined, ACH cannot be calculated') + if internal_zone.area <= 0: + raise Exception('Internal zone area is zero, ACH cannot be calculated') + volume_per_area = internal_zone.volume / internal_zone.area + usage_zone = Usage() + usage_zone.name = usage_name + self._assign_values(usage_zone, archetype_usage, volume_per_area) + usage_zone.percentage = 1 + self._calculate_reduced_values_from_extended_library(usage_zone, archetype_usage) + + internal_zone.usages = [usage_zone] + + @staticmethod + def _search_archetypes(comnet_catalog, usage_name): + comnet_archetypes = comnet_catalog.entries('archetypes').usages + for building_archetype in comnet_archetypes: + if str(usage_name) == str(building_archetype.name): + return building_archetype + raise KeyError('archetype not found') + + @staticmethod + def _assign_values(usage_zone, archetype, volume_per_area): + # Due to the fact that python is not a typed language, the wrong object type is assigned to + # usage_zone.occupancy when writing usage_zone.occupancy = archetype.occupancy. + # Same happens for lighting and appliances. Therefore, this walk around has been done. + if archetype.mechanical_air_change > 0: + usage_zone.mechanical_air_change = archetype.mechanical_air_change + elif archetype.ventilation_rate > 0: + usage_zone.mechanical_air_change = archetype.ventilation_rate / volume_per_area \ + * cte.HOUR_TO_MINUTES * cte.MINUTES_TO_SECONDS + else: + usage_zone.mechanical_air_change = 0 + _occupancy = Occupancy() + _occupancy.occupancy_density = archetype.occupancy.occupancy_density + _occupancy.sensible_radiative_internal_gain = archetype.occupancy.sensible_radiative_internal_gain + _occupancy.latent_internal_gain = archetype.occupancy.latent_internal_gain + _occupancy.sensible_convective_internal_gain = archetype.occupancy.sensible_convective_internal_gain + _occupancy.occupancy_schedules = archetype.occupancy.occupancy_schedules + usage_zone.occupancy = _occupancy + _lighting = Lighting() + _lighting.density = archetype.lighting.density + _lighting.convective_fraction = archetype.lighting.convective_fraction + _lighting.radiative_fraction = archetype.lighting.radiative_fraction + _lighting.latent_fraction = archetype.lighting.latent_fraction + _lighting.schedules = archetype.lighting.schedules + usage_zone.lighting = _lighting + _appliances = Appliances() + _appliances.density = archetype.appliances.density + _appliances.convective_fraction = archetype.appliances.convective_fraction + _appliances.radiative_fraction = archetype.appliances.radiative_fraction + _appliances.latent_fraction = archetype.appliances.latent_fraction + _appliances.schedules = archetype.appliances.schedules + usage_zone.appliances = _appliances + _control = ThermalControl() + _control.cooling_set_point_schedules = archetype.thermal_control.cooling_set_point_schedules + _control.heating_set_point_schedules = archetype.thermal_control.heating_set_point_schedules + _control.hvac_availability_schedules = archetype.thermal_control.hvac_availability_schedules + usage_zone.thermal_control = _control + + @staticmethod + def _calculate_reduced_values_from_extended_library(usage_zone, archetype): + number_of_days_per_type = {'WD': 251, 'Sat': 52, 'Sun': 62} + total = 0 + for schedule in archetype.thermal_control.hvac_availability_schedules: + if schedule.day_types[0] == cte.SATURDAY: + for value in schedule.values: + total += value * number_of_days_per_type['Sat'] + elif schedule.day_types[0] == cte.SUNDAY: + for value in schedule.values: + total += value * number_of_days_per_type['Sun'] + else: + for value in schedule.values: + total += value * number_of_days_per_type['WD'] + + usage_zone.hours_day = total / 365 + usage_zone.days_year = 365 + + @staticmethod + def _calculate_internal_gains(archetype): + + _DAYS = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY, cte.SATURDAY, cte.SUNDAY, cte.HOLIDAY] + _number_of_days_per_type = [51, 50, 50, 50, 50, 52, 52, 10] + + _mean_internal_gain = InternalGain() + _mean_internal_gain.type = 'mean_value_of_internal_gains' + _base_schedule = Schedule() + _base_schedule.type = cte.INTERNAL_GAINS + _base_schedule.time_range = cte.DAY + _base_schedule.time_step = cte.HOUR + _base_schedule.data_type = cte.FRACTION + + _latent_heat_gain = archetype.occupancy.latent_internal_gain + _convective_heat_gain = archetype.occupancy.sensible_convective_internal_gain + _radiative_heat_gain = archetype.occupancy.sensible_radiative_internal_gain + _total_heat_gain = (_latent_heat_gain + _convective_heat_gain + _radiative_heat_gain) + + _schedule_values = numpy.zeros([24, 8]) + _sum = 0 + for day, _schedule in enumerate(archetype.occupancy.schedules): + for v, value in enumerate(_schedule.values): + _schedule_values[v, day] = value * _total_heat_gain + _sum += value * _total_heat_gain * _number_of_days_per_type[day] + + _total_heat_gain += archetype.lighting.density + archetype.appliances.density + _latent_heat_gain += archetype.lighting.latent_fraction * archetype.lighting.density\ + + archetype.appliances.latent_fraction * archetype.appliances.density + _radiative_heat_gain = archetype.lighting.radiative_fraction * archetype.lighting.density \ + + archetype.appliances.radiative_fraction * archetype.appliances.density + _convective_heat_gain = archetype.lighting.convective_fraction * archetype.lighting.density \ + + archetype.appliances.convective_fraction * archetype.appliances.density + + for day, _schedule in enumerate(archetype.lighting.schedules): + for v, value in enumerate(_schedule.values): + _schedule_values[v, day] += value * archetype.lighting.density + _sum += value * archetype.lighting.density * _number_of_days_per_type[day] + + for day, _schedule in enumerate(archetype.appliances.schedules): + for v, value in enumerate(_schedule.values): + _schedule_values[v, day] += value * archetype.appliances.density + _sum += value * archetype.appliances.density * _number_of_days_per_type[day] + + _latent_fraction = _latent_heat_gain / _total_heat_gain + _radiative_fraction = _radiative_heat_gain / _total_heat_gain + _convective_fraction = _convective_heat_gain / _total_heat_gain + _average_internal_gain = _sum / _total_heat_gain + + _schedules = [] + for day in range(0, len(_DAYS)): + _schedule = copy.deepcopy(_base_schedule) + _schedule.day_types = [_DAYS[day]] + _schedule.values = _schedule_values[:day] + _schedules.append(_schedule) + + _mean_internal_gain.average_internal_gain = _average_internal_gain + _mean_internal_gain.latent_fraction = _latent_fraction + _mean_internal_gain.convective_fraction = _convective_fraction + _mean_internal_gain.radiative_fraction = _radiative_fraction + _mean_internal_gain.schedules = _schedules + + return [_mean_internal_gain] diff --git a/imports/usage_factory.py b/imports/usage_factory.py index 3ec4e45d..873812ff 100644 --- a/imports/usage_factory.py +++ b/imports/usage_factory.py @@ -7,9 +7,8 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ from pathlib import Path -from imports.usage.hft_usage_parameters import HftUsageParameters from imports.usage.comnet_usage_parameters import ComnetUsageParameters - +from imports.usage.nrcan_usage_parameters import NrcanUsageParameters class UsageFactory: """ @@ -22,20 +21,19 @@ class UsageFactory: self._city = city self._base_path = base_path - def _hft(self): - """ - Enrich the city with HFT usage library - """ - self._city.level_of_detail.usage = 2 - HftUsageParameters(self._city, self._base_path).enrich_buildings() - def _comnet(self): """ Enrich the city with COMNET usage library """ - self._city.level_of_detail.usage = 2 + self._city.level_of_detail.usage_name = 2 ComnetUsageParameters(self._city, self._base_path).enrich_buildings() + def _nrcan(self): + """ + Enrich the city with NRCAN usage library + """ + return NrcanUsageParameters(self._city, self._base_path).enrich_buildings() + def enrich(self): """ Enrich the city given to the class using the usage factory given handler diff --git a/unittests/test_construction_factory.py b/unittests/test_construction_factory.py index 6ba75ee9..5be1db00 100644 --- a/unittests/test_construction_factory.py +++ b/unittests/test_construction_factory.py @@ -122,7 +122,7 @@ class TestConstructionFactory(TestCase): self.assertIsNone(thermal_zone.ordinate_number, 'thermal_zone ordinate number is not none') self.assertIsNotNone(thermal_zone.view_factors_matrix, 'thermal_zone view factors matrix is none') self.assertIsNotNone(thermal_zone.total_floor_area, 'thermal zone total_floor_area is none') - self.assertIsNone(thermal_zone.usage, 'thermal_zone usage is not none') + self.assertIsNone(thermal_zone.usage_name, 'thermal_zone usage is not none') self.assertIsNone(thermal_zone.hours_day, 'thermal_zone hours a day is not none') self.assertIsNone(thermal_zone.days_year, 'thermal_zone days a year is not none') self.assertIsNone(thermal_zone.mechanical_air_change, 'thermal_zone mechanical air change is not none') diff --git a/unittests/test_enrichement.py b/unittests/test_enrichement.py index 1e7fc6d3..c63922f2 100644 --- a/unittests/test_enrichement.py +++ b/unittests/test_enrichement.py @@ -55,7 +55,7 @@ class TestGeometryFactory(TestCase): def _check_thermal_zone(self, thermal_zone): self.assertIsNotNone(thermal_zone.id, 'thermal_zone id is none') - self.assertIsNotNone(thermal_zone.usage, 'thermal_zone usage is not none') + self.assertIsNotNone(thermal_zone.usage_name, 'thermal_zone usage is not none') self.assertIsNotNone(thermal_zone.hours_day, 'thermal_zone hours a day is none') self.assertIsNotNone(thermal_zone.days_year, 'thermal_zone days a year is none') self.assertIsNotNone(thermal_zone.occupancy, 'thermal_zone occupancy is none') @@ -91,7 +91,7 @@ class TestGeometryFactory(TestCase): def _test_hft(self, file): _construction_keys = ['nrel'] - _usage_keys = ['comnet', 'hft'] + _usage_keys = ['comnet', 'nrcan'] for construction_key in _construction_keys: for usage_key in _usage_keys: # construction factory called first