diff --git a/hub/catalog_factories/data_models/energy_systems/archetype.py b/hub/catalog_factories/data_models/energy_systems/archetype.py index 58b671b6..b770eae1 100644 --- a/hub/catalog_factories/data_models/energy_systems/archetype.py +++ b/hub/catalog_factories/data_models/energy_systems/archetype.py @@ -5,28 +5,17 @@ Copyright © 2023 Concordia CERC group Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ -from typing import Union +from typing import List -from hub.catalog_factories.data_models.energy_systems.generation_system import GenerationSystem -from hub.catalog_factories.data_models.energy_systems.distribution_system import DistributionSystem -from hub.catalog_factories.data_models.energy_systems.emission_system import EmissionSystem +from hub.catalog_factories.data_models.energy_systems.equipment import Equipment class Archetype: - def __init__(self, - lod, - name, - demand_types, - generation_system, - distribution_system, - emission_system): + def __init__(self, lod, name, equipments): self._lod = lod self._name = name - self._demand_types = demand_types - self._generation_system = generation_system - self._distribution_system = distribution_system - self._emission_system = emission_system + self._equipments = equipments @property def lod(self): @@ -45,33 +34,9 @@ class Archetype: return f'{self._name}_lod{self._lod}' @property - def demand_types(self): + def equipments(self) -> List[Equipment]: """ - Get demand able to cover from [heating, cooling, domestic_hot_water, electricity] - :return: [string] + Get list of equipments that compose the total energy system + :return: [Equipment] """ - return self._demand_types - - @property - def generation_system(self) -> GenerationSystem: - """ - Get generation system - :return: GenerationSystem - """ - return self._generation_system - - @property - def distribution_system(self) -> Union[None, DistributionSystem]: - """ - Get distribution system - :return: DistributionSystem - """ - return self._distribution_system - - @property - def emission_system(self) -> Union[None, EmissionSystem]: - """ - Get emission system - :return: EmissionSystem - """ - return self._emission_system + return self._equipments diff --git a/hub/catalog_factories/data_models/energy_systems/content.py b/hub/catalog_factories/data_models/energy_systems/content.py index 5a0b61f3..53c07efb 100644 --- a/hub/catalog_factories/data_models/energy_systems/content.py +++ b/hub/catalog_factories/data_models/energy_systems/content.py @@ -7,12 +7,20 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca class Content: - def __init__(self, archetypes): + def __init__(self, archetypes, equipments): self._archetypes = archetypes + self._equipments = equipments @property def archetypes(self): """ - All archetypes in the catalog + All archetype systems in the catalog """ return self._archetypes + + @property + def equipments(self): + """ + All equipments in the catalog + """ + return self._equipments diff --git a/hub/catalog_factories/data_models/energy_systems/equipment.py b/hub/catalog_factories/data_models/energy_systems/equipment.py new file mode 100644 index 00000000..9b2ddcd4 --- /dev/null +++ b/hub/catalog_factories/data_models/energy_systems/equipment.py @@ -0,0 +1,87 @@ +""" +Energy System catalog equipment +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2023 Concordia CERC group +Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" + +from typing import Union + +from hub.catalog_factories.data_models.energy_systems.generation_system import GenerationSystem +from hub.catalog_factories.data_models.energy_systems.distribution_system import DistributionSystem +from hub.catalog_factories.data_models.energy_systems.emission_system import EmissionSystem + + +class Equipment: + def __init__(self, + lod, + equipment_id, + name, + demand_types, + generation_system, + distribution_system, + emission_system): + + self._lod = lod + self._equipment_id = equipment_id + self._name = name + self._demand_types = demand_types + self._generation_system = generation_system + self._distribution_system = distribution_system + self._emission_system = emission_system + + @property + def lod(self): + """ + Get level of detail of the catalog + :return: string + """ + return self._lod + + @property + def id(self): + """ + Get equipment id + :return: string + """ + return self._equipment_id + + @property + def name(self): + """ + Get name + :return: string + """ + return f'{self._name}_lod{self._lod}' + + @property + def demand_types(self): + """ + Get demand able to cover from [heating, cooling, domestic_hot_water, electricity] + :return: [string] + """ + return self._demand_types + + @property + def generation_system(self) -> GenerationSystem: + """ + Get generation system + :return: GenerationSystem + """ + return self._generation_system + + @property + def distribution_system(self) -> Union[None, DistributionSystem]: + """ + Get distribution system + :return: DistributionSystem + """ + return self._distribution_system + + @property + def emission_system(self) -> Union[None, EmissionSystem]: + """ + Get emission system + :return: EmissionSystem + """ + return self._emission_system diff --git a/hub/catalog_factories/data_models/energy_systems/generation_system.py b/hub/catalog_factories/data_models/energy_systems/generation_system.py index b6d0eb3a..51a5325c 100644 --- a/hub/catalog_factories/data_models/energy_systems/generation_system.py +++ b/hub/catalog_factories/data_models/energy_systems/generation_system.py @@ -11,7 +11,7 @@ from typing import Union class GenerationSystem: def __init__(self, system_type, fuel_type, source_types, heat_efficiency, cooling_efficiency, electricity_efficiency, - source_temperature, source_mass_flow, storage_capacity, auxiliary_equipment): + source_temperature, source_mass_flow, storage, auxiliary_equipment): self._type = system_type self._fuel_type = fuel_type @@ -21,7 +21,7 @@ class GenerationSystem: self._electricity_efficiency = electricity_efficiency self._source_temperature = source_temperature self._source_mass_flow = source_mass_flow - self._storage_capacity = storage_capacity + self._storage = storage self._auxiliary_equipment = auxiliary_equipment @property @@ -89,12 +89,12 @@ class GenerationSystem: return self._source_mass_flow @property - def storage_capacity(self): + def storage(self): """ - Get storage_capacity in J - :return: float + Get boolean storage exists + :return: bool """ - return self._storage_capacity + return self._storage @property def auxiliary_equipment(self) -> Union[None, GenerationSystem]: diff --git a/hub/catalog_factories/energy_systems/montreal_custom_catalog.py b/hub/catalog_factories/energy_systems/montreal_custom_catalog.py index 2ac6daab..d42601b2 100644 --- a/hub/catalog_factories/energy_systems/montreal_custom_catalog.py +++ b/hub/catalog_factories/energy_systems/montreal_custom_catalog.py @@ -8,50 +8,127 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca import xmltodict from hub.catalog_factories.catalog import Catalog -from hub.catalog_factories.data_models.energy_systems.archetype import Archetype +from hub.catalog_factories.data_models.energy_systems.equipment import Equipment from hub.catalog_factories.data_models.energy_systems.content import Content +from hub.catalog_factories.data_models.energy_systems.generation_system import GenerationSystem +from hub.catalog_factories.data_models.energy_systems.distribution_system import DistributionSystem +from hub.catalog_factories.data_models.energy_systems.archetype import Archetype class MontrealCustomCatalog(Catalog): def __init__(self, path): path = str(path / 'montreal_custom_systems.xml') with open(path) as xml: - self._archetypes = xmltodict.parse(xml.read(), force_list='archetype') + self._archetypes = xmltodict.parse(xml.read(), force_list=('equipment', 'system', 'demand', 'equipment_id')) + + self._lod = float(self._archetypes['catalog']['@lod']) + + self._catalog_equipments = self._load_equipments() + self._catalog_archetypes = self._load_archetypes() # store the full catalog data model in self._content - self._content = Content(self._load_archetypes()) + self._content = Content(self._catalog_archetypes, + self._catalog_equipments) + + def _load_equipments(self): + _catalog_equipments = [] + equipments = self._archetypes['catalog']['equipments']['equipment'] + for equipment in equipments: + equipment_id = float(equipment['@id']) + equipment_type = equipment['@type'] + fuel_type = equipment['@fuel_type'] + name = equipment['name'] + demands = equipment['demands']['demand'] + heating_efficiency = None + if 'heating_efficiency' in equipment: + heating_efficiency = float(equipment['heating_efficiency']) + cooling_efficiency = None + if 'cooling_efficiency' in equipment: + cooling_efficiency = float(equipment['cooling_efficiency']) + electricity_efficiency = None + if 'electricity_efficiency' in equipment: + electricity_efficiency = float(equipment['electricity_efficiency']) + distribution_heat_losses = None + if 'distribution_heat_losses' in equipment: + distribution_heat_losses = float(equipment['distribution_heat_losses']['#text']) / 100 + distribution_consumption = None + if 'distribution_consumption' in equipment: + distribution_consumption = float(equipment['distribution_consumption']['#text']) / 100 + storage = eval(equipment['storage'].capitalize()) + generation_system = GenerationSystem(equipment_type, + fuel_type, + None, + heating_efficiency, + cooling_efficiency, + electricity_efficiency, + None, + None, + storage, + None) + distribution_system = DistributionSystem(None, + None, + distribution_consumption, + distribution_heat_losses) + _catalog_equipments.append(Equipment(self._lod, + equipment_id, + name, + demands, + generation_system, + distribution_system, + None)) + return _catalog_equipments def _load_archetypes(self): _catalog_archetypes = [] - archetypes = self._archetypes['archetypes']['archetype'] - for archetype in archetypes: - name = archetype['@name'] - lod = float(archetype['@lod']) - demand_types = archetype['@demands'] - _catalog_archetypes.append(Archetype(lod, - name, - demand_types, - generation_system, - distribution_system, - emission_system)) + systems = self._archetypes['catalog']['systems']['system'] + for system in systems: + name = system['@name'] + system_equipments = system['equipments']['equipment_id'] + _equipments = [] + for system_equipment in system_equipments: + for equipment in self._catalog_equipments: + if int(equipment.id) == int(system_equipment): + _equipments.append(equipment) + _catalog_archetypes.append(Archetype(self._lod, name, _equipments)) return _catalog_archetypes def names(self, category=None): """ Get the catalog elements names - :parm: for energy systems catalog category filter does nothing as there is only one category (archetypes) + :parm: optional category filter """ - _names = {'archetypes': []} - for archetype in self._content.archetypes: - _names['archetypes'].append(archetype.name) + if category is None: + _names = {'archetypes': [], 'equipments': []} + for archetype in self._content.archetypes: + _names['archetypes'].append(archetype.name) + for equipment in self._content.equipments: + _names['equipments'].append(equipment.name) + else: + _names = {category: []} + if category.lower() == 'archetypes': + for archetype in self._content.archetypes: + _names[category].append(archetype.name) + elif category.lower() == 'equipments': + for equipment in self._content.equipments: + _names[category].append(equipment.name) + else: + raise ValueError(f'Unknown category [{category}]') return _names def entries(self, category=None): """ Get the catalog elements - :parm: for energy systems catalog category filter does nothing as there is only one category (archetypes) + :parm: optional category filter """ - return self._content + if category is None: + return self._content + else: + if category.lower() == 'archetypes': + return self._content.archetypes + elif category.lower() == 'equipments': + return self._content.equipments + else: + raise ValueError(f'Unknown category [{category}]') def get_entry(self, name): """ @@ -61,4 +138,7 @@ class MontrealCustomCatalog(Catalog): for entry in self._content.archetypes: if entry.name.lower() == name.lower(): return entry + for entry in self._content.equipments: + if entry.name.lower() == name.lower(): + return entry raise IndexError(f"{name} doesn't exists in the catalog") diff --git a/hub/data/energy_systems/montreal_custom_systems.xml b/hub/data/energy_systems/montreal_custom_systems.xml index a7f21025..65c03767 100644 --- a/hub/data/energy_systems/montreal_custom_systems.xml +++ b/hub/data/energy_systems/montreal_custom_systems.xml @@ -30,7 +30,7 @@ 0.85 25 - NEEDED VALUE + 100000 false @@ -41,7 +41,7 @@ 1 25 - NEEDED VALUE + 100000 false @@ -73,7 +73,7 @@ 3.23 0 - NEEDED VALUE + 100000 false @@ -93,7 +93,7 @@ 3.23 0 - NEEDED VALUE + 100000 false @@ -214,7 +214,7 @@ 8 - + 6 8 diff --git a/hub/unittests/test_systems_catalog.py b/hub/unittests/test_systems_catalog.py new file mode 100644 index 00000000..da1f8d01 --- /dev/null +++ b/hub/unittests/test_systems_catalog.py @@ -0,0 +1,30 @@ +""" +TestSystemsCatalog +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 unittest import TestCase +from hub.catalog_factories.energy_systems_catalog_factory import EnergySystemsCatalogFactory + + +class TestSystemsCatalog(TestCase): + + def test_montreal_custom_catalog(self): + catalog = EnergySystemsCatalogFactory('montreal_custom').catalog + catalog_categories = catalog.names() + archetypes = catalog.names('archetypes') + self.assertEqual(18, len(archetypes['archetypes'])) + equipments = catalog.names('equipments') + self.assertEqual(10, len(equipments['equipments'])) + with self.assertRaises(ValueError): + catalog.names('unknown') + + # retrieving all the entries should not raise any exceptions + for category in catalog_categories: + for value in catalog_categories[category]: + catalog.get_entry(value) + + with self.assertRaises(IndexError): + catalog.get_entry('unknown')