From 5752c5d31d5e056dd02796ade206334f38703865 Mon Sep 17 00:00:00 2001 From: guille Date: Thu, 16 Sep 2021 13:45:27 -0400 Subject: [PATCH] Small corrections in doe_idf import --- city_model_structure/city.py | 4 +- city_model_structure/city_object.py | 11 +++-- data/schedules/doe_idf.xml | 32 +++++++-------- imports/schedules/doe_idf.py | 36 ++++++++-------- imports/schedules/helpers/schedules_helper.py | 33 +++++++++++++++ imports/schedules_factory.py | 2 +- unittests/test_doe_idf.py | 41 +++++++++++++++++++ 7 files changed, 116 insertions(+), 43 deletions(-) create mode 100644 unittests/test_doe_idf.py diff --git a/city_model_structure/city.py b/city_model_structure/city.py index 3b7e2cd2..4aada9c0 100644 --- a/city_model_structure/city.py +++ b/city_model_structure/city.py @@ -240,14 +240,14 @@ class City: """ Save a city into the given filename :param city_filename: destination city filename - :return: + :return: None """ with open(city_filename, 'wb') as file: pickle.dump(self, file) def region(self, center, radius) -> City: """ - Save a city into the given filename + Get a region from the city :param center: specific point in space [x, y, z] :param radius: distance to center of the sphere selected in meters :return: selected_region_city diff --git a/city_model_structure/city_object.py b/city_model_structure/city_object.py index 9b935e60..2147909b 100644 --- a/city_model_structure/city_object.py +++ b/city_model_structure/city_object.py @@ -7,7 +7,6 @@ Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc import math from typing import List, Union - from city_model_structure.iot.sensor import Sensor from city_model_structure.building_demand.surface import Surface from city_model_structure.attributes.polyhedron import Polyhedron @@ -38,7 +37,7 @@ class CityObject: self._sensors = [] @property - def lod(self): + def lod(self) -> int: """ Get city object level of detail 1, 2, 3 or 4 :return: int @@ -47,7 +46,7 @@ class CityObject: return lod @property - def type(self): + def type(self) -> str: """ Get city object type :return: str @@ -55,7 +54,7 @@ class CityObject: return self._type @property - def volume(self): + def volume(self) -> float: """ Get city object volume in cubic meters :return: float @@ -132,7 +131,7 @@ class CityObject: return self._centroid @property - def max_height(self): + def max_height(self) -> float: """ Get city object maximal height in meters :return: float @@ -140,7 +139,7 @@ class CityObject: return self.simplified_polyhedron.max_z @property - def external_temperature(self) -> dict: + def external_temperature(self) -> {float}: """ Get external temperature surrounding the city object in Celsius :return: dict{DataFrame(float)} diff --git a/data/schedules/doe_idf.xml b/data/schedules/doe_idf.xml index e2ed7086..949a57cc 100644 --- a/data/schedules/doe_idf.xml +++ b/data/schedules/doe_idf.xml @@ -2,82 +2,82 @@ - data/schedules/idf_files/ASHRAE901_ApartmentHighRise_STD2019_Rochester.idf + idf_files/ASHRAE901_OfficeSmall_STD2019_Rochester.idf - data/schedules/idf_files/ASHRAE901_ApartmentMidRise_STD2019_Rochester.idf + idf_files/ASHRAE901_ApartmentMidRise_STD2019_Rochester.idf - data/schedules/idf_files/ASHRAE901_Hospital_STD2019_Rochester.idf + idf_files/ASHRAE901_Hospital_STD2019_Rochester.idf - data/schedules/idf_files/ASHRAE901_HotelLarge_STD2019_Rochester.idf + idf_files/ASHRAE901_HotelLarge_STD2019_Rochester.idf - data/schedules/idf_files/ASHRAE901_HotelSmall_STD2019_Rochester.idf + idf_files/ASHRAE901_HotelSmall_STD2019_Rochester.idf - data/schedules/idf_files/ASHRAE901_OfficeLarge_STD2019_Rochester.idf + idf_files/ASHRAE901_OfficeLarge_STD2019_Rochester.idf - data/schedules/idf_files/ASHRAE901_OfficeMedium_STD2019_Rochester.idf + idf_files/ASHRAE901_OfficeMedium_STD2019_Rochester.idf - data/schedules/idf_files/ASHRAE901_OfficeSmall_STD2019_Rochester.idf + idf_files/ASHRAE901_OfficeSmall_STD2019_Rochester.idf - data/schedules/idf_files/ASHRAE901_OutPatientHealthCare_STD2019_Rochester.idf + idf_files/ASHRAE901_OutPatientHealthCare_STD2019_Rochester.idf - data/schedules/idf_files/ASHRAE901_RestaurantFastFood_STD2019_Rochester.idf + idf_files/ASHRAE901_RestaurantFastFood_STD2019_Rochester.idf - data/schedules/idf_files/ASHRAE901_RestaurantSitDown_STD2019_Rochester.idf + idf_files/ASHRAE901_RestaurantSitDown_STD2019_Rochester.idf - data/schedules/idf_files/ASHRAE901_RetailStandalone_STD2019_Rochester.idf + idf_files/ASHRAE901_RetailStandalone_STD2019_Rochester.idf - data/schedules/idf_files/ASHRAE901_RetailStripmall_STD2019_Rochester.idf + idf_files/ASHRAE901_RetailStripmall_STD2019_Rochester.idf - data/schedules/idf_files/ASHRAE901_SchoolPrimary_STD2019_Rochester.idf + idf_files/ASHRAE901_SchoolPrimary_STD2019_Rochester.idf - data/schedules/idf_files/ASHRAE901_SchoolSecondary_STD2019_Rochester.idf + idf_files/ASHRAE901_SchoolSecondary_STD2019_Rochester.idf - data/schedules/idf_files/ASHRAE901_Warehouse_STD2019_Rochester.idf + idf_files/ASHRAE901_Warehouse_STD2019_Rochester.idf diff --git a/imports/schedules/doe_idf.py b/imports/schedules/doe_idf.py index 8c434850..c29b80dc 100644 --- a/imports/schedules/doe_idf.py +++ b/imports/schedules/doe_idf.py @@ -6,6 +6,7 @@ Copyright © 2020 Project import pandas as pd import parseidf import xmltodict +from imports.schedules.helpers.schedules_helper import SchedulesHelper class DoeIdf: @@ -21,7 +22,7 @@ class DoeIdf: _SCHEDULE_COMPACT_TYPE = 'SCHEDULE:COMPACT' _SCHEDULE_TYPE_NAME = 1 - def __init__(self, city, base_path): + def __init__(self, city, base_path, doe_idf_file): self._hours = [] panda_hours = pd.timedelta_range(0, periods=24, freq='H') for _, hour in enumerate(panda_hours): @@ -29,20 +30,22 @@ class DoeIdf: self._city = city path = str(base_path / doe_idf_file) + print(path) with open(path) as xml: - self._library = xmltodict.parse(xml.read()) - for archetype in self._library['archetypes']['archetypes']: - self._archetype_keys = {} - for key, value in archetype.items(): - if key[0] == '@': - archetype_keys[key] = value + self._schedule_library = xmltodict.parse(xml.read()) - self._idf_schedules_path = archetype['idf']['path'] - with open(self._idf_schedules_path, 'r') as file: - idf = parseidf.parse(file.read()) - self._load_schedule(idf, archetype_keys['@building_type']) + for building in self._city.buildings: + for usage_zone in building.usage_zones: + for schedule_archetype in self._schedule_library['archetypes']['archetypes']: + function = schedule_archetype['@building_type'] + if SchedulesHelper.usage_from_function(function) == usage_zone.usage: + self._idf_schedules_path = (base_path / schedule_archetype['idf']['path']).resolve() + with open(self._idf_schedules_path, 'r') as file: + idf = parseidf.parse(file.read()) + self._load_schedule(idf, usage_zone) + break - def _load_schedule(self, idf, building_usage): + def _load_schedule(self, idf, usage_zone): schedules_day = {} for compact_schedule in idf[self._SCHEDULE_COMPACT_TYPE]: if compact_schedule[self._SCHEDULE_TYPE_NAME] in self.idf_schedule_to_comnet_schedule: @@ -116,9 +119,6 @@ class DoeIdf: else: continue df = pd.DataFrame(data, index=rows) - for building in self._city.buildings: - for usage_zone in building.usage_zones: - if usage_zone.usage == building_usage: - if usage_zone.schedules is None: - usage_zone.schedules = {} - usage_zone.schedules[schedule_type] = df + if usage_zone.schedules is None: + usage_zone.schedules = {} + usage_zone.schedules[schedule_type] = df diff --git a/imports/schedules/helpers/schedules_helper.py b/imports/schedules/helpers/schedules_helper.py index ac459452..a36c2fa6 100644 --- a/imports/schedules/helpers/schedules_helper.py +++ b/imports/schedules/helpers/schedules_helper.py @@ -31,6 +31,30 @@ class SchedulesHelper: 'Temperature': cte.TEMPERATURE } + # usage + function_to_usage = { + 'full service restaurant': 'restaurant', + 'high-rise apartment': cte.RESIDENTIAL, + 'hospital': 'health care', + 'large hotel': 'hotel', + 'large office': 'office and administration', + 'medium office': 'office and administration', + 'midrise apartment': cte.RESIDENTIAL, + 'outpatient healthcare': 'health care', + 'primary school': 'education', + 'quick service restaurant': 'restaurant', + 'secondary school': 'education', + 'small hotel': 'hotel', + 'small office': 'office and administration', + 'stand-alone-retail': 'retail', + 'strip mall': 'hall', + 'supermarket': 'retail', + 'warehouse': 'industry', + 'residential': cte.RESIDENTIAL + } + + + @staticmethod def comnet_from_usage(usage): """ @@ -55,3 +79,12 @@ class SchedulesHelper: return SchedulesHelper.comnet_to_data_type[comnet_data_type] except KeyError: raise ValueError(f"Error: comnet data type keyword not found.") + + @staticmethod + def usage_from_function(building_function): + """ + Get the internal usage for the given internal building function + :param building_function: str + :return: str + """ + return SchedulesHelper.function_to_usage[building_function] diff --git a/imports/schedules_factory.py b/imports/schedules_factory.py index ea1634ac..d501a6d1 100644 --- a/imports/schedules_factory.py +++ b/imports/schedules_factory.py @@ -27,7 +27,7 @@ class SchedulesFactory: ComnetSchedules(self._city, self._base_path) def _doe_idf(self): - DoeIdf(self._city, self._base_path) + DoeIdf(self._city, self._base_path, 'doe_idf.xml') def enrich(self): """ diff --git a/unittests/test_doe_idf.py b/unittests/test_doe_idf.py new file mode 100644 index 00000000..8c930656 --- /dev/null +++ b/unittests/test_doe_idf.py @@ -0,0 +1,41 @@ +""" +Building test +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Guille Gutierrez Morote Guillermo.GutierrezMorote@concordia.ca +""" +from pathlib import Path +from unittest import TestCase +from imports.geometry_factory import GeometryFactory +from imports.usage_factory import UsageFactory +from imports.schedules_factory import SchedulesFactory +from imports.construction_factory import ConstructionFactory +from exports.exports_factory import ExportsFactory + + +class TestBuildings(TestCase): + """ + TestBuilding TestCase 1 + """ + def setUp(self) -> None: + """ + Test setup + :return: None + """ + self._city_gml = None + self._example_path = (Path(__file__).parent / 'tests_data').resolve() + + def test_doe_idf(self): + city_file = "../unittests/tests_data/C40_Final.gml" + output_path = Path('../unittests/tests_outputs/').resolve() + city = GeometryFactory('citygml', city_file).city + ConstructionFactory('nrel', city).enrich() + UsageFactory('ca', city).enrich() + SchedulesFactory('doe_idf', city).enrich() + ExportsFactory('idf', city, output_path).export() + self.assertEqual(10, len(city.buildings)) + for building in city.buildings: + self.assertTrue(len(building.usage_zones) > 0) + for usage_zone in building.usage_zones: + self.assertTrue('Lights' in usage_zone.schedules) + +