From 2af8bf5db3fdc415aff2e72422d4bc48f3dd8edb Mon Sep 17 00:00:00 2001 From: Pilar Date: Mon, 25 Oct 2021 18:05:51 -0400 Subject: [PATCH] created customized imports factory and correspondent test --- .../sanam_customized_usage_parameters.py | 37 +++++++++++---- imports/customized_imports_factory.py | 10 ++-- imports/usage/ca_usage_parameters.py | 2 +- unittests/test_customized_imports_factory.py | 47 +++++++++++++++++++ 4 files changed, 83 insertions(+), 13 deletions(-) create mode 100644 unittests/test_customized_imports_factory.py diff --git a/imports/customized_imports/sanam_customized_usage_parameters.py b/imports/customized_imports/sanam_customized_usage_parameters.py index 58dbdef0..80d2e063 100644 --- a/imports/customized_imports/sanam_customized_usage_parameters.py +++ b/imports/customized_imports/sanam_customized_usage_parameters.py @@ -3,9 +3,11 @@ SanamCustomizedUsageParameters add two parameters to usage properties from ASHRA SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ -import sys +import sys +import xmltodict from imports.geometry.helpers.geometry_helper import GeometryHelper as gh +from imports.usage.data_classes.hft_usage_zone_archetype import HftUsageZoneArchetype as huza from city_model_structure.building_demand.usage_zone import UsageZone @@ -14,9 +16,17 @@ class SanamCustomizedUsageParameters: SanamCustomizedUsageParameters class """ def __init__(self, city, base_path): - super().__init__(base_path, 'ashrae_archetypes.xml') + file = 'ashrae_archetypes.xml' + path = str(base_path / 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) + self._city = city - self._usage_archetypes = None def enrich_buildings(self): """ @@ -25,20 +35,19 @@ class SanamCustomizedUsageParameters: """ city = self._city for building in city.buildings: - archetype = self._search_archetype(building.function) + archetype = self._search_archetype(building.function) # todo: building.function or other translation??????? if archetype is None: sys.stderr.write(f'Building {building.name} has unknown archetype for building function:' f' {building.function}, that assigns building usage as ' f'{gh.usage_from_function(building.function)}\n') continue - # todo: what to do with mix-usage usage from gml? mix_usage = False if not mix_usage: # just one usage_zone for thermal_zone in building.thermal_zones: usage_zone = UsageZone() - usage_zone.volume = thermal_zone.volume self._assign_values(usage_zone, archetype) + usage_zone.volume = thermal_zone.volume thermal_zone.usage_zones = [usage_zone] def _search_archetype(self, building_usage): @@ -50,6 +59,18 @@ class SanamCustomizedUsageParameters: @staticmethod def _assign_values(usage_zone, archetype): usage_zone.usage = archetype.usage + # Due to the fact that python is not a typed language, the wrong object type is assigned to + # usage_zone.internal_gains when writing usage_zone.internal_gains = archetype.internal_gains. + # Therefore, this walk around has been done. usage_zone.occupancy_density = archetype.occupancy_density - # todo: should I use this value: self._min_air_change?? - usage_zone.minimum_ventilation_rate = archetype.minimum_ventilation_rate + usage_zone.mechanical_air_change = archetype.mechanical_air_change + + @staticmethod + def _parse_zone_usage_type(usage, zone_usage_type): + occupancy_density = None + if 'occupancy' in zone_usage_type: + occupancy_density = zone_usage_type['occupancy']['occupancyDensity'] + mechanical_air_change = zone_usage_type['endUses']['ventilation']['minimumVentilationRate'] + usage_zone_archetype = huza(usage=usage, occupancy_density=occupancy_density, + mechanical_air_change=mechanical_air_change) + return usage_zone_archetype diff --git a/imports/customized_imports_factory.py b/imports/customized_imports_factory.py index 520cf853..bca8a87c 100644 --- a/imports/customized_imports_factory.py +++ b/imports/customized_imports_factory.py @@ -11,11 +11,11 @@ class CustomizedImportsFactory: """ CustomizedImportsFactory class """ - def __init__(self, city, importer_class, base_path): + def __init__(self, importer_class, city, base_path=None): if base_path is None: base_path = Path(Path(__file__).parent.parent / 'data/customized_imports') - self._city = city self._importer_class = importer_class + self._city = city self._base_path = base_path for building in city.buildings: if len(building.thermal_zones) == 0: @@ -24,6 +24,8 @@ class CustomizedImportsFactory: def enrich(self): """ - Enrich the city given to the class using the given importer class - :return: None + Returns the class that will enrich the city given + :return: Class """ + importer = self._importer_class(self._city, self._base_path) + return importer.enrich_buildings() diff --git a/imports/usage/ca_usage_parameters.py b/imports/usage/ca_usage_parameters.py index 1bbe5d58..01cc85a5 100644 --- a/imports/usage/ca_usage_parameters.py +++ b/imports/usage/ca_usage_parameters.py @@ -41,8 +41,8 @@ class CaUsageParameters(HftUsageInterface): # just one usage_zone for thermal_zone in building.thermal_zones: usage_zone = UsageZone() - usage_zone.volume = thermal_zone.volume self._assign_values(usage_zone, archetype) + usage_zone.volume = thermal_zone.volume thermal_zone.usage_zones = [usage_zone] def _search_archetype(self, building_usage): diff --git a/unittests/test_customized_imports_factory.py b/unittests/test_customized_imports_factory.py new file mode 100644 index 00000000..7b98784a --- /dev/null +++ b/unittests/test_customized_imports_factory.py @@ -0,0 +1,47 @@ +""" +TestCustomizedImportsFactory tests and validates the factory to import customized data +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2021 Project Author 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.construction_factory import ConstructionFactory +from imports.customized_imports_factory import CustomizedImportsFactory +from imports.customized_imports.sanam_customized_usage_parameters import SanamCustomizedUsageParameters as scp + + +class TestCustomizedImportsFactory(TestCase): + """ + TestCustomizedImportsFactory 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 + self.assertIsNotNone(_city, 'city is none') + ConstructionFactory('nrel', _city).enrich() + + return _city + + def test_city_with_customized_data(self): + """ + Enrich the city with the usage information and verify it + :return: None + """ + + file = 'one_building_in_kelowna.gml' + city = self._get_citygml(file) + + CustomizedImportsFactory(scp, city).enrich() + for building in city.buildings: + self.assertIsNot(len(building.usage_zones), 0, 'no building usage_zones defined') + for usage_zone in building.usage_zones: + self.assertIsNotNone(usage_zone.mechanical_air_change, 'usage is none')