diff --git a/hub/city_model_structure/building.py b/hub/city_model_structure/building.py index 307354ff..4e204065 100644 --- a/hub/city_model_structure/building.py +++ b/hub/city_model_structure/building.py @@ -47,6 +47,7 @@ class Building(CityObject): self._domestic_hot_water_heat_demand = dict() self._eave_height = None self._energy_systems = None + self._systems_archetype_name = None self._grounds = [] self._roofs = [] self._walls = [] @@ -468,3 +469,19 @@ class Building(CityObject): :param value: [GenericEnergySystem] """ self._energy_systems = value + + @property + def systems_archetype_name(self): + """ + Get systems archetype name + :return: str + """ + return self._systems_archetype_name + + @systems_archetype_name.setter + def systems_archetype_name(self, value): + """ + Set systems archetype name + :param value: str + """ + self._systems_archetype_name = value diff --git a/hub/city_model_structure/level_of_detail.py b/hub/city_model_structure/level_of_detail.py index 2c8665b9..273c60a3 100644 --- a/hub/city_model_structure/level_of_detail.py +++ b/hub/city_model_structure/level_of_detail.py @@ -16,6 +16,7 @@ class LevelOfDetail: self._usage = None self._weather = None self._surface_radiation = None + self._energy_systems = None @property def geometry(self): @@ -75,7 +76,7 @@ class LevelOfDetail: """ Set the city minimal weather level of detail, 0 (yearly), 1 (monthly), 2 (hourly) """ - self._usage = value + self._weather = value @property def surface_radiation(self): @@ -91,3 +92,18 @@ class LevelOfDetail: Set the city minimal surface radiation level of detail, 0 (yearly), 1 (monthly), 2 (hourly) """ self._surface_radiation = value + + @property + def energy_systems(self): + """ + Get the city minimal energy systems level of detail, 1 or 2 + :return: int + """ + return self._energy_systems + + @energy_systems.setter + def energy_systems(self, value): + """ + Set the city minimal energy systems level of detail, 1 or 2 + """ + self._energy_systems = value diff --git a/hub/imports/construction/nrcan_physics_parameters.py b/hub/imports/construction/nrcan_physics_parameters.py index 25867dfa..d46061cc 100644 --- a/hub/imports/construction/nrcan_physics_parameters.py +++ b/hub/imports/construction/nrcan_physics_parameters.py @@ -4,7 +4,6 @@ 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 datetime import math import sys from hub.hub_logger import get_logger @@ -24,9 +23,8 @@ class NrcanPhysicsParameters: """ NrcanPhysicsParameters class """ - def __init__(self, city, base_path, divide_in_storeys=False): + def __init__(self, city, divide_in_storeys=False): self._city = city - self._path = base_path self._divide_in_storeys = divide_in_storeys self._climate_zone = ConstructionHelper.city_to_nrcan_climate_zone(city.name) @@ -37,9 +35,8 @@ class NrcanPhysicsParameters: city = self._city nrcan_catalog = ConstructionCatalogFactory('nrcan').catalog for building in city.buildings: + function = Dictionaries().hub_function_to_nrcan_construction_function[building.function] try: - function = Dictionaries().hub_function_to_nrcan_construction_function[building.function] - archetype = self._search_archetype(nrcan_catalog, function, building.year_of_construction, self._climate_zone) except KeyError: @@ -47,7 +44,8 @@ class NrcanPhysicsParameters: f'{function} [{building.function}], building year of construction: {building.year_of_construction} ' f'and climate zone {self._climate_zone}\n') sys.stderr.write(f'Building {building.name} has unknown construction archetype for building function: ' - f'{function} [{building.function}], building year of construction: {building.year_of_construction} ' + f'{function} [{building.function}], ' + f'building year of construction: {building.year_of_construction} ' f'and climate zone {self._climate_zone}\n') continue diff --git a/hub/imports/construction/nrel_physics_parameters.py b/hub/imports/construction/nrel_physics_parameters.py index 4d74e4ea..c3dcbe07 100644 --- a/hub/imports/construction/nrel_physics_parameters.py +++ b/hub/imports/construction/nrel_physics_parameters.py @@ -23,9 +23,8 @@ class NrelPhysicsParameters: NrelPhysicsParameters class """ - def __init__(self, city, base_path, divide_in_storeys=False): + def __init__(self, city, divide_in_storeys=False): self._city = city - self._path = base_path self._divide_in_storeys = divide_in_storeys self._climate_zone = ConstructionHelper.city_to_nrel_climate_zone(city.name) @@ -36,17 +35,18 @@ class NrelPhysicsParameters: city = self._city nrel_catalog = ConstructionCatalogFactory('nrel').catalog for building in city.buildings: + function = Dictionaries().hub_function_to_nrel_construction_function[building.function] try: - function = Dictionaries().hub_function_to_nrel_construction_function[building.function] archetype = self._search_archetype(nrel_catalog, function, building.year_of_construction, self._climate_zone) except KeyError: logger.error(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') + f'{function} [{building.function}], building year of construction: {building.year_of_construction} ' + f'and climate zone {self._climate_zone}\n') 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') + f'{function} [{building.function}], ' + f'building year of construction: {building.year_of_construction} ' + f'and climate zone {self._climate_zone}\n') continue diff --git a/hub/imports/construction_factory.py b/hub/imports/construction_factory.py index f70dc0f9..f27d05f7 100644 --- a/hub/imports/construction_factory.py +++ b/hub/imports/construction_factory.py @@ -6,7 +6,6 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ -from pathlib import Path from hub.hub_logger import get_logger from hub.helpers.utils import validate_import_export_type from hub.imports.construction.nrel_physics_parameters import NrelPhysicsParameters @@ -19,9 +18,7 @@ class ConstructionFactory: """ ConstructionFactory class """ - def __init__(self, handler, city, base_path=None): - if base_path is None: - base_path = Path(Path(__file__).parent.parent / 'data/construction') + def __init__(self, handler, city): self._handler = '_' + handler.lower().replace(' ', '_') class_funcs = validate_import_export_type(ConstructionFactory) if self._handler not in class_funcs: @@ -29,20 +26,19 @@ class ConstructionFactory: logger.error(err_msg) raise Exception(err_msg) self._city = city - self._base_path = base_path def _nrel(self): """ Enrich the city by using NREL information """ - NrelPhysicsParameters(self._city, self._base_path).enrich_buildings() + NrelPhysicsParameters(self._city).enrich_buildings() self._city.level_of_detail.construction = 2 def _nrcan(self): """ Enrich the city by using NRCAN information """ - NrcanPhysicsParameters(self._city, self._base_path).enrich_buildings() + NrcanPhysicsParameters(self._city).enrich_buildings() self._city.level_of_detail.construction = 2 def enrich(self): @@ -57,4 +53,4 @@ class ConstructionFactory: Enrich the city given to the class using the class given handler :return: None """ - NrelPhysicsParameters(self._city, self._base_path).enrich_buildings() + NrelPhysicsParameters(self._city).enrich_buildings() diff --git a/hub/imports/energy_systems/montreal_custom_energy_system_parameters.py b/hub/imports/energy_systems/montreal_custom_energy_system_parameters.py new file mode 100644 index 00000000..43cadb36 --- /dev/null +++ b/hub/imports/energy_systems/montreal_custom_energy_system_parameters.py @@ -0,0 +1,98 @@ +""" +Montreal custom energy system importer +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2023 Concordia CERC group +Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" + +import sys + +from hub.hub_logger import get_logger +from hub.catalog_factories.energy_systems_catalog_factory import EnergySystemsCatalogFactory +from hub.city_model_structure.energy_systems.generic_energy_system import GenericEnergySystem +from hub.city_model_structure.energy_systems.generic_generation_system import GenericGenerationSystem +from hub.city_model_structure.energy_systems.generic_distribution_system import GenericDistributionSystem + +logger = get_logger() + + +class MontrealCustomEnergySystemParameters: + """ + MontrealCustomEnergySystemParameters class + """ + + def __init__(self, city): + self._city = city + + def enrich_buildings(self): + """ + Returns the city with the system parameters assigned to the buildings + :return: + """ + city = self._city + montreal_custom_catalog = EnergySystemsCatalogFactory('montreal_custom').catalog + for building in city.buildings: + archetype_name = building.systems_archetype_name + try: + archetype = self._search_archetypes(montreal_custom_catalog, archetype_name) + except KeyError: + logger.error(f'Building {building.name} has unknown usage archetype for usage: {archetype_name}') + sys.stderr.write(f'Building {building.name} has unknown usage archetype for usage: {archetype_name}') + continue + building_systems = [] + for equipment in archetype.equipments: + energy_system = GenericEnergySystem() + energy_system.demand_types = equipment.demand_types + _generation_system = GenericGenerationSystem() + archetype_generation_equipment = equipment.generation_system + # todo: calculate powers + _generation_system.heat_power = 0 + _generation_system.cooling_power = 0 + _generation_system.electricity_power = 0 + _generation_system.fuel_type = archetype_generation_equipment.fuel_type + _generation_system.source_types = archetype_generation_equipment.source_types + _generation_system.heat_efficiency = archetype_generation_equipment.heat_efficiency + _generation_system.cooling_efficiency = archetype_generation_equipment.cooling_efficiency + _generation_system.electricity_efficiency = archetype_generation_equipment.electricity_efficiency + _generation_system.source_temperature = archetype_generation_equipment.source_temperature + _generation_system.source_mass_flow = archetype_generation_equipment.source_mass_flow + if archetype_generation_equipment.storage: + # todo: calculate storage capacity in J + _generation_system.storage_capacity = 0 + archetype_auxiliary_equipment = archetype_generation_equipment.auxiliary_equipment + if archetype_auxiliary_equipment is not None: + _auxiliary_equipment = GenericGenerationSystem() + # todo: calculate powers + _auxiliary_equipment.heat_power = 0 + _auxiliary_equipment.cooling_power = 0 + _auxiliary_equipment.electricity_power = 0 + _auxiliary_equipment.fuel_type = archetype_auxiliary_equipment.fuel_type + _auxiliary_equipment.source_types = archetype_auxiliary_equipment.source_types + _auxiliary_equipment.heat_efficiency = archetype_auxiliary_equipment.heat_efficiency + _auxiliary_equipment.cooling_efficiency = archetype_auxiliary_equipment.cooling_efficiency + _auxiliary_equipment.electricity_efficiency = archetype_auxiliary_equipment.electricity_efficiency + _auxiliary_equipment.source_temperature = archetype_auxiliary_equipment.source_temperature + _auxiliary_equipment.source_mass_flow = archetype_auxiliary_equipment.source_mass_flow + _auxiliary_equipment.storage_capacity = 0 + _generation_system.auxiliary_equipment = _auxiliary_equipment + + energy_system.generation_system = _generation_system + + _distribution_system = GenericDistributionSystem() + archetype_distribution_equipment = equipment.distribution_system + _distribution_system.type = archetype_distribution_equipment.type + _distribution_system.supply_temperature = archetype_distribution_equipment.supply_temperature + _distribution_system.distribution_consumption = archetype_distribution_equipment.distribution_consumption + _distribution_system.heat_losses = archetype_distribution_equipment.heat_losses + + energy_system.distribution_system = _distribution_system + + building_systems.append(energy_system) + + @staticmethod + def _search_archetypes(catalog, name): + archetypes = catalog.entries('archetypes') + for building_archetype in archetypes: + if str(name) == str(building_archetype.name): + return building_archetype + raise KeyError('archetype not found') diff --git a/hub/imports/energy_systems_factory.py b/hub/imports/energy_systems_factory.py index 84cb4c1e..fb923300 100644 --- a/hub/imports/energy_systems_factory.py +++ b/hub/imports/energy_systems_factory.py @@ -10,6 +10,7 @@ from hub.imports.energy_systems.air_source_hp_parameters import AirSourceHeatPum from hub.imports.energy_systems.water_to_water_hp_parameters import WaterToWaterHPParameters from hub.helpers.utils import validate_import_export_type from hub.hub_logger import get_logger +from hub.imports.energy_systems.montreal_custom_energy_system_parameters import MontrealCustomEnergySystemParameters logger = get_logger() @@ -43,6 +44,13 @@ class EnergySystemsFactory: """ WaterToWaterHPParameters(self._city, self._base_path).enrich_city() + def _montreal_custom(self): + """ + Enrich the city by using montreal custom energy systems catalog information + """ + self._city.level_of_detail.energy_systems = 1 + MontrealCustomEnergySystemParameters(self._city).enrich_buildings() + def enrich(self): """ Enrich the city given to the class using the class given handler diff --git a/hub/imports/usage/comnet_usage_parameters.py b/hub/imports/usage/comnet_usage_parameters.py index 6ee24ec6..58c02d2d 100644 --- a/hub/imports/usage/comnet_usage_parameters.py +++ b/hub/imports/usage/comnet_usage_parameters.py @@ -28,9 +28,8 @@ class ComnetUsageParameters: """ ComnetUsageParameters class """ - def __init__(self, city, base_path): + def __init__(self, city): self._city = city - self._path = base_path def enrich_buildings(self): """ diff --git a/hub/imports/usage/nrcan_usage_parameters.py b/hub/imports/usage/nrcan_usage_parameters.py index 8073ca67..39563fce 100644 --- a/hub/imports/usage/nrcan_usage_parameters.py +++ b/hub/imports/usage/nrcan_usage_parameters.py @@ -25,9 +25,8 @@ class NrcanUsageParameters: """ NrcanUsageParameters class """ - def __init__(self, city, base_path): + def __init__(self, city): self._city = city - self._path = base_path def enrich_buildings(self): """ diff --git a/hub/imports/usage_factory.py b/hub/imports/usage_factory.py index 4871532c..9602166e 100644 --- a/hub/imports/usage_factory.py +++ b/hub/imports/usage_factory.py @@ -6,7 +6,7 @@ Copyright © 2022 Concordia CERC group Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ -from pathlib import Path + from hub.imports.usage.comnet_usage_parameters import ComnetUsageParameters from hub.imports.usage.nrcan_usage_parameters import NrcanUsageParameters from hub.hub_logger import get_logger @@ -19,9 +19,7 @@ class UsageFactory: """ UsageFactory class """ - def __init__(self, handler, city, base_path=None): - if base_path is None: - base_path = Path(Path(__file__).parent.parent / 'data/usage') + def __init__(self, handler, city): self._handler = '_' + handler.lower().replace(' ', '_') class_funcs = validate_import_export_type(UsageFactory) if self._handler not in class_funcs: @@ -29,21 +27,20 @@ class UsageFactory: logger.error(err_msg) raise Exception(err_msg) self._city = city - self._base_path = base_path def _comnet(self): """ Enrich the city with COMNET usage library """ self._city.level_of_detail.usage = 2 - ComnetUsageParameters(self._city, self._base_path).enrich_buildings() + ComnetUsageParameters(self._city).enrich_buildings() def _nrcan(self): """ Enrich the city with NRCAN usage library """ self._city.level_of_detail.usage = 2 - NrcanUsageParameters(self._city, self._base_path).enrich_buildings() + NrcanUsageParameters(self._city).enrich_buildings() def enrich(self): """ diff --git a/hub/unittests/test_systems_catalog.py b/hub/unittests/test_systems_catalog.py index da1f8d01..03702fbd 100644 --- a/hub/unittests/test_systems_catalog.py +++ b/hub/unittests/test_systems_catalog.py @@ -14,6 +14,9 @@ class TestSystemsCatalog(TestCase): def test_montreal_custom_catalog(self): catalog = EnergySystemsCatalogFactory('montreal_custom').catalog catalog_categories = catalog.names() + for archetype in catalog.entries('archetypes'): + for equipment in archetype.equipments: + print(equipment._equipment_id) archetypes = catalog.names('archetypes') self.assertEqual(18, len(archetypes['archetypes'])) equipments = catalog.names('equipments')