diff --git a/hub/exports/building_energy/idf.py b/hub/exports/building_energy/idf.py index b4a63a7d..0c97cbdd 100644 --- a/hub/exports/building_energy/idf.py +++ b/hub/exports/building_energy/idf.py @@ -187,6 +187,8 @@ class Idf: def _add_infiltration_schedules(self, thermal_zone): _infiltration_schedules = [] + if thermal_zone.thermal_control is None: + return for hvac_availability_schedule in thermal_zone.thermal_control.hvac_availability_schedules: _schedule = Schedule() _schedule.type = cte.INFILTRATION @@ -337,13 +339,17 @@ class Idf: ) def _add_infiltration(self, thermal_zone, zone_name): + for zone in self._idf.idfobjects["ZONE"]: if zone.Name == f'{zone_name}_infiltration': return + schedule = f'Infiltration schedules {thermal_zone.usage_name}' + if schedule not in self._idf.idfobjects[self._HOURLY_SCHEDULE]: + return self._idf.newidfobject(self._INFILTRATION, Name=f'{zone_name}_infiltration', Zone_or_ZoneList_Name=zone_name, - Schedule_Name=f'Infiltration schedules {thermal_zone.usage_name}', + Schedule_Name=schedule, Design_Flow_Rate_Calculation_Method='AirChanges/Hour', Air_Changes_per_Hour=thermal_zone.mechanical_air_change ) @@ -378,6 +384,8 @@ class Idf: self._lod = self._city.level_of_detail.geometry for building in self._city.buildings: for internal_zone in building.internal_zones: + if internal_zone.thermal_zones is None: + continue for thermal_zone in internal_zone.thermal_zones: for thermal_boundary in thermal_zone.thermal_boundaries: self._add_construction(thermal_boundary) @@ -388,19 +396,26 @@ class Idf: usage = thermal_zone.usage_name if building.name in self._target_buildings or building.name in self._adjacent_buildings: self._add_infiltration_schedules(thermal_zone) - self._add_schedules(usage, 'Occupancy', thermal_zone.occupancy.occupancy_schedules) - self._add_schedules(usage, 'HVAC AVAIL', thermal_zone.thermal_control.hvac_availability_schedules) - self._add_schedules(usage, 'Heating thermostat', thermal_zone.thermal_control.heating_set_point_schedules) - self._add_schedules(usage, 'Cooling thermostat', thermal_zone.thermal_control.cooling_set_point_schedules) - self._add_people_activity_level_schedules(thermal_zone) + if thermal_zone.occupancy is not None: + self._add_schedules(usage, 'Occupancy', thermal_zone.occupancy.occupancy_schedules) + self._add_people_activity_level_schedules(thermal_zone) + self._add_occupancy(thermal_zone, building.name) + + if thermal_zone.thermal_control is not None: + self._add_schedules(usage, 'HVAC AVAIL', thermal_zone.thermal_control.hvac_availability_schedules) + self._add_schedules(usage, 'Heating thermostat', thermal_zone.thermal_control.heating_set_point_schedules) + self._add_schedules(usage, 'Cooling thermostat', thermal_zone.thermal_control.cooling_set_point_schedules) self._add_zone(thermal_zone, building.name) self._add_heating_system(thermal_zone, building.name) self._add_infiltration(thermal_zone, building.name) - self._add_occupancy(thermal_zone, building.name) + if self._export_type == "Surfaces": if building.name in self._target_buildings or building.name in self._adjacent_buildings: - self._add_surfaces(building, building.name) + if building.internal_zones[0].thermal_zones is not None: + self._add_surfaces(building, building.name) + else: + self._add_pure_geometry(building, building.name) else: self._add_shading(building) else: @@ -478,7 +493,34 @@ class Idf: Diffuse_Solar_Reflectance_of_Unglazed_Part_of_Shading_Surface=solar_reflectance, Fraction_of_Shading_Surface_That_Is_Glazed=0) - # todo: Add properties for that name + def _add_pure_geometry(self, building, zone_name): + for surface in building.surfaces: + idf_surface_type = self.idf_surfaces[surface.type] + outside_boundary_condition = 'Outdoors' + sun_exposure = 'SunExposed' + wind_exposure = 'WindExposed' + if surface.type == cte.GROUND: + outside_boundary_condition = 'Ground' + sun_exposure = 'NoSun' + wind_exposure = 'NoWind' + idf_surface = self._idf.newidfobject(self._SURFACE, Name=f'{surface.name}', + Surface_Type=idf_surface_type, + Zone_Name=zone_name, + Outside_Boundary_Condition=outside_boundary_condition, + Sun_Exposure=sun_exposure, + Wind_Exposure=wind_exposure) + coordinates = self._matrix_to_list(surface.solid_polygon.coordinates, + self._city.lower_corner) + idf_surface.setcoords(coordinates) + 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 + self._idf.set_wwr(wwr) def _add_surfaces(self, building, zone_name): for internal_zone in building.internal_zones: @@ -505,7 +547,6 @@ class Idf: Wind_Exposure=wind_exposure) coordinates = self._matrix_to_list(boundary.parent_surface.solid_polygon.coordinates, self._city.lower_corner) - surface.setcoords(coordinates) if self._lod >= 3: diff --git a/hub/unittests/test_exports.py b/hub/unittests/test_exports.py index 7cf35a4c..aad41374 100644 --- a/hub/unittests/test_exports.py +++ b/hub/unittests/test_exports.py @@ -106,13 +106,12 @@ class TestExports(TestCase): path=file_path, function_to_hub=Dictionaries().alkis_function_to_hub_function).city self.assertIsNotNone(city, 'city is none') - ConstructionFactory('nrcan', city=city) - ConstructionFactory('nrcan', city).enrich() + # EnergyBuildingsExportsFactory('idf', city, self._output_path).export() + # ConstructionFactory('nrcan', city).enrich() + # EnergyBuildingsExportsFactory('idf', city, self._output_path).export() UsageFactory('nrcan', city).enrich() try: 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!") diff --git a/hub/requirements.txt b/requirements.txt similarity index 100% rename from hub/requirements.txt rename to requirements.txt diff --git a/setup.py b/setup.py index 0053ffc1..fc8b3bd1 100644 --- a/setup.py +++ b/setup.py @@ -5,13 +5,12 @@ from distutils.util import convert_path import pkg_resources from setuptools import setup -with pathlib.Path('hub/requirements.txt').open() as r: +with pathlib.Path('requirements.txt').open() as r: install_requires = [ str(requirement) for requirement in pkg_resources.parse_requirements(r) ] - install_requires.append('setuptools') main_ns = {} @@ -82,7 +81,7 @@ setup( ], setup_requires=install_requires, data_files=[ - ('hub', glob.glob('hub/requirements.txt')), + ('hub', glob.glob('requirements.txt')), ('hub/config', glob.glob('hub/config/*.ini')), ('hub/catalog_factories/greenery/ecore_greenery', glob.glob('hub/catalog_factories/greenery/ecore_greenery/*.ecore')), ('hub/data/construction.', glob.glob('hub/data/construction/*')),