From ef6afb0ab48abfa4f5ad094608b9481da11dee3a Mon Sep 17 00:00:00 2001 From: Pilar Date: Fri, 5 Nov 2021 10:16:35 -0400 Subject: [PATCH] created customized imports factory and correspondent test --- city_model_structure/building.py | 11 +++++ data/customized_imports/ashrae_archetypes.xml | 42 +++++++++---------- helpers/constants.py | 4 ++ helpers/enrich_city.py | 5 ++- imports/construction/us_physics_parameters.py | 1 + .../helpers/sanam_customized_usage_helper.py | 38 +++++++++++++++++ .../sanam_customized_usage_parameters.py | 34 ++++++++------- imports/geometry_factory.py | 30 ++++++------- unittests/test_customized_imports_factory.py | 2 + 9 files changed, 116 insertions(+), 51 deletions(-) create mode 100644 imports/customized_imports/helpers/sanam_customized_usage_helper.py diff --git a/city_model_structure/building.py b/city_model_structure/building.py index 9e144141..423d5ab8 100644 --- a/city_model_structure/building.py +++ b/city_model_structure/building.py @@ -174,6 +174,8 @@ class Building(CityObject): :return: [ThermalZone] """ if len(self._thermal_zones) == 0: + if self.storeys is None: + return [] for storey in self.storeys: self._thermal_zones.append(storey.thermal_zone) return self._thermal_zones @@ -194,6 +196,15 @@ class Building(CityObject): """ return self._year_of_construction + @year_of_construction.setter + def year_of_construction(self, value): + """ + Set building year of construction + :param value: int + """ + if value is not None: + self._year_of_construction = value + @property def function(self) -> Union[None, str]: """ diff --git a/data/customized_imports/ashrae_archetypes.xml b/data/customized_imports/ashrae_archetypes.xml index 17569df2..df281f3a 100644 --- a/data/customized_imports/ashrae_archetypes.xml +++ b/data/customized_imports/ashrae_archetypes.xml @@ -5,99 +5,99 @@ assembly - 0.15 + 0.15 - 5 + 5 health - 0.1 + 0.1 - 20 + 20 hotel - 0.11 + 0.11 - 5 + 5 manufacturing - 0.07 + 0.07 - 10 + 10 office - 0.05 + 0.05 - 5 + 5 restaurant - 0.7 + 0.7 - 7.5 + 7.5 retail - 0.2 + 0.2 - 7.5 + 7.5 school - 0.25 + 0.25 - 10 + 10 lab - 0.25 + 0.25 - 10 + 10 @@ -105,18 +105,18 @@ residential - 5 + 5 gymnasium - 0.3 + 0.3 - 7.5 + 7.5 diff --git a/helpers/constants.py b/helpers/constants.py index 6fdd14e0..8445fdfb 100644 --- a/helpers/constants.py +++ b/helpers/constants.py @@ -7,6 +7,10 @@ Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.mons # universal constants KELVIN = 273.15 +# converters +HOUR_TO_MINUTES = 60 +METERS_TO_FEET = 3.28084 + # time SECOND = 'second' MINUTE = 'minute' diff --git a/helpers/enrich_city.py b/helpers/enrich_city.py index e19bcf41..cc5067a1 100644 --- a/helpers/enrich_city.py +++ b/helpers/enrich_city.py @@ -55,10 +55,13 @@ class EnrichCity: def _construction(self, construction_format): ConstructionFactory(construction_format, self._city).enrich() + for building in self._city.buildings: # infiltration_rate_system_off is a mandatory parameter. # If it is not returned, extract the building from the calculation list - if building.thermal_zones[0].infiltration_rate_system_off is None: + if len(building.thermal_zones) == 0: + self._city.remove_city_object(building) + elif building.thermal_zones[0].infiltration_rate_system_off is None: self._city.remove_city_object(building) if self._city.buildings is None: self._errors.append('no archetype found per construction') diff --git a/imports/construction/us_physics_parameters.py b/imports/construction/us_physics_parameters.py index 50b0b8ca..f45e0f29 100644 --- a/imports/construction/us_physics_parameters.py +++ b/imports/construction/us_physics_parameters.py @@ -31,6 +31,7 @@ class UsPhysicsParameters(NrelPhysicsInterface): # it is assumed that all buildings have the same archetypes' keys for building in city.buildings: building_type = ConstructionHelper.nrel_from_function(building.function) + print(building_type) if building_type is None: return archetype = self._search_archetype(building_type, diff --git a/imports/customized_imports/helpers/sanam_customized_usage_helper.py b/imports/customized_imports/helpers/sanam_customized_usage_helper.py new file mode 100644 index 00000000..a0d981b8 --- /dev/null +++ b/imports/customized_imports/helpers/sanam_customized_usage_helper.py @@ -0,0 +1,38 @@ +""" +Sanam's customized importer Usage helper +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" +import sys +import helpers.constants as cte + + +class SanamCustomizedUsageHelper: + """ + SanamCustomizedUsage class + """ + usage_to_customized = { + cte.RESIDENTIAL: 'residential', + cte.INDUSTRY: 'manufacturing', + cte.OFFICE_ADMINISTRATION: 'office', + cte.HOTEL: 'hotel', + cte.HEALTH_CARE: 'health', + cte.RETAIL: 'retail', + cte.HALL: 'assembly', + cte.RESTAURANT: 'restaurant', + cte.EDUCATION: 'school' + } + customized_default_value = 'residential' + + @staticmethod + def customized_from_usage(usage): + """ + Get customized usage from the given internal usage key + :param usage: str + :return: str + """ + try: + return SanamCustomizedUsageHelper.usage_to_customized[usage] + except KeyError: + sys.stderr.write('Error: keyword not found. Returned default HfT usage "residential"\n') + return SanamCustomizedUsageHelper.customized_default_value diff --git a/imports/customized_imports/sanam_customized_usage_parameters.py b/imports/customized_imports/sanam_customized_usage_parameters.py index 80d2e063..6604a8e5 100644 --- a/imports/customized_imports/sanam_customized_usage_parameters.py +++ b/imports/customized_imports/sanam_customized_usage_parameters.py @@ -8,7 +8,7 @@ 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 +import helpers.constants as cte class SanamCustomizedUsageParameters: @@ -36,6 +36,11 @@ class SanamCustomizedUsageParameters: city = self._city for building in city.buildings: archetype = self._search_archetype(building.function) # todo: building.function or other translation??????? + height = building.average_storey_height + if height is None: + raise Exception('Average storey height not defined, ACH cannot be calculated') + if height <= 0: + raise Exception('Average storey height is zero, ACH cannot be calculated') 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 ' @@ -44,11 +49,8 @@ class SanamCustomizedUsageParameters: mix_usage = False if not mix_usage: # just one usage_zone - for thermal_zone in building.thermal_zones: - usage_zone = UsageZone() - self._assign_values(usage_zone, archetype) - usage_zone.volume = thermal_zone.volume - thermal_zone.usage_zones = [usage_zone] + for usage_zone in building.usage_zones: + self._assign_values(usage_zone, archetype, height) def _search_archetype(self, building_usage): for building_archetype in self._usage_archetypes: @@ -57,20 +59,24 @@ class SanamCustomizedUsageParameters: return None @staticmethod - def _assign_values(usage_zone, archetype): + def _assign_values(usage_zone, archetype, height): 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 - usage_zone.mechanical_air_change = archetype.mechanical_air_change + if archetype.occupancy_density is not None: + usage_zone.occupancy_density = archetype.occupancy_density + archetype_mechanical_air_change = float(archetype.mechanical_air_change) * float(usage_zone.occupancy_density) \ + * cte.HOUR_TO_MINUTES / cte.METERS_TO_FEET**3 / height + usage_zone.mechanical_air_change = archetype_mechanical_air_change @staticmethod def _parse_zone_usage_type(usage, zone_usage_type): - occupancy_density = None + mechanical_air_change = zone_usage_type['endUses']['ventilation']['minimumVentilationRate']['#text'] 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) + occupancy_density = zone_usage_type['occupancy']['occupancyDensity']['#text'] + usage_zone_archetype = huza(usage=usage, occupancy_density=occupancy_density, + mechanical_air_change=mechanical_air_change) + else: + usage_zone_archetype = huza(usage=usage, mechanical_air_change=mechanical_air_change) return usage_zone_archetype diff --git a/imports/geometry_factory.py b/imports/geometry_factory.py index d24199bf..da35c334 100644 --- a/imports/geometry_factory.py +++ b/imports/geometry_factory.py @@ -8,7 +8,7 @@ from city_model_structure.city import City from imports.geometry.citygml import CityGml from imports.geometry.obj import Obj from imports.geometry.osm_subway import OsmSubway -from imports.geometry.rhino import Rhino +#from imports.geometry.rhino import Rhino class GeometryFactory: @@ -43,13 +43,13 @@ class GeometryFactory: """ return OsmSubway(self._path).city - @property - def _rhino(self) -> City: - """ - Enrich the city by using OpenStreetMap information as data source - :return: City - """ - return Rhino(self._path).city +# @property +# def _rhino(self) -> City: +# """ +# Enrich the city by using OpenStreetMap information as data source +# :return: City +# """ +# return Rhino(self._path).city @property def city(self) -> City: @@ -59,10 +59,10 @@ class GeometryFactory: """ return getattr(self, self._file_type, lambda: None) - @property - def city_debug(self) -> City: - """ - Enrich the city given to the class using the class given handler - :return: City - """ - return Rhino(self._path).city +# @property +# def city_debug(self) -> City: +# """ +# Enrich the city given to the class using the class given handler +# :return: City +# """ +# return Rhino(self._path).city diff --git a/unittests/test_customized_imports_factory.py b/unittests/test_customized_imports_factory.py index 7b98784a..80373480 100644 --- a/unittests/test_customized_imports_factory.py +++ b/unittests/test_customized_imports_factory.py @@ -8,6 +8,7 @@ from unittest import TestCase from imports.geometry_factory import GeometryFactory from imports.construction_factory import ConstructionFactory +from imports.usage_factory import UsageFactory from imports.customized_imports_factory import CustomizedImportsFactory from imports.customized_imports.sanam_customized_usage_parameters import SanamCustomizedUsageParameters as scp @@ -28,6 +29,7 @@ class TestCustomizedImportsFactory(TestCase): _city = GeometryFactory('citygml', file_path).city self.assertIsNotNone(_city, 'city is none') ConstructionFactory('nrel', _city).enrich() + UsageFactory('hft', _city).enrich() return _city