diff --git a/city_model_structure/city.py b/city_model_structure/city.py index 520d9e84..c8f9ea60 100644 --- a/city_model_structure/city.py +++ b/city_model_structure/city.py @@ -30,6 +30,7 @@ from helpers.location import Location from city_model_structure.energy_system import EnergySystem from city_model_structure.lca_material import LcaMaterial + class City: """ City class @@ -82,8 +83,6 @@ class City: if self._location is None: gps = pyproj.CRS('EPSG:4326') # LatLon with WGS84 datum used by GPS units and Google Earth try: - if self._srs_name in GeometryHelper.srs_transformations.keys(): - self._srs_name = GeometryHelper.srs_transformations[self._srs_name] input_reference = pyproj.CRS(self.srs_name) # Projected coordinate system from input data except pyproj.exceptions.CRSError: sys.stderr.write('Invalid projection reference system, please check the input data. ' @@ -108,9 +107,7 @@ class City: Get city name :return: str """ - if self._name is None: - return self._get_location().city - return self._name + return self._get_location().city @property def climate_reference_city(self) -> Union[None, str]: @@ -455,5 +452,5 @@ class City: return _merge_city @property - def level_of_detail(self) -> LevelOfDetail: + def level_of_detail(self): return self._level_of_detail diff --git a/city_model_structure/level_of_detail.py b/city_model_structure/level_of_detail.py index 5f6c8274..d673971c 100644 --- a/city_model_structure/level_of_detail.py +++ b/city_model_structure/level_of_detail.py @@ -5,6 +5,7 @@ Copyright © 2022 Concordia CERC group Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ + class LevelOfDetail: """ Level of detail for the city class @@ -17,41 +18,44 @@ class LevelOfDetail: @property def geometry(self): """ - Get the city minimal geometry level of detail + Get the city minimal geometry level of detail from 0 to 4 + :return: int """ return self._geometry @geometry.setter def geometry(self, value): """ - Set the city minimal geometry level of detail + Set the city minimal geometry level of detail from 0 to 4 """ self._geometry = value @property def construction(self): """ - Get the city minimal construction level of detail + Get the city minimal construction level of detail, 1 or 2 + :return: int """ return self._construction @construction.setter def construction(self, value): """ - Set the city minimal construction level of detail + Set the city minimal construction level of detail, 1 or 2 """ self._construction = value @property def usage(self): """ - Get the city minimal usage level of detail + Get the city minimal usage level of detail, 1 or 2 + :return: int """ return self._usage @usage.setter def usage(self, value): """ - Set the city minimal usage level of detail + Set the city minimal usage level of detail, 1 or 2 """ self._usage = value diff --git a/exports/building_energy/idf.py b/exports/building_energy/idf.py index 0d1ee196..912b9306 100644 --- a/exports/building_energy/idf.py +++ b/exports/building_energy/idf.py @@ -7,7 +7,6 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord Soroush Samareh Abolhassani soroush.samarehabolhassani@mail.concordia.ca """ import copy -import math from pathlib import Path from geomeppy import IDF import helpers.constants as cte @@ -57,10 +56,6 @@ class Idf: cte.GROUND: 'floor', cte.ROOF: 'roof' } - idf_usage = { - # todo: make an enum for all the usage types - cte.RESIDENTIAL: 'residential_building' - } idf_type_limits = { cte.ON_OFF: 'on/off', cte.FRACTION: 'Fraction', @@ -80,20 +75,6 @@ class Idf: cte.WINTER_DESIGN_DAY: 'WinterDesignDay', cte.SUMMER_DESIGN_DAY: 'SummerDesignDay' } - idf_schedule_types = { - 'compact': 'Compact', - cte.DAY: 'Day', - cte.WEEK: 'Week', - cte.YEAR: 'Year', - 'file': 'File' - } - idf_schedule_data_type = { - 'compact': 'Compact', - 'hourly': 'Hourly', - 'daily': 'Daily', - 'interval': 'Interval', - 'list': 'List', - } def __init__(self, city, output_path, idf_file_path, idd_file_path, epw_file_path, export_type="Surfaces", target_buildings=None, adjacent_buildings=None): @@ -395,6 +376,7 @@ class Idf: self._remove_location() self._remove_sizing_periods() self._rename_building(self._city.name) + self._lod = self._city.level_of_detail.geometry for building in self._city.buildings: for internal_zone in building.internal_zones: @@ -518,19 +500,22 @@ class Idf: self._city.lower_corner) surface.setcoords(coordinates) - self._add_windows(boundary) - def _add_windows(self, boundary): - for opening in boundary.thermal_openings: - for construction in self._idf.idfobjects[self._CONSTRUCTION]: - if construction['Outside_Layer'].split('_')[0] == 'glazing': - window_construction = construction - if self._compare_window_constructions(window_construction, opening): - opening_name = 'window_' + str(len(self._idf.idfobjects[self._WINDOW]) + 1) - opening_length = math.sqrt(opening.area) - self._idf.newidfobject(self._WINDOW, Name=f'{opening_name}', Construction_Name=window_construction['Name'], - Building_Surface_Name=boundary.parent_surface.name, Multiplier='1', - Length=opening_length, Height=opening_length) + if self._lod >= 3: + for internal_zone in building.internal_zones: + for thermal_zone in internal_zone.thermal_zones: + for boundary in thermal_zone.thermal_boundaries: + self._add_windows_by_vertices(boundary) + else: + # idf only allows setting wwr for external walls + wwr = 0 + for surface in building.surfaces: + if surface.type == cte.WALL: + wwr = surface.associated_thermal_boundaries[0].window_ratio + self._idf.set_wwr(wwr, construction='glazing_1') + + def _add_windows_by_vertices(self, boundary): + raise NotImplementedError def _compare_window_constructions(self, window_construction, opening): glazing = window_construction['Outside_Layer'] diff --git a/unittests/test_doe_idf.py b/unittests/test_doe_idf.py deleted file mode 100644 index 5981eae0..00000000 --- a/unittests/test_doe_idf.py +++ /dev/null @@ -1,57 +0,0 @@ -""" -Building test -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Concordia CERC group -Project Coder 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.construction_factory import ConstructionFactory -from exports.energy_building_exports_factory import EnergyBuildingsExportsFactory - - -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/one_building_in_kelowna.gml" - output_path = Path('../unittests/tests_outputs/').resolve() - city = GeometryFactory('citygml', path=city_file).city - for building in city.buildings: - building.year_of_construction = 2006 - ConstructionFactory('nrel', city).enrich() - UsageFactory('comnet', city).enrich() - EnergyBuildingsExportsFactory('idf', city, output_path).export() - - self.assertEqual(1, len(city.buildings)) - for building in city.buildings: - for internal_zone in building.internal_zones: - self.assertTrue(len(internal_zone.usage_zones) > 0) - for usage_zone in internal_zone.usage_zones: - self.assertIsNot(len(usage_zone.occupancy.occupancy_schedules), 0, 'no occupancy schedules defined') - for schedule in usage_zone.occupancy.occupancy_schedules: - self.assertIsNotNone(schedule.type) - self.assertIsNotNone(schedule.values) - self.assertIsNotNone(schedule.data_type) - self.assertIsNotNone(schedule.time_step) - self.assertIsNotNone(schedule.time_range) - self.assertIsNotNone(schedule.day_types) - self.assertIsNot(len(usage_zone.lighting.schedules), 0, 'no lighting schedules defined') - for schedule in usage_zone.lighting.schedules: - self.assertIsNotNone(schedule.type) - self.assertIsNotNone(schedule.values) - self.assertIsNotNone(schedule.data_type) - self.assertIsNotNone(schedule.time_step) - self.assertIsNotNone(schedule.time_range) - self.assertIsNotNone(schedule.day_types) diff --git a/unittests/test_exports.py b/unittests/test_exports.py index c33fa715..db564af2 100644 --- a/unittests/test_exports.py +++ b/unittests/test_exports.py @@ -14,6 +14,7 @@ from imports.geometry.helpers.geometry_helper import GeometryHelper from imports.construction_factory import ConstructionFactory from imports.usage_factory import UsageFactory from exports.exports_factory import ExportsFactory +from exports.energy_building_exports_factory import EnergyBuildingsExportsFactory import helpers.constants as cte from city_model_structure.city import City @@ -102,8 +103,9 @@ class TestExports(TestCase): ConstructionFactory('nrel', city).enrich() UsageFactory('comnet', city).enrich() try: - ExportsFactory('idf', city, self._output_path).export() - ExportsFactory('idf', city, self._output_path, target_buildings=['gml_1066158', 'gml_1066159']).export() + EnergyBuildingsExportsFactory('idf', city, self._output_path).export() + EnergyBuildingsExportsFactory('idf', city, self._output_path, + target_buildings=['gml_1066158', 'gml_1066159']).export() except Exception: self.fail("Idf ExportsFactory raised ExceptionType unexpectedly!")