From 7488b6ba91ef646d3a56d45ac0adac273562abbb Mon Sep 17 00:00:00 2001 From: Pilar Date: Thu, 24 Mar 2022 16:51:01 -0400 Subject: [PATCH] Major changes with two objectives: homogenize the usage parameters regardless the data source, and completely uncouple geometry and construction library (before the geometry was modified after reading storey high from construction library), and construction and usage libraries (before the usage library could only be read after construction library, now ant order is accepted). --- city_model_structure/building.py | 17 -- helpers/constants.py | 60 ++++--- imports/construction/ca_physics_parameters.py | 21 +-- .../helpers/construction_helper.py | 65 ++++--- .../helpers/storeys_generation.py | 61 ++++--- .../construction/nrel_physics_interface.py | 50 ++++++ imports/construction/us_physics_parameters.py | 19 +-- .../helpers/sanam_customized_usage_helper.py | 2 +- .../sanam_customized_usage_parameters.py | 92 +++++----- imports/customized_imports_factory.py | 4 - imports/geometry/helpers/geometry_helper.py | 158 ++++++++++-------- imports/schedules/doe_idf.py | 31 ++-- imports/schedules/helpers/schedules_helper.py | 14 +- imports/schedules_factory.py | 15 +- imports/usage/ca_usage_parameters.py | 9 +- imports/usage/comnet_usage_parameters.py | 10 +- imports/usage/helpers/usage_helper.py | 59 +++++-- imports/usage/hft_usage_interface.py | 5 +- imports/usage/hft_usage_parameters.py | 29 ++-- recognized_functions_and_usages.md | 64 +++++++ unittests/test_construction_factory.py | 11 +- unittests/test_customized_imports_factory.py | 12 +- unittests/test_doe_idf.py | 31 ++-- unittests/test_enrichement.py | 78 +++++---- unittests/test_exports.py | 5 +- unittests/test_geometry_factory.py | 7 +- unittests/test_schedules_factory.py | 45 +++-- unittests/test_usage_factory.py | 40 ++--- 28 files changed, 603 insertions(+), 411 deletions(-) create mode 100644 recognized_functions_and_usages.md diff --git a/city_model_structure/building.py b/city_model_structure/building.py index a94e55a6..bab214a1 100644 --- a/city_model_structure/building.py +++ b/city_model_structure/building.py @@ -8,7 +8,6 @@ contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca from typing import List, Union import numpy as np from city_model_structure.building_demand.surface import Surface -from city_model_structure.building_demand.storey import Storey from city_model_structure.city_object import CityObject from city_model_structure.building_demand.household import Household from city_model_structure.building_demand.internal_zone import InternalZone @@ -265,22 +264,6 @@ class Building(CityObject): self._eave_height = max(self._eave_height, wall.upper_corner[2]) return self._eave_height - @property - def storeys(self) -> List[Storey]: - """ - Get building storeys - :return: [Storey] - """ - return self._storeys - - @storeys.setter - def storeys(self, value): - """ - Set building storeys - :param value: [Storey] - """ - self._storeys = value - @property def roof_type(self): """ diff --git a/helpers/constants.py b/helpers/constants.py index 684a367f..b4291102 100644 --- a/helpers/constants.py +++ b/helpers/constants.py @@ -58,31 +58,47 @@ WINDOW = 'Window' DOOR = 'Door' SKYLIGHT = 'Skylight' -# todo: homogenize function and usage!! -# function -RESIDENTIAL = 'residential' -SFH = 'single family house' -MFH = 'multifamily house' -HOTEL = 'hotel' -HOSPITAL = 'hospital' -OUTPATIENT = 'outpatient' -COMMERCIAL = 'commercial' -STRIP_MALL = 'strip mall' -WAREHOUSE = 'warehouse' +# functions and usages +SINGLE_FAMILY_HOUSE = 'single family house' +MULTI_FAMILY_HOUSE = 'multifamily house' +ROW_HOSE = 'row house' +MID_RISE_APARTMENT = 'mid rise apartment' +HIGH_RISE_APARTMENT = 'high rise apartment' +SMALL_OFFICE = 'small office' +MEDIUM_OFFICE = 'medium office' +LARGE_OFFICE = 'large office' PRIMARY_SCHOOL = 'primary school' SECONDARY_SCHOOL = 'secondary school' -OFFICE = 'office' -LARGE_OFFICE = 'large office' -OFFICE_WORKSHOP = 'office/workshop' - -# usage -INDUSTRY = 'industry' -OFFICE_ADMINISTRATION = 'office and administration' -HEALTH_CARE = 'health care' -RETAIL = 'retail' -HALL = 'hall' -RESTAURANT = 'restaurant' +STAND_ALONE_RETAIL = 'stand alone retail' +HOSPITAL = 'hospital' +OUT_PATIENT_HEALTH_CARE = 'out-patient health care' +STRIP_MALL = 'strip mall' +SUPERMARKET = 'supermarket' +WAREHOUSE = 'warehouse' +QUICK_SERVICE_RESTAURANT = 'quick service restaurant' +FULL_SERVICE_RESTAURANT = 'full service restaurant' +SMALL_HOTEL = 'small hotel' +LARGE_HOTEL = 'large hotel' +RESIDENTIAL = 'residential' EDUCATION = 'education' +SCHOOL_WITHOUT_SHOWER = 'school without shower' +SCHOOL_WITH_SHOWER = 'school with shower' +RETAIL_SHOP_WITHOUT_REFRIGERATED_FOOD = 'retail shop without refrigerated food' +RETAIL_SHOP_WITH_REFRIGERATED_FOOD = 'retail shop with refrigerated food' +HOTEL = 'hotel' +HOTEL_MEDIUM_CLASS = 'hotel medium class' +DORMITORY = 'dormitory' +INDUSTRY = 'industry' +RESTAURANT = 'restaurant' +HEALTH_CARE = 'health care' +RETIREMENT_HOME_OR_ORPHANAGE = 'retirement home or orphanage' +OFFICE_AND_ADMINISTRATION = 'office and administration' +EVENT_LOCATION = 'event location' +HALL = 'hall' +SPORTS_LOCATION = 'sports location' +LABOR = 'labor' +GREEN_HOUSE = 'green house' +NON_HEATED = 'non-heated' LIGHTING = 'Lights' OCCUPANCY = 'Occupancy' diff --git a/imports/construction/ca_physics_parameters.py b/imports/construction/ca_physics_parameters.py index 5b2ff50c..6f4120fc 100644 --- a/imports/construction/ca_physics_parameters.py +++ b/imports/construction/ca_physics_parameters.py @@ -6,7 +6,6 @@ Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.mons import sys from imports.construction.helpers.construction_helper import ConstructionHelper from imports.construction.nrel_physics_interface import NrelPhysicsInterface -from imports.construction.helpers.storeys_generation import StoreysGeneration class CaPhysicsParameters(NrelPhysicsInterface): @@ -26,11 +25,11 @@ class CaPhysicsParameters(NrelPhysicsInterface): # it is assumed that all buildings have the same archetypes' keys for building in city.buildings: try: - archetype = self._search_archetype(ConstructionHelper.nrcan_from_function(building.function), + archetype = self._search_archetype(ConstructionHelper.nrcan_from_libs_function(building.function), building.year_of_construction) except KeyError: sys.stderr.write(f'Building {building.name} has unknown archetype for building function: ' - f'{ConstructionHelper.nrcan_from_function(building.function)} ' + f'{ConstructionHelper.nrcan_from_libs_function(building.function)} ' f'and building year of construction: {building.year_of_construction}\n') return @@ -38,12 +37,11 @@ class CaPhysicsParameters(NrelPhysicsInterface): if len(building.internal_zones) == 1: if building.internal_zones[0].thermal_zones is None: self._create_storeys(building, archetype) - thermal_zones = [] - for storey in building.storeys: - thermal_zones.append(storey.thermal_zone) - building.internal_zones[0].thermal_zones = thermal_zones self._assign_values(building.internal_zones, archetype) + for internal_zone in building.internal_zones: + for thermal_zone in internal_zone.thermal_zones: + self._calculate_view_factors(thermal_zone) def _search_archetype(self, function, year_of_construction): for building_archetype in self._building_archetypes: @@ -82,12 +80,3 @@ class CaPhysicsParameters(NrelPhysicsInterface): thermal_opening.frame_ratio = thermal_opening_archetype.frame_ratio thermal_opening.g_value = thermal_opening_archetype.g_value thermal_opening.overall_u_value = thermal_opening_archetype.overall_u_value - - @staticmethod - def _create_storeys(building, archetype): - building.average_storey_height = archetype.average_storey_height - building.storeys_above_ground = archetype.storeys_above_ground - storeys_generation = StoreysGeneration(building) - storeys = storeys_generation.storeys - building.storeys = storeys - storeys_generation.assign_thermal_zones_delimited_by_thermal_boundaries() diff --git a/imports/construction/helpers/construction_helper.py b/imports/construction/helpers/construction_helper.py index 32402984..ba93d614 100644 --- a/imports/construction/helpers/construction_helper.py +++ b/imports/construction/helpers/construction_helper.py @@ -14,18 +14,26 @@ class ConstructionHelper: # NREL _function_to_nrel = { cte.RESIDENTIAL: 'residential', - cte.SFH: 'single family house', - cte.MFH: 'multifamily house', - cte.HOTEL: 'hotel', - cte.HOSPITAL: 'hospital', - cte.OUTPATIENT: 'outpatient', - cte.COMMERCIAL: 'commercial', - cte.STRIP_MALL: 'strip mall', - cte.WAREHOUSE: 'warehouse', + cte.SINGLE_FAMILY_HOUSE: 'residential', + cte.MULTI_FAMILY_HOUSE: 'residential', + cte.ROW_HOSE: 'residential', + cte.MID_RISE_APARTMENT: 'midrise apartment', + cte.HIGH_RISE_APARTMENT: 'high-rise apartment', + cte.SMALL_OFFICE: 'small office', + cte.MEDIUM_OFFICE: 'medium office', + cte.LARGE_OFFICE: 'large office', cte.PRIMARY_SCHOOL: 'primary school', cte.SECONDARY_SCHOOL: 'secondary school', - cte.OFFICE: 'office', - cte.LARGE_OFFICE: 'large office' + cte.STAND_ALONE_RETAIL: 'stand-alone retail', + cte.HOSPITAL: 'hospital', + cte.OUT_PATIENT_HEALTH_CARE: 'outpatient healthcare', + cte.STRIP_MALL: 'strip mall', + cte.SUPERMARKET: 'supermarket', + cte.WAREHOUSE: 'warehouse', + cte.QUICK_SERVICE_RESTAURANT: 'quick service restaurant', + cte.FULL_SERVICE_RESTAURANT: 'full service restaurant', + cte.SMALL_HOTEL: 'small hotel', + cte.LARGE_HOTEL: 'large hotel' } _nrel_standards = { @@ -65,19 +73,26 @@ class ConstructionHelper: # NRCAN _function_to_nrcan = { cte.RESIDENTIAL: 'residential', - cte.SFH: 'single family house', - cte.MFH: 'multifamily house', - cte.HOTEL: 'hotel', - cte.HOSPITAL: 'hospital', - cte.OUTPATIENT: 'outpatient', - cte.COMMERCIAL: 'commercial', - cte.STRIP_MALL: 'strip mall', - cte.WAREHOUSE: 'warehouse', - cte.PRIMARY_SCHOOL: 'primary school', - cte.SECONDARY_SCHOOL: 'secondary school', - cte.OFFICE: 'office', - cte.LARGE_OFFICE: 'large office', - cte.OFFICE_WORKSHOP: 'residential' + cte.SINGLE_FAMILY_HOUSE: 'residential', + cte.MULTI_FAMILY_HOUSE: 'residential', + cte.ROW_HOSE: 'residential', + cte.MID_RISE_APARTMENT: 'residential', + cte.HIGH_RISE_APARTMENT: 'residential', + cte.SMALL_OFFICE: cte.SMALL_OFFICE, + cte.MEDIUM_OFFICE: cte.MEDIUM_OFFICE, + cte.LARGE_OFFICE: cte.LARGE_OFFICE, + cte.PRIMARY_SCHOOL: cte.PRIMARY_SCHOOL, + cte.SECONDARY_SCHOOL: cte.SECONDARY_SCHOOL, + cte.STAND_ALONE_RETAIL: cte.STAND_ALONE_RETAIL, + cte.HOSPITAL: cte.HOSPITAL, + cte.OUT_PATIENT_HEALTH_CARE: cte.OUT_PATIENT_HEALTH_CARE, + cte.STRIP_MALL: cte.STRIP_MALL, + cte.SUPERMARKET: cte.SUPERMARKET, + cte.WAREHOUSE: cte.WAREHOUSE, + cte.QUICK_SERVICE_RESTAURANT: cte.QUICK_SERVICE_RESTAURANT, + cte.FULL_SERVICE_RESTAURANT: cte.FULL_SERVICE_RESTAURANT, + cte.SMALL_HOTEL: cte.SMALL_HOTEL, + cte.LARGE_HOTEL: cte.LARGE_HOTEL } nrcan_window_types = [cte.WINDOW] @@ -92,7 +107,7 @@ class ConstructionHelper: } @staticmethod - def nrel_from_function(function): + def nrel_from_libs_function(function): """ Get NREL function from the given internal function key :param function: str @@ -140,7 +155,7 @@ class ConstructionHelper: return ConstructionHelper._reference_city_to_nrel_climate_zone[reference_city] @staticmethod - def nrcan_from_function(function): + def nrcan_from_libs_function(function): """ Get NREL function from the given internal function key :param function: str diff --git a/imports/construction/helpers/storeys_generation.py b/imports/construction/helpers/storeys_generation.py index b67a820d..da0b4dcc 100644 --- a/imports/construction/helpers/storeys_generation.py +++ b/imports/construction/helpers/storeys_generation.py @@ -6,12 +6,14 @@ Copyright © 2022 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.mons import sys import math import numpy as np +from typing import List from helpers import constants as cte from city_model_structure.attributes.polygon import Polygon from city_model_structure.attributes.point import Point from city_model_structure.building_demand.storey import Storey from city_model_structure.building_demand.surface import Surface +from city_model_structure.building_demand.thermal_zone import ThermalZone class StoreysGeneration: @@ -20,14 +22,14 @@ class StoreysGeneration: """ def __init__(self, building, divide_in_storeys=False): self._building = building + self._thermal_zones = [] self._divide_in_storeys = divide_in_storeys - self._storeys = None self._floor_area = 0 for ground in building.grounds: self._floor_area += ground.perimeter_polygon.area @property - def storeys(self) -> [Storey]: + def thermal_zones(self) -> List[ThermalZone]: """ Get subsections of building trimesh by storey in case of no interiors defined :return: [Storey] @@ -37,7 +39,21 @@ class StoreysGeneration: self._building.storeys_above_ground) number_of_storeys = 1 if not self._divide_in_storeys or number_of_storeys == 1: - return [Storey('storey_0', self._building.surfaces, [None, None], self._building.volume, self._floor_area)] + storey = Storey('storey_0', self._building.surfaces, [None, None], self._building.volume, self._floor_area) + for thermal_boundary in storey.thermal_boundaries: + if thermal_boundary.type != cte.INTERIOR_WALL or thermal_boundary.type != cte.INTERIOR_SLAB: + # external thermal boundary -> only one thermal zone + thermal_zones = [storey.thermal_zone] + else: + # internal thermal boundary -> two thermal zones + grad = np.rad2deg(thermal_boundary.inclination) + if grad >= 170: + thermal_zones = [storey.thermal_zone, storey.neighbours[0]] + else: + thermal_zones = [storey.neighbours[1], storey.thermal_zone] + thermal_boundary.thermal_zones = thermal_zones + + return [storey.thermal_zone] if number_of_storeys == 0: raise Exception('Number of storeys cannot be 0') @@ -89,7 +105,25 @@ class StoreysGeneration: if volume < 0: raise Exception('Error in storeys creation, volume of last storey cannot be lower that 0') storeys.append(Storey(name, surfaces_child_last_storey, neighbours, volume, self._floor_area)) - return storeys + + for storey in storeys: + for thermal_boundary in storey.thermal_boundaries: + if thermal_boundary.type != cte.INTERIOR_WALL or thermal_boundary.type != cte.INTERIOR_SLAB: + # external thermal boundary -> only one thermal zone + thermal_zones = [storey.thermal_zone] + else: + # internal thermal boundary -> two thermal zones + grad = np.rad2deg(thermal_boundary.inclination) + if grad >= 170: + thermal_zones = [storey.thermal_zone, storey.neighbours[0]] + else: + thermal_zones = [storey.neighbours[1], storey.thermal_zone] + thermal_boundary.thermal_zones = thermal_zones + + for storey in storeys: + self._thermal_zones.append(storey.thermal_zone) + + return self._thermal_zones @staticmethod def _calculate_number_storeys_and_height(average_storey_height, eave_height, storeys_above_ground): @@ -139,22 +173,3 @@ class StoreysGeneration: for point in points: array_points.append(point.coordinates) return np.array(array_points) - - def assign_thermal_zones_delimited_by_thermal_boundaries(self): - """ - During storeys creation, the thermal boundaries and zones are also created. - It is afterwards needed to define which zones are delimited by each thermal boundary - """ - for storey in self._building.storeys: - for thermal_boundary in storey.thermal_boundaries: - if thermal_boundary.type != cte.INTERIOR_WALL or thermal_boundary.type != cte.INTERIOR_SLAB: - # external thermal boundary -> only one thermal zone - thermal_zones = [storey.thermal_zone] - else: - # internal thermal boundary -> two thermal zones - grad = np.rad2deg(thermal_boundary.inclination) - if grad >= 170: - thermal_zones = [storey.thermal_zone, storey.neighbours[0]] - else: - thermal_zones = [storey.neighbours[1], storey.thermal_zone] - thermal_boundary.thermal_zones = thermal_zones diff --git a/imports/construction/nrel_physics_interface.py b/imports/construction/nrel_physics_interface.py index 9065ca9e..6f08e536 100644 --- a/imports/construction/nrel_physics_interface.py +++ b/imports/construction/nrel_physics_interface.py @@ -10,6 +10,7 @@ from imports.construction.data_classes.building_achetype import BuildingArchetyp from imports.construction.data_classes.thermal_boundary_archetype import ThermalBoundaryArchetype as ntba from imports.construction.data_classes.thermal_opening_archetype import ThermalOpeningArchetype as ntoa from imports.construction.data_classes.layer_archetype import LayerArchetype as nla +from imports.construction.helpers.storeys_generation import StoreysGeneration class NrelPhysicsInterface: @@ -181,8 +182,57 @@ class NrelPhysicsInterface: return thermal_boundary raise Exception('Construction type not found') + # todo: verify windows + @staticmethod + def _calculate_view_factors(thermal_zone): + """ + Get thermal zone view factors matrix + :return: [[float]] + """ + total_area = 0 + for thermal_boundary in thermal_zone.thermal_boundaries: + total_area += thermal_boundary.opaque_area + for thermal_opening in thermal_boundary.thermal_openings: + total_area += thermal_opening.area + + view_factors_matrix = [] + for thermal_boundary_1 in thermal_zone.thermal_boundaries: + values = [] + for thermal_boundary_2 in thermal_zone.thermal_boundaries: + value = 0 + if thermal_boundary_1.id != thermal_boundary_2.id: + value = thermal_boundary_2.opaque_area / (total_area - thermal_boundary_1.opaque_area) + values.append(value) + for thermal_boundary in thermal_zone.thermal_boundaries: + for thermal_opening in thermal_boundary.thermal_openings: + value = thermal_opening.area / (total_area - thermal_boundary_1.opaque_area) + values.append(value) + view_factors_matrix.append(values) + + for thermal_boundary_1 in thermal_zone.thermal_boundaries: + values = [] + for thermal_opening_1 in thermal_boundary_1.thermal_openings: + for thermal_boundary_2 in thermal_zone.thermal_boundaries: + value = thermal_boundary_2.opaque_area / (total_area - thermal_opening_1.area) + values.append(value) + for thermal_boundary in thermal_zone.thermal_boundaries: + for thermal_opening_2 in thermal_boundary.thermal_openings: + value = 0 + if thermal_opening_1.id != thermal_opening_2.id: + value = thermal_opening_2.area / (total_area - thermal_opening_1.area) + values.append(value) + view_factors_matrix.append(values) + thermal_zone.view_factors_matrix = view_factors_matrix + def enrich_buildings(self): """ Raise not implemented error """ raise NotImplementedError + + @staticmethod + def _create_storeys(building, archetype): + building.average_storey_height = archetype.average_storey_height + building.storeys_above_ground = archetype.storeys_above_ground + thermal_zones = StoreysGeneration(building).thermal_zones + building.internal_zones[0].thermal_zones = thermal_zones diff --git a/imports/construction/us_physics_parameters.py b/imports/construction/us_physics_parameters.py index de79697b..7a23b318 100644 --- a/imports/construction/us_physics_parameters.py +++ b/imports/construction/us_physics_parameters.py @@ -10,7 +10,6 @@ from imports.construction.nrel_physics_interface import NrelPhysicsInterface from imports.construction.helpers.construction_helper import ConstructionHelper from city_model_structure.building_demand.layer import Layer from city_model_structure.building_demand.material import Material -from imports.construction.helpers.storeys_generation import StoreysGeneration class UsPhysicsParameters(NrelPhysicsInterface): @@ -30,7 +29,7 @@ class UsPhysicsParameters(NrelPhysicsInterface): city = self._city # it is assumed that all buildings have the same archetypes' keys for building in city.buildings: - building_type = ConstructionHelper.nrel_from_function(building.function) + building_type = ConstructionHelper.nrel_from_libs_function(building.function) if building_type is None: return try: @@ -46,12 +45,11 @@ class UsPhysicsParameters(NrelPhysicsInterface): if len(building.internal_zones) == 1: if building.internal_zones[0].thermal_zones is None: self._create_storeys(building, archetype) - thermal_zones = [] - for storey in building.storeys: - thermal_zones.append(storey.thermal_zone) - building.internal_zones[0].thermal_zones = thermal_zones self._assign_values(building.internal_zones, archetype) + for internal_zone in building.internal_zones: + for thermal_zone in internal_zone.thermal_zones: + self._calculate_view_factors(thermal_zone) def _search_archetype(self, building_type, standard, climate_zone): for building_archetype in self._building_archetypes: @@ -109,12 +107,3 @@ class UsPhysicsParameters(NrelPhysicsInterface): thermal_opening_archetype.back_side_solar_transmittance_at_normal_incidence thermal_opening.front_side_solar_transmittance_at_normal_incidence = \ thermal_opening_archetype.front_side_solar_transmittance_at_normal_incidence - - @staticmethod - def _create_storeys(building, archetype): - building.average_storey_height = archetype.average_storey_height - building.storeys_above_ground = archetype.storeys_above_ground - storeys_generation = StoreysGeneration(building) - storeys = storeys_generation.storeys - building.storeys = storeys - storeys_generation.assign_thermal_zones_delimited_by_thermal_boundaries() diff --git a/imports/customized_imports/helpers/sanam_customized_usage_helper.py b/imports/customized_imports/helpers/sanam_customized_usage_helper.py index a0d981b8..7bf73f65 100644 --- a/imports/customized_imports/helpers/sanam_customized_usage_helper.py +++ b/imports/customized_imports/helpers/sanam_customized_usage_helper.py @@ -14,7 +14,7 @@ class SanamCustomizedUsageHelper: usage_to_customized = { cte.RESIDENTIAL: 'residential', cte.INDUSTRY: 'manufacturing', - cte.OFFICE_ADMINISTRATION: 'office', + cte.OFFICE_AND_ADMINISTRATION: 'office', cte.HOTEL: 'hotel', cte.HEALTH_CARE: 'health', cte.RETAIL: 'retail', diff --git a/imports/customized_imports/sanam_customized_usage_parameters.py b/imports/customized_imports/sanam_customized_usage_parameters.py index 442ec9d3..ea3cac62 100644 --- a/imports/customized_imports/sanam_customized_usage_parameters.py +++ b/imports/customized_imports/sanam_customized_usage_parameters.py @@ -6,9 +6,11 @@ Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.mons import sys import xmltodict -from imports.geometry.helpers.geometry_helper import GeometryHelper as gh -from imports.usage.data_classes.usage_zone_archetype import UsageZoneArchetype as huza import helpers.constants as cte +from imports.usage.helpers.usage_helper import UsageHelper +from city_model_structure.building_demand.occupancy import Occupancy +from city_model_structure.building_demand.usage_zone import UsageZone +from imports.geometry.helpers.geometry_helper import GeometryHelper class SanamCustomizedUsageParameters: @@ -18,15 +20,10 @@ class SanamCustomizedUsageParameters: def __init__(self, city, base_path): file = 'ashrae_archetypes.xml' path = str(base_path / file) + self._city = city 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 def enrich_buildings(self): """ @@ -35,48 +32,59 @@ 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') + libs_usage = GeometryHelper().libs_usage_from_libs_function(building.function) + comnet_usage = UsageHelper().comnet_from_libs_usage(libs_usage) + archetype = self._search_archetype(comnet_usage) 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 - mix_usage = False - if not mix_usage: - # just one usage_zone - for usage_zone in building.usage_zones: - self._assign_values(usage_zone, archetype, height) + f'{libs_usage}\n') + return - def _search_archetype(self, building_usage): - for building_archetype in self._usage_archetypes: - if building_archetype.usage == building_usage: - return building_archetype + for internal_zone in building.internal_zones: + if internal_zone.area is None: + raise Exception('Internal zone area not defined, ACH cannot be calculated') + if internal_zone.volume is None: + raise Exception('Internal zone volume not defined, ACH cannot be calculated') + if internal_zone.area <= 0: + raise Exception('Internal zone area is zero, ACH cannot be calculated') + if internal_zone.volume <= 0: + raise Exception('Internal zone volume is zero, ACH cannot be calculated') + volume_per_area = internal_zone.volume / internal_zone.area + + usage_zone = UsageZone() + usage_zone.usage = libs_usage + self._assign_values(usage_zone, archetype, volume_per_area) + + def _search_archetype(self, libs_usage): + comnet_usage = UsageHelper.comnet_from_libs_usage(libs_usage) + for building_archetype in self._archetypes['buildingUsageLibrary']['zoneUsageType']: + if building_archetype['id'] == comnet_usage: + usage_archetype = self._parse_usage_type(self._archetypes) + return usage_archetype return None @staticmethod - def _assign_values(usage_zone, archetype, height): + def _assign_values(usage_zone, archetype, volume_per_area): 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. - 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 + if archetype.occupancy.occupancy_density is not None: + if usage_zone.occupancy is None: + _occupancy = Occupancy() + usage_zone.occupancy = _occupancy + usage_zone.occupancy.occupancy_density = archetype.occupancy.occupancy_density + archetype_mechanical_air_change = float(archetype.mechanical_air_change) * \ + float(usage_zone.occupancy.occupancy_density) * cte.METERS_TO_FEET ** 2 \ + * cte.HOUR_TO_MINUTES / cte.METERS_TO_FEET ** 3 / volume_per_area + usage_zone.mechanical_air_change = archetype_mechanical_air_change @staticmethod - def _parse_zone_usage_type(usage, zone_usage_type): - mechanical_air_change = zone_usage_type['endUses']['ventilation']['minimumVentilationRate']['#text'] - if 'occupancy' in zone_usage_type: - 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) + def _parse_usage_type(data): + usage_zone_archetype = UsageZone() + usage_zone_archetype.usage = data['id'] + usage_zone_archetype.mechanical_air_change = data['endUses']['ventilation']['minimumVentilationRate'][ + '#text'] + if 'occupancy' in data: + _occupancy = Occupancy() + _occupancy.occupancy_density = data['occupancy']['occupancyDensity']['#text'] + usage_zone_archetype.occupancy = _occupancy return usage_zone_archetype diff --git a/imports/customized_imports_factory.py b/imports/customized_imports_factory.py index bca8a87c..0a072f89 100644 --- a/imports/customized_imports_factory.py +++ b/imports/customized_imports_factory.py @@ -17,10 +17,6 @@ class CustomizedImportsFactory: self._importer_class = importer_class self._city = city self._base_path = base_path - for building in city.buildings: - if len(building.thermal_zones) == 0: - raise Exception('It seems that the customized imports factory is being called before the construction factory. ' - 'Please ensure that the construction factory is called first.') def enrich(self): """ diff --git a/imports/geometry/helpers/geometry_helper.py b/imports/geometry/helpers/geometry_helper.py index 104792d5..7307673a 100644 --- a/imports/geometry/helpers/geometry_helper.py +++ b/imports/geometry/helpers/geometry_helper.py @@ -13,20 +13,20 @@ class GeometryHelper: """ # function _pluto_to_function = { - 'A0': cte.SFH, - 'A1': cte.SFH, - 'A2': cte.SFH, - 'A3': cte.SFH, - 'A4': cte.SFH, - 'A5': cte.SFH, - 'A6': cte.SFH, - 'A7': cte.SFH, - 'A8': cte.SFH, - 'A9': cte.SFH, - 'B1': cte.MFH, - 'B2': cte.MFH, - 'B3': cte.MFH, - 'B9': cte.MFH, + 'A0': cte.SINGLE_FAMILY_HOUSE, + 'A1': cte.SINGLE_FAMILY_HOUSE, + 'A2': cte.SINGLE_FAMILY_HOUSE, + 'A3': cte.SINGLE_FAMILY_HOUSE, + 'A4': cte.SINGLE_FAMILY_HOUSE, + 'A5': cte.SINGLE_FAMILY_HOUSE, + 'A6': cte.SINGLE_FAMILY_HOUSE, + 'A7': cte.SINGLE_FAMILY_HOUSE, + 'A8': cte.SINGLE_FAMILY_HOUSE, + 'A9': cte.SINGLE_FAMILY_HOUSE, + 'B1': cte.MULTI_FAMILY_HOUSE, + 'B2': cte.MULTI_FAMILY_HOUSE, + 'B3': cte.MULTI_FAMILY_HOUSE, + 'B9': cte.MULTI_FAMILY_HOUSE, 'C0': cte.RESIDENTIAL, 'C1': cte.RESIDENTIAL, 'C2': cte.RESIDENTIAL, @@ -59,16 +59,16 @@ class GeometryHelper: 'F5': cte.WAREHOUSE, 'F8': cte.WAREHOUSE, 'F9': cte.WAREHOUSE, - 'G0': cte.OFFICE, - 'G1': cte.OFFICE, - 'G2': cte.OFFICE, - 'G3': cte.OFFICE, - 'G4': cte.OFFICE, - 'G5': cte.OFFICE, - 'G6': cte.OFFICE, - 'G7': cte.OFFICE, - 'G8': cte.OFFICE, - 'G9': cte.OFFICE, + 'G0': cte.SMALL_OFFICE, + 'G1': cte.SMALL_OFFICE, + 'G2': cte.SMALL_OFFICE, + 'G3': cte.SMALL_OFFICE, + 'G4': cte.SMALL_OFFICE, + 'G5': cte.SMALL_OFFICE, + 'G6': cte.SMALL_OFFICE, + 'G7': cte.SMALL_OFFICE, + 'G8': cte.SMALL_OFFICE, + 'G9': cte.SMALL_OFFICE, 'H1': cte.HOTEL, 'H2': cte.HOTEL, 'H3': cte.HOTEL, @@ -83,13 +83,13 @@ class GeometryHelper: 'HR': cte.HOTEL, 'HS': cte.HOTEL, 'I1': cte.HOSPITAL, - 'I2': cte.OUTPATIENT, - 'I3': cte.OUTPATIENT, + 'I2': cte.OUT_PATIENT_HEALTH_CARE, + 'I3': cte.OUT_PATIENT_HEALTH_CARE, 'I4': cte.RESIDENTIAL, - 'I5': cte.OUTPATIENT, - 'I6': cte.OUTPATIENT, - 'I7': cte.OUTPATIENT, - 'I9': cte.OUTPATIENT, + 'I5': cte.OUT_PATIENT_HEALTH_CARE, + 'I6': cte.OUT_PATIENT_HEALTH_CARE, + 'I7': cte.OUT_PATIENT_HEALTH_CARE, + 'I9': cte.OUT_PATIENT_HEALTH_CARE, 'J1': cte.LARGE_OFFICE, 'J2': cte.LARGE_OFFICE, 'J3': cte.LARGE_OFFICE, @@ -104,10 +104,10 @@ class GeometryHelper: 'K3': cte.STRIP_MALL, 'K4': cte.RESIDENTIAL, 'K5': cte.RESTAURANT, - 'K6': cte.COMMERCIAL, - 'K7': cte.COMMERCIAL, - 'K8': cte.COMMERCIAL, - 'K9': cte.COMMERCIAL, + 'K6': cte.SUPERMARKET, + 'K7': cte.SUPERMARKET, + 'K8': cte.SUPERMARKET, + 'K9': cte.SUPERMARKET, 'L1': cte.RESIDENTIAL, 'L2': cte.RESIDENTIAL, 'L3': cte.RESIDENTIAL, @@ -123,34 +123,34 @@ class GeometryHelper: 'N3': cte.RESIDENTIAL, 'N4': cte.RESIDENTIAL, 'N9': cte.RESIDENTIAL, - 'O1': cte.OFFICE, - 'O2': cte.OFFICE, - 'O3': cte.OFFICE, - 'O4': cte.OFFICE, - 'O5': cte.OFFICE, - 'O6': cte.OFFICE, - 'O7': cte.OFFICE, - 'O8': cte.OFFICE, - 'O9': cte.OFFICE, + 'O1': cte.SMALL_OFFICE, + 'O2': cte.SMALL_OFFICE, + 'O3': cte.SMALL_OFFICE, + 'O4': cte.SMALL_OFFICE, + 'O5': cte.SMALL_OFFICE, + 'O6': cte.SMALL_OFFICE, + 'O7': cte.SMALL_OFFICE, + 'O8': cte.SMALL_OFFICE, + 'O9': cte.SMALL_OFFICE, 'P1': cte.LARGE_OFFICE, 'P2': cte.HOTEL, - 'P3': cte.OFFICE, - 'P4': cte.OFFICE, - 'P5': cte.OFFICE, - 'P6': cte.OFFICE, + 'P3': cte.SMALL_OFFICE, + 'P4': cte.SMALL_OFFICE, + 'P5': cte.SMALL_OFFICE, + 'P6': cte.SMALL_OFFICE, 'P7': cte.LARGE_OFFICE, 'P8': cte.LARGE_OFFICE, - 'P9': cte.OFFICE, - 'Q0': cte.OFFICE, - 'Q1': cte.OFFICE, - 'Q2': cte.OFFICE, - 'Q3': cte.OFFICE, - 'Q4': cte.OFFICE, - 'Q5': cte.OFFICE, - 'Q6': cte.OFFICE, - 'Q7': cte.OFFICE, - 'Q8': cte.OFFICE, - 'Q9': cte.OFFICE, + 'P9': cte.SMALL_OFFICE, + 'Q0': cte.SMALL_OFFICE, + 'Q1': cte.SMALL_OFFICE, + 'Q2': cte.SMALL_OFFICE, + 'Q3': cte.SMALL_OFFICE, + 'Q4': cte.SMALL_OFFICE, + 'Q5': cte.SMALL_OFFICE, + 'Q6': cte.SMALL_OFFICE, + 'Q7': cte.SMALL_OFFICE, + 'Q8': cte.SMALL_OFFICE, + 'Q9': cte.SMALL_OFFICE, 'R0': cte.RESIDENTIAL, 'R1': cte.RESIDENTIAL, 'R2': cte.RESIDENTIAL, @@ -214,37 +214,47 @@ class GeometryHelper: } _hft_to_function = { 'residential': cte.RESIDENTIAL, - 'single family house': cte.SFH, - 'multifamily house': cte.MFH, + 'single family house': cte.SINGLE_FAMILY_HOUSE, + 'multifamily house': cte.MULTI_FAMILY_HOUSE, 'hotel': cte.HOTEL, 'hospital': cte.HOSPITAL, - 'outpatient': cte.OUTPATIENT, - 'commercial': cte.COMMERCIAL, + 'outpatient': cte.OUT_PATIENT_HEALTH_CARE, + 'commercial': cte.SUPERMARKET, 'strip mall': cte.STRIP_MALL, 'warehouse': cte.WAREHOUSE, 'primary school': cte.PRIMARY_SCHOOL, 'secondary school': cte.SECONDARY_SCHOOL, - 'office': cte.OFFICE, + 'office': cte.MEDIUM_OFFICE, 'large office': cte.LARGE_OFFICE } # usage _function_to_usage = { - cte.RESTAURANT: cte.RESTAURANT, cte.RESIDENTIAL: cte.RESIDENTIAL, - cte.HOSPITAL: cte.HEALTH_CARE, - cte.HOTEL: cte.HOTEL, - cte.LARGE_OFFICE: cte.OFFICE_ADMINISTRATION, - cte.OFFICE: cte.OFFICE_ADMINISTRATION, + cte.SINGLE_FAMILY_HOUSE: cte.SINGLE_FAMILY_HOUSE, + cte.MULTI_FAMILY_HOUSE: cte.MULTI_FAMILY_HOUSE, + cte.ROW_HOSE: cte.RESIDENTIAL, + cte.MID_RISE_APARTMENT: cte.RESIDENTIAL, + cte.HIGH_RISE_APARTMENT: cte.RESIDENTIAL, + cte.SMALL_OFFICE: cte.OFFICE_AND_ADMINISTRATION, + cte.MEDIUM_OFFICE: cte.OFFICE_AND_ADMINISTRATION, + cte.LARGE_OFFICE: cte.OFFICE_AND_ADMINISTRATION, cte.PRIMARY_SCHOOL: cte.EDUCATION, cte.SECONDARY_SCHOOL: cte.EDUCATION, - cte.RETAIL: cte.RETAIL, - cte.STRIP_MALL: cte.HALL, - cte.WAREHOUSE: cte.INDUSTRY + cte.STAND_ALONE_RETAIL: cte.RETAIL_SHOP_WITHOUT_REFRIGERATED_FOOD, + cte.HOSPITAL: cte.HEALTH_CARE, + cte.OUT_PATIENT_HEALTH_CARE: cte.HEALTH_CARE, + cte.STRIP_MALL: cte.RETAIL_SHOP_WITHOUT_REFRIGERATED_FOOD, + cte.SUPERMARKET: cte.RETAIL_SHOP_WITH_REFRIGERATED_FOOD, + cte.WAREHOUSE: cte.RETAIL_SHOP_WITHOUT_REFRIGERATED_FOOD, + cte.QUICK_SERVICE_RESTAURANT: cte.RESTAURANT, + cte.FULL_SERVICE_RESTAURANT: cte.RESTAURANT, + cte.SMALL_HOTEL: cte.HOTEL, + cte.LARGE_HOTEL: cte.HOTEL } @staticmethod - def function_from_hft(building_hft_function): + def libs_function_from_hft(building_hft_function): """ Get internal function from the given HfT function :param building_hft_function: str @@ -253,7 +263,7 @@ class GeometryHelper: return GeometryHelper._hft_to_function[building_hft_function] @staticmethod - def function_from_pluto(building_pluto_function): + def libs_function_from_pluto(building_pluto_function): """ Get internal function from the given pluto function :param building_pluto_function: str @@ -262,7 +272,7 @@ class GeometryHelper: return GeometryHelper._pluto_to_function[building_pluto_function] @staticmethod - def usage_from_function(building_function): + def libs_usage_from_libs_function(building_function): """ Get the internal usage for the given internal building function :param building_function: str diff --git a/imports/schedules/doe_idf.py b/imports/schedules/doe_idf.py index d8f88f4f..cbec5105 100644 --- a/imports/schedules/doe_idf.py +++ b/imports/schedules/doe_idf.py @@ -10,6 +10,8 @@ import parseidf import xmltodict from imports.schedules.helpers.schedules_helper import SchedulesHelper from city_model_structure.attributes.schedule import Schedule +from city_model_structure.building_demand.occupancy import Occupancy +from city_model_structure.building_demand.lighting import Lighting import helpers.constants as cte @@ -52,15 +54,16 @@ class DoeIdf: self._schedule_library = xmltodict.parse(xml.read()) 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 + for internal_zone in building.internal_zones: + for usage_zone in internal_zone.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, usage_zone): schedules_day = {} @@ -131,4 +134,12 @@ class DoeIdf: continue schedules.append(schedule) - usage_zone.schedules = schedules + for schedule in schedules: + if schedule.type == cte.OCCUPANCY: + if usage_zone.occupancy is None: + usage_zone.occupancy = Occupancy() + usage_zone.occupancy.occupancy_schedules = [schedule] + elif schedule.type == cte.LIGHTING: + if usage_zone.lighting is None: + usage_zone.lighting = Lighting() + usage_zone.lighting.schedules = [schedule] diff --git a/imports/schedules/helpers/schedules_helper.py b/imports/schedules/helpers/schedules_helper.py index 9aca6fab..4d4881a5 100644 --- a/imports/schedules/helpers/schedules_helper.py +++ b/imports/schedules/helpers/schedules_helper.py @@ -15,10 +15,10 @@ class SchedulesHelper: _usage_to_comnet = { cte.RESIDENTIAL: 'C-12 Residential', cte.INDUSTRY: 'C-10 Warehouse', - cte.OFFICE_ADMINISTRATION: 'C-5 Office', + cte.OFFICE_AND_ADMINISTRATION: 'C-5 Office', cte.HOTEL: 'C-3 Hotel', cte.HEALTH_CARE: 'C-2 Health', - cte.RETAIL: 'C-8 Retail', + cte.RETAIL_SHOP_WITHOUT_REFRIGERATED_FOOD: 'C-8 Retail', cte.HALL: 'C-8 Retail', cte.RESTAURANT: 'C-7 Restaurant', cte.EDUCATION: 'C-9 School' @@ -36,18 +36,18 @@ class SchedulesHelper: 'high-rise apartment': cte.RESIDENTIAL, 'hospital': cte.HEALTH_CARE, 'large hotel': cte.HOTEL, - 'large office': cte.OFFICE_ADMINISTRATION, - 'medium office': cte.OFFICE_ADMINISTRATION, + 'large office': cte.OFFICE_AND_ADMINISTRATION, + 'medium office': cte.OFFICE_AND_ADMINISTRATION, 'midrise apartment': cte.RESIDENTIAL, 'outpatient healthcare': cte.HEALTH_CARE, 'primary school': cte.EDUCATION, 'quick service restaurant': cte.RESTAURANT, 'secondary school': cte.EDUCATION, 'small hotel': cte.HOTEL, - 'small office': cte.OFFICE_ADMINISTRATION, - 'stand-alone-retail': cte.RETAIL, + 'small office': cte.OFFICE_AND_ADMINISTRATION, + 'stand-alone-retail': cte.RETAIL_SHOP_WITHOUT_REFRIGERATED_FOOD, 'strip mall': cte.HALL, - 'supermarket': cte.RETAIL, + 'supermarket': cte.RETAIL_SHOP_WITH_REFRIGERATED_FOOD, 'warehouse': cte.INDUSTRY, 'residential': cte.RESIDENTIAL } diff --git a/imports/schedules_factory.py b/imports/schedules_factory.py index 8437593f..3842889a 100644 --- a/imports/schedules_factory.py +++ b/imports/schedules_factory.py @@ -6,7 +6,6 @@ Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc """ from pathlib import Path -from imports.schedules.comnet_schedules_parameters import ComnetSchedules from imports.schedules.doe_idf import DoeIdf @@ -19,15 +18,11 @@ class SchedulesFactory: self._city = city self._base_path = base_path for building in city.buildings: - if len(building.usage_zones) == 0: - raise Exception('It seems that the schedule factory is being called before the usage factory. ' - 'Please ensure that the usage factory is called first.') - - def _comnet(self): - """ - Enrich the city by using COMNET schedules as data source - """ - ComnetSchedules(self._city, self._base_path) + for internal_zone in building.internal_zones: + if len(internal_zone.usage_zones) == 0: + raise Exception('It seems that the schedule factory is being called before the usage factory. ' + 'Please ensure that the usage factory is called first as the usage zones must be ' + 'firstly generated.') def _doe_idf(self): """ diff --git a/imports/usage/ca_usage_parameters.py b/imports/usage/ca_usage_parameters.py index c9034b85..b2320fa6 100644 --- a/imports/usage/ca_usage_parameters.py +++ b/imports/usage/ca_usage_parameters.py @@ -5,7 +5,9 @@ Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.mons """ import sys +from imports.geometry.helpers.geometry_helper import GeometryHelper from imports.usage.hft_usage_interface import HftUsageInterface +from imports.usage.helpers.usage_helper import UsageHelper from city_model_structure.building_demand.usage_zone import UsageZone from city_model_structure.building_demand.internal_gains import InternalGains from city_model_structure.building_demand.occupancy import Occupancy @@ -28,9 +30,9 @@ class CaUsageParameters(HftUsageInterface): """ city = self._city for building in city.buildings: + usage = GeometryHelper().libs_usage_from_libs_function(building.function) try: - print(building.function) - archetype = self._search_archetype(building.function) + archetype = self._search_archetype(usage) except KeyError: sys.stderr.write(f'Building {building.name} has unknown archetype for building function:' f' {building.function}\n') @@ -43,7 +45,8 @@ class CaUsageParameters(HftUsageInterface): self._assign_values_usage_zone(usage_zone, archetype) internal_zone.usage_zones = [usage_zone] - def _search_archetype(self, building_usage): + def _search_archetype(self, libs_usage): + building_usage = UsageHelper().hft_from_libs_usage(libs_usage) for building_archetype in self._usage_archetypes: if building_archetype.usage == building_usage: return building_archetype diff --git a/imports/usage/comnet_usage_parameters.py b/imports/usage/comnet_usage_parameters.py index 851473da..5d159af7 100644 --- a/imports/usage/comnet_usage_parameters.py +++ b/imports/usage/comnet_usage_parameters.py @@ -175,13 +175,13 @@ class ComnetUsageParameters: return _usage_zone - def _search_archetypes(self, usage): + def _search_archetypes(self, libs_usage): for item in self._data['lighting']: - comnet_usage = UsageHelper.comnet_from_usage(usage) + comnet_usage = UsageHelper.comnet_from_libs_usage(libs_usage) if comnet_usage == item: usage_archetype = self._parse_usage_type(comnet_usage, self._data, self._xls) return usage_archetype - return None, None + return None def enrich_buildings(self): """ @@ -190,13 +190,13 @@ class ComnetUsageParameters: """ city = self._city for building in city.buildings: - usage = GeometryHelper.usage_from_function(building.function) + usage = GeometryHelper.libs_usage_from_libs_function(building.function) try: archetype_usage = self._search_archetypes(usage) except KeyError: sys.stderr.write(f'Building {building.name} has unknown archetype for building function:' f' {building.function}, that assigns building usage as ' - f'{GeometryHelper.usage_from_function(building.function)}\n') + f'{GeometryHelper.libs_usage_from_libs_function(building.function)}\n') return for internal_zone in building.internal_zones: diff --git a/imports/usage/helpers/usage_helper.py b/imports/usage/helpers/usage_helper.py index f6257503..98f873da 100644 --- a/imports/usage/helpers/usage_helper.py +++ b/imports/usage/helpers/usage_helper.py @@ -13,17 +13,30 @@ class UsageHelper: """ _usage_to_hft = { cte.RESIDENTIAL: 'residential', - cte.INDUSTRY: 'industry', - cte.OFFICE_ADMINISTRATION: 'office and administration', + cte.SINGLE_FAMILY_HOUSE: 'Single family house', + cte.MULTI_FAMILY_HOUSE: 'Multi-family house', + cte.EDUCATION: 'education', + cte.SCHOOL_WITHOUT_SHOWER: 'school without shower', + cte.SCHOOL_WITH_SHOWER: 'school with shower', + cte.RETAIL_SHOP_WITHOUT_REFRIGERATED_FOOD: 'retail', + cte.RETAIL_SHOP_WITH_REFRIGERATED_FOOD: 'retail shop / refrigerated food', cte.HOTEL: 'hotel', - cte.HEALTH_CARE: 'health care', - cte.RETAIL: 'retail', - cte.HALL: 'hall', + cte.HOTEL_MEDIUM_CLASS: 'hotel (Medium-class)', + cte.DORMITORY: 'dormitory', + cte.INDUSTRY: 'industry', cte.RESTAURANT: 'restaurant', - cte.EDUCATION: 'education'} + cte.HEALTH_CARE: 'health care', + cte.RETIREMENT_HOME_OR_ORPHANAGE: 'Home for the aged or orphanage', + cte.OFFICE_AND_ADMINISTRATION: 'office and administration', + cte.EVENT_LOCATION: 'event location', + cte.HALL: 'hall', + cte.SPORTS_LOCATION: 'sport location', + cte.LABOR: 'Labor', + cte.GREEN_HOUSE: 'green house', + cte.NON_HEATED: 'non-heated'} @staticmethod - def hft_from_usage(usage): + def hft_from_libs_usage(usage): """ Get HfT usage from the given internal usage key :param usage: str @@ -32,18 +45,32 @@ class UsageHelper: try: return UsageHelper._usage_to_hft[usage] except KeyError: - sys.stderr.write('Error: keyword not found.\n') + sys.stderr.write('Error: keyword not found to translate from libs_usage to hft usage.\n') _usage_to_comnet = { cte.RESIDENTIAL: 'BA Multifamily', - cte.INDUSTRY: 'BA Manufacturing Facility', - cte.OFFICE_ADMINISTRATION: 'BA Office', + cte.SINGLE_FAMILY_HOUSE: 'BA Multifamily', + cte.MULTI_FAMILY_HOUSE: 'BA Multifamily', + cte.EDUCATION: 'BA School/University', + cte.SCHOOL_WITHOUT_SHOWER: 'BA School/University', + cte.SCHOOL_WITH_SHOWER: 'BA School/University', + cte.RETAIL_SHOP_WITHOUT_REFRIGERATED_FOOD: 'BA Retail', + cte.RETAIL_SHOP_WITH_REFRIGERATED_FOOD: 'BA Retail', cte.HOTEL: 'BA Hotel', + cte.HOTEL_MEDIUM_CLASS: 'BA Hotel', + cte.DORMITORY: 'BA Dormitory', + cte.INDUSTRY: 'BA Manufacturing Facility', + cte.RESTAURANT: 'BA Dining: Family', cte.HEALTH_CARE: 'BA Hospital', - cte.RETAIL: 'BA Retail', - cte.HALL: 'BA Town Hall', - cte.RESTAURANT: 'BA Dining: Bar Lounge/Leisure', - cte.EDUCATION: 'BA School/University'} + cte.RETIREMENT_HOME_OR_ORPHANAGE: 'BA Multifamily', + cte.OFFICE_AND_ADMINISTRATION: 'BA Office', + cte.EVENT_LOCATION: 'BA Convention Center', + cte.HALL: 'BA Convention Center', + cte.SPORTS_LOCATION: 'BA Sports Arena', + cte.LABOR: 'BA Gymnasium', + cte.GREEN_HOUSE: cte.GREEN_HOUSE, + cte.NON_HEATED: cte.NON_HEATED + } _comnet_schedules_key_to_comnet_schedules = { 'C-1 Assembly': 'C-1 Assembly', @@ -62,7 +89,7 @@ class UsageHelper: 'C-14 Gymnasium': 'C-14 Gymnasium'} @staticmethod - def comnet_from_usage(usage): + def comnet_from_libs_usage(usage): """ Get Comnet usage from the given internal usage key :param usage: str @@ -71,7 +98,7 @@ class UsageHelper: try: return UsageHelper._usage_to_comnet[usage] except KeyError: - sys.stderr.write('Error: keyword not found.\n') + sys.stderr.write('Error: keyword not found to translate from libs_usage to comnet usage.\n') @staticmethod def schedules_key(usage): diff --git a/imports/usage/hft_usage_interface.py b/imports/usage/hft_usage_interface.py index 340d8a80..959afbef 100644 --- a/imports/usage/hft_usage_interface.py +++ b/imports/usage/hft_usage_interface.py @@ -5,6 +5,7 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2022 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ import xmltodict +import copy from city_model_structure.building_demand.usage_zone import UsageZone from city_model_structure.building_demand.internal_gains import InternalGains from city_model_structure.building_demand.occupancy import Occupancy @@ -33,8 +34,6 @@ class HftUsageInterface: usage = usage_zone_variant['id'] usage_archetype_variant = self._parse_zone_usage_variant(usage, usage_archetype, usage_zone_variant) self._usage_archetypes.append(usage_archetype_variant) - for usage in self._usage_archetypes: - print(usage.usage) @staticmethod def _parse_zone_usage_type(usage, zone_usage_type): @@ -135,7 +134,7 @@ class HftUsageInterface: @staticmethod def _parse_zone_usage_variant(usage, usage_zone, usage_zone_variant): # the variants mimic the inheritance concept from OOP - usage_zone_archetype = usage_zone + usage_zone_archetype = copy.deepcopy(usage_zone) usage_zone_archetype.usage = usage if 'occupancy' in usage_zone_variant: diff --git a/imports/usage/hft_usage_parameters.py b/imports/usage/hft_usage_parameters.py index d95a2c3c..48a7b267 100644 --- a/imports/usage/hft_usage_parameters.py +++ b/imports/usage/hft_usage_parameters.py @@ -4,14 +4,12 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ import sys +import copy -from imports.geometry.helpers.geometry_helper import GeometryHelper as gh +from imports.geometry.helpers.geometry_helper import GeometryHelper from imports.usage.hft_usage_interface import HftUsageInterface +from imports.usage.helpers.usage_helper import UsageHelper from city_model_structure.building_demand.usage_zone import UsageZone -from city_model_structure.building_demand.internal_gains import InternalGains -from city_model_structure.building_demand.occupancy import Occupancy -from city_model_structure.building_demand.appliances import Appliances -from city_model_structure.building_demand.thermal_control import ThermalControl class HftUsageParameters(HftUsageInterface): @@ -29,23 +27,25 @@ class HftUsageParameters(HftUsageInterface): """ city = self._city for building in city.buildings: - usage = gh.usage_from_function(building.function) + usage = GeometryHelper().libs_usage_from_libs_function(building.function) try: archetype = self._search_archetype(usage) except KeyError: 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') + f'{GeometryHelper().libs_usage_from_libs_function(building.function)}\n') return for internal_zone in building.internal_zones: usage_zone = UsageZone() - usage_zone.usage = building.function + libs_usage = GeometryHelper().libs_usage_from_libs_function(building.function) + usage_zone.usage = UsageHelper().hft_from_libs_usage(libs_usage) self._assign_values(usage_zone, archetype) usage_zone.percentage = 1 internal_zone.usage_zones = [usage_zone] - def _search_archetype(self, building_usage): + def _search_archetype(self, libs_usage): + building_usage = UsageHelper().hft_from_libs_usage(libs_usage) for building_archetype in self._usage_archetypes: if building_archetype.usage == building_usage: return building_archetype @@ -53,7 +53,7 @@ class HftUsageParameters(HftUsageInterface): @staticmethod def _assign_values(usage_zone, archetype): - # Due to the fact that python is not a typed language, the wrong object type is assigned to + """ # 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. # Due to the fact that python is not a typed language, the wrong object type is assigned to @@ -83,3 +83,12 @@ class HftUsageParameters(HftUsageInterface): _internal_gain.schedules = archetype_internal_gain.schedules _internal_gains.append(_internal_gain) usage_zone.not_detailed_source_mean_annual_internal_gains = _internal_gains + """ + usage_zone.mechanical_air_change = archetype.mechanical_air_change + usage_zone.occupancy = copy.deepcopy(archetype.occupancy) + usage_zone.appliances = copy.deepcopy(archetype.appliances) + usage_zone.thermal_control = copy.deepcopy(archetype.thermal_control) + usage_zone.not_detailed_source_mean_annual_internal_gains = \ + copy.deepcopy(archetype.not_detailed_source_mean_annual_internal_gains) + usage_zone.days_year = archetype.days_year + usage_zone.hours_day = archetype.hours_day diff --git a/recognized_functions_and_usages.md b/recognized_functions_and_usages.md new file mode 100644 index 00000000..fad74352 --- /dev/null +++ b/recognized_functions_and_usages.md @@ -0,0 +1,64 @@ +# Functions and usages internally recognized within the libs + +The libs uses a list of building functions a building usages that are the only ones recognized. All new categories should be added to the dicctionaries that translate from the input formats to the libs functions. From the libs functions to the libs usages and from the libs usages and libs functions to the output formats. + +Input formats accepted: +* Function: + * pluto + * hft + +Output formats accepted: +* Function: + * nrel + * nrcan +* Usage: + * ca + * hft + * comnet + +Libs_functions: +* single family house +* multi family house +* row hose +* mid rise apartment +* high rise apartment +* residential +* small office +* medium office +* large office +* primary school +* secondary school +* stand alone retail +* hospital +* out-patient health care +* strip mall +* supermarket +* ware house +* quick service restaurant +* full service restaurant +* small hotel +* large hotel + +Libs_usage: +* residential +* single family house +* multi family house +* education +* school without shower +* school with shower +* retail shop without refrigerated food +* retail shop with refrigerated food +* hotel +* hotel medium class +* dormitory +* industry +* restaurant +* health care +* retirement home or orphanage +* office and administration +* event location +* hall +* sports location +* labor +* green-house +* non-heated diff --git a/unittests/test_construction_factory.py b/unittests/test_construction_factory.py index df7e1edd..9348e8d3 100644 --- a/unittests/test_construction_factory.py +++ b/unittests/test_construction_factory.py @@ -64,7 +64,6 @@ class TestConstructionFactory(TestCase): self.assertEqual(len(building.heating), 0, 'building heating is not none') self.assertEqual(len(building.cooling), 0, 'building cooling is not none') self.assertIsNotNone(building.eave_height, 'building eave height is none') - self.assertIsNotNone(building.storeys, 'building storeys are not defined') self.assertIsNotNone(building.roof_type, 'building roof type is none') self.assertIsNotNone(building.floor_area, 'building floor_area is none') self.assertIsNone(building.households, 'building households is not none') @@ -93,7 +92,7 @@ class TestConstructionFactory(TestCase): for thermal_boundary in thermal_zone.thermal_boundaries: self.assertIsNotNone(thermal_boundary.id, 'thermal_boundary id is none') self.assertIsNotNone(thermal_boundary.parent_surface, 'thermal_boundary surface is none') - self.assertIsNotNone(thermal_boundary.thermal_zones, 'thermal_boundary delimits is none') + self.assertIsNotNone(thermal_boundary.thermal_zones, 'thermal_boundary delimits no thermal zone') self.assertIsNotNone(thermal_boundary.opaque_area, 'thermal_boundary area is none') self.assertIsNotNone(thermal_boundary.azimuth, 'thermal_boundary azimuth is none') self.assertIsNotNone(thermal_boundary.inclination, 'thermal_boundary inclination is none') @@ -166,7 +165,7 @@ class TestConstructionFactory(TestCase): file = 'one_building_in_kelowna.gml' city = self._get_citygml(file) for building in city.buildings: - building.function = GeometryHelper.function_from_hft(building.function) + building.function = GeometryHelper.libs_function_from_hft(building.function) ConstructionFactory('nrcan', city).enrich() self._check_buildings(city) @@ -196,7 +195,7 @@ class TestConstructionFactory(TestCase): file = 'pluto_building.gml' city = self._get_citygml(file) for building in city.buildings: - building.function = GeometryHelper.function_from_pluto(building.function) + building.function = GeometryHelper.libs_function_from_pluto(building.function) ConstructionFactory('nrel', city).enrich() self._check_buildings(city) @@ -226,9 +225,9 @@ class TestConstructionFactory(TestCase): @staticmethod def _internal_function(function_format, original_function): if function_format == 'hft': - new_function = GeometryHelper.function_from_hft(original_function) + new_function = GeometryHelper.libs_function_from_hft(original_function) elif function_format == 'pluto': - new_function = GeometryHelper.function_from_pluto(original_function) + new_function = GeometryHelper.libs_function_from_pluto(original_function) else: raise Exception('Function key not recognized. Implemented only "hft" and "pluto"') return new_function diff --git a/unittests/test_customized_imports_factory.py b/unittests/test_customized_imports_factory.py index 80373480..245a8821 100644 --- a/unittests/test_customized_imports_factory.py +++ b/unittests/test_customized_imports_factory.py @@ -6,8 +6,8 @@ Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.mons from pathlib import Path from unittest import TestCase +import helpers.constants as cte 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,7 +28,6 @@ class TestCustomizedImportsFactory(TestCase): file_path = (self._example_path / file).resolve() _city = GeometryFactory('citygml', file_path).city self.assertIsNotNone(_city, 'city is none') - ConstructionFactory('nrel', _city).enrich() UsageFactory('hft', _city).enrich() return _city @@ -44,6 +43,9 @@ class TestCustomizedImportsFactory(TestCase): 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') + self.assertIsNot(len(building.internal_zones), 0, 'no building internal_zones defined') + for internal_zone in building.internal_zones: + for usage_zone in internal_zone.usage_zones: + if usage_zone.usage != cte.RESIDENTIAL: + self.assertIsNotNone(usage_zone.mechanical_air_change, 'mechanical air change rate is none') + self.assertIsNotNone(usage_zone.occupancy.occupancy_density, 'occupancy density us none') diff --git a/unittests/test_doe_idf.py b/unittests/test_doe_idf.py index 76ce8440..c75e6372 100644 --- a/unittests/test_doe_idf.py +++ b/unittests/test_doe_idf.py @@ -34,15 +34,22 @@ class TestBuildings(TestCase): 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.assertIsNot(len(usage_zone.schedules), 0, 'no usage_zones schedules defined') - for schedule in usage_zone.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) - - + 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_enrichement.py b/unittests/test_enrichement.py index 7c5861d5..95861429 100644 --- a/unittests/test_enrichement.py +++ b/unittests/test_enrichement.py @@ -54,10 +54,10 @@ class TestGeometryFactory(TestCase): def _prepare_case_usage_first(city, input_key, construction_key, usage_key): if input_key == 'pluto': for building in city.buildings: - building.function = GeometryHelper.function_from_pluto(building.function) + building.function = GeometryHelper.libs_function_from_pluto(building.function) elif input_key == 'hft': for building in city.buildings: - building.function = GeometryHelper.function_from_hft(building.function) + building.function = GeometryHelper.libs_function_from_hft(building.function) UsageFactory(usage_key, city).enrich() ConstructionFactory(construction_key, city).enrich() @@ -65,10 +65,10 @@ class TestGeometryFactory(TestCase): def _prepare_case_construction_first(city, input_key, construction_key, usage_key): if input_key == 'pluto': for building in city.buildings: - building.function = GeometryHelper.function_from_pluto(building.function) + building.function = GeometryHelper.libs_function_from_pluto(building.function) elif input_key == 'hft': for building in city.buildings: - building.function = GeometryHelper.function_from_hft(building.function) + building.function = GeometryHelper.libs_function_from_hft(building.function) ConstructionFactory(construction_key, city).enrich() UsageFactory(usage_key, city).enrich() @@ -112,34 +112,52 @@ class TestGeometryFactory(TestCase): self._check_thermal_zones(thermal_zone) for construction_key in _construction_keys: - for usage_key in _usage_keys: - if usage_key != 'ca': - city = self._get_citygml(file_2) - self.assertTrue(len(city.buildings) == 1) - self._prepare_case_construction_first(city, 'pluto', construction_key, usage_key) - self._check_buildings(city) - for building in city.buildings: - for internal_zone in building.internal_zones: - self.assertIsNot(len(internal_zone.usage_zones), 0, 'no building usage_zones defined') - for usage_zone in internal_zone.usage_zones: - self._check_usage_zone(usage_zone) - for thermal_zone in internal_zone.thermal_zones: - self._check_thermal_zones(thermal_zone) + if construction_key != 'nrcan': + for usage_key in _usage_keys: + if usage_key != 'ca': + city = self._get_citygml(file_2) + self.assertTrue(len(city.buildings) == 1) + self._prepare_case_construction_first(city, 'pluto', construction_key, usage_key) + self._check_buildings(city) + for building in city.buildings: + for internal_zone in building.internal_zones: + self.assertIsNot(len(internal_zone.usage_zones), 0, 'no building usage_zones defined') + for usage_zone in internal_zone.usage_zones: + self._check_usage_zone(usage_zone) + for thermal_zone in internal_zone.thermal_zones: + self._check_thermal_zones(thermal_zone) for construction_key in _construction_keys: - for usage_key in _usage_keys: - if usage_key != 'ca': - city = self._get_citygml(file_2) - self.assertTrue(len(city.buildings) == 1) - self._prepare_case_usage_first(city, 'pluto', construction_key, usage_key) - self._check_buildings(city) - for building in city.buildings: - for internal_zone in building.internal_zones: - self.assertIsNot(len(internal_zone.usage_zones), 0, 'no building usage_zones defined') - for usage_zone in internal_zone.usage_zones: - self._check_usage_zone(usage_zone) - for thermal_zone in internal_zone.thermal_zones: - self._check_thermal_zones(thermal_zone) + if construction_key != 'nrcan': + for usage_key in _usage_keys: + if usage_key != 'ca': + city = self._get_citygml(file_2) + self.assertTrue(len(city.buildings) == 1) + self._prepare_case_usage_first(city, 'pluto', construction_key, usage_key) + self._check_buildings(city) + for building in city.buildings: + for internal_zone in building.internal_zones: + self.assertIsNot(len(internal_zone.usage_zones), 0, 'no building usage_zones defined') + for usage_zone in internal_zone.usage_zones: + self._check_usage_zone(usage_zone) + for thermal_zone in internal_zone.thermal_zones: + self._check_thermal_zones(thermal_zone) city = self._get_citygml(file_3) self.assertTrue(len(city.buildings) == 10) + + for construction_key in _construction_keys: + if construction_key != 'nrcan': + for usage_key in _usage_keys: + if usage_key != 'ca': + city = self._get_citygml(file_2) + self.assertTrue(len(city.buildings) == 1) + self._prepare_case_construction_first(city, 'pluto', construction_key, usage_key) + self._check_buildings(city) + for building in city.buildings: + for internal_zone in building.internal_zones: + self.assertIsNot(len(internal_zone.usage_zones), 0, 'no building usage_zones defined') + for usage_zone in internal_zone.usage_zones: + self._check_usage_zone(usage_zone) + for thermal_zone in internal_zone.thermal_zones: + self._check_thermal_zones(thermal_zone) diff --git a/unittests/test_exports.py b/unittests/test_exports.py index b4c38be1..20900b6f 100644 --- a/unittests/test_exports.py +++ b/unittests/test_exports.py @@ -9,8 +9,8 @@ from pathlib import Path from unittest import TestCase import pandas as pd from imports.geometry_factory import GeometryFactory +from imports.geometry.helpers.geometry_helper import GeometryHelper from imports.construction_factory import ConstructionFactory -from imports.schedules_factory import SchedulesFactory from imports.usage_factory import UsageFactory from exports.exports_factory import ExportsFactory import helpers.constants as cte @@ -45,9 +45,10 @@ class TestExports(TestCase): else: file_path = (self._example_path / 'one_building_in_kelowna.gml').resolve() self._complete_city = self._get_citygml(file_path) + for building in self._complete_city.buildings: + building.function = GeometryHelper().libs_function_from_hft(building.function) ConstructionFactory('nrel', self._complete_city).enrich() UsageFactory('ca', self._complete_city).enrich() - SchedulesFactory('comnet', self._complete_city).enrich() cli = 'C:\\Users\\Pilar\\PycharmProjects\\monthlyenergybalance\\tests_data\\weather\\inseldb_Summerland.cli' self._complete_city.climate_file = Path(cli) self._complete_city.climate_reference_city = 'Summerland' diff --git a/unittests/test_geometry_factory.py b/unittests/test_geometry_factory.py index db5ae4bb..d3fc1473 100644 --- a/unittests/test_geometry_factory.py +++ b/unittests/test_geometry_factory.py @@ -55,8 +55,10 @@ class TestGeometryFactory(TestCase): self.assertIsNotNone(building.grounds, 'building grounds is none') self.assertIsNotNone(building.walls, 'building walls is none') self.assertIsNotNone(building.roofs, 'building roofs is none') - self.assertIsNone(building.usage_zones, 'usage zones are defined') - self.assertIsNone(building.thermal_zones, 'thermal zones are defined') + self.assertIsNotNone(building.internal_zones, 'building internal zones is none') + for internal_zone in building.internal_zones: + self.assertIsNone(internal_zone.usage_zones, 'usage zones are defined') + self.assertIsNone(internal_zone.thermal_zones, 'thermal zones are defined') self.assertIsNone(building.basement_heated, 'building basement_heated is not none') self.assertIsNone(building.attic_heated, 'building attic_heated is not none') self.assertIsNone(building.terrains, 'building terrains is not none') @@ -67,7 +69,6 @@ class TestGeometryFactory(TestCase): self.assertEqual(len(building.heating), 0, 'building heating is not none') self.assertEqual(len(building.cooling), 0, 'building cooling is not none') self.assertIsNotNone(building.eave_height, 'building eave height is none') - self.assertIsNone(building.storeys, 'building storeys are defined') self.assertIsNotNone(building.roof_type, 'building roof type is none') self.assertIsNotNone(building.floor_area, 'building floor_area is none') self.assertIsNone(building.households, 'building households is not none') diff --git a/unittests/test_schedules_factory.py b/unittests/test_schedules_factory.py index 0506abae..f59f3cf6 100644 --- a/unittests/test_schedules_factory.py +++ b/unittests/test_schedules_factory.py @@ -31,30 +31,10 @@ class TestSchedulesFactory(TestCase): ConstructionFactory('nrel', _city).enrich() self.assertIsNotNone(_city, 'city is none') for building in _city.buildings: - building.function = GeometryHelper.hft_to_function[building.function] + building.function = GeometryHelper.libs_function_from_hft(building.function) UsageFactory('hft', _city).enrich() return _city - def test_comnet_archetypes(self): - """ - Enrich the city with commet schedule archetypes and verify it - """ - file = (self._example_path / 'one_building_in_kelowna.gml').resolve() - city = self._get_citygml(file) - occupancy_handler = 'comnet' - SchedulesFactory(occupancy_handler, 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.assertIsNot(len(usage_zone.schedules), 0, 'no usage_zones schedules defined') - for schedule in usage_zone.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) - def test_doe_idf_archetypes(self): """ Enrich the city with doe_idf schedule archetypes and verify it @@ -64,7 +44,22 @@ class TestSchedulesFactory(TestCase): occupancy_handler = 'doe_idf' SchedulesFactory(occupancy_handler, city).enrich() for building in city.buildings: - for usage_zone in building.usage_zones: - for schedule in usage_zone.schedules: - print(schedule) - print(usage_zone.schedules[schedule]) + 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_usage_factory.py b/unittests/test_usage_factory.py index bae8dc2c..c3d2a0fe 100644 --- a/unittests/test_usage_factory.py +++ b/unittests/test_usage_factory.py @@ -52,7 +52,7 @@ class TestUsageFactory(TestCase): self.assertIsNotNone(building.roofs, 'building roofs is none') for internal_zone in building.internal_zones: self.assertTrue(len(internal_zone.usage_zones) > 0, 'usage zones are not defined') - self.assertIsNone(building.thermal_zones, 'thermal zones are defined') + self.assertIsNone(internal_zone.thermal_zones, 'thermal zones are defined') self.assertIsNone(building.basement_heated, 'building basement_heated is not none') self.assertIsNone(building.attic_heated, 'building attic_heated is not none') self.assertIsNone(building.terrains, 'building terrains is not none') @@ -63,7 +63,6 @@ class TestUsageFactory(TestCase): self.assertEqual(len(building.heating), 0, 'building heating is not none') self.assertEqual(len(building.cooling), 0, 'building cooling is not none') self.assertIsNotNone(building.eave_height, 'building eave height is none') - self.assertIsNone(building.storeys, 'building storeys are defined') self.assertIsNotNone(building.roof_type, 'building roof type is none') self.assertIsNotNone(building.floor_area, 'building floor_area is none') self.assertIsNone(building.households, 'building households is not none') @@ -75,7 +74,6 @@ class TestUsageFactory(TestCase): self.assertIsNotNone(usage_zone.get_internal_gains, 'internal gains is none') self.assertIsNotNone(usage_zone.hours_day, 'hours per day is none') self.assertIsNotNone(usage_zone.days_year, 'days per year is none') - self.assertIsNotNone(usage_zone.mechanical_air_change, 'mechanical air change is none') self.assertIsNotNone(usage_zone.thermal_control, 'thermal control is none') self.assertIsNotNone(usage_zone.thermal_control.mean_heating_set_point, 'control heating set point is none') self.assertIsNotNone(usage_zone.thermal_control.heating_set_back, 'control heating set back is none') @@ -88,7 +86,7 @@ class TestUsageFactory(TestCase): file = 'pluto_building.gml' city = self._get_citygml(file) for building in city.buildings: - building.function = GeometryHelper.function_from_pluto(building.function) + building.function = GeometryHelper.libs_function_from_pluto(building.function) UsageFactory('comnet', city).enrich() self._check_buildings(city) @@ -97,6 +95,7 @@ class TestUsageFactory(TestCase): self.assertIsNot(len(internal_zone.usage_zones), 0, 'no building usage_zones defined') for usage_zone in internal_zone.usage_zones: self._check_usage_zone(usage_zone) + self.assertIsNotNone(usage_zone.mechanical_air_change, 'mechanical air change is none') self.assertIsNotNone(usage_zone.thermal_control.heating_set_point_schedules, 'control heating set point schedule is none') self.assertIsNotNone(usage_zone.thermal_control.cooling_set_point_schedules, @@ -141,6 +140,7 @@ class TestUsageFactory(TestCase): self.assertIsNot(len(internal_zone.usage_zones), 0, 'no building usage_zones defined') for usage_zone in internal_zone.usage_zones: self._check_usage_zone(usage_zone) + self.assertIsNotNone(usage_zone.mechanical_air_change, 'mechanical air change is none') self.assertIsNotNone(usage_zone.not_detailed_source_mean_annual_internal_gains, 'not detailed internal gains is none') @@ -151,7 +151,7 @@ class TestUsageFactory(TestCase): file = 'pluto_building.gml' city = self._get_citygml(file) for building in city.buildings: - building.function = GeometryHelper.function_from_pluto(building.function) + building.function = GeometryHelper.libs_function_from_pluto(building.function) UsageFactory('hft', city).enrich() self._check_buildings(city) @@ -160,6 +160,7 @@ class TestUsageFactory(TestCase): self.assertIsNot(len(internal_zone.usage_zones), 0, 'no building usage_zones defined') for usage_zone in internal_zone.usage_zones: self._check_usage_zone(usage_zone) + self.assertIsNone(usage_zone.mechanical_air_change, 'mechanical air change is not none') self.assertIsNotNone(usage_zone.thermal_control.heating_set_point_schedules, 'control heating set point schedule is none') self.assertIsNotNone(usage_zone.thermal_control.cooling_set_point_schedules, @@ -167,24 +168,13 @@ class TestUsageFactory(TestCase): self.assertIsNotNone(usage_zone.occupancy, 'occupancy is none') occupancy = usage_zone.occupancy self.assertIsNotNone(occupancy.occupancy_density, 'occupancy density is none') - self.assertIsNotNone(occupancy.latent_internal_gain, 'occupancy latent internal gain is none') - self.assertIsNotNone(occupancy.sensible_convective_internal_gain, - 'occupancy sensible convective internal gain is none') - self.assertIsNotNone(occupancy.sensible_radiative_internal_gain, - 'occupancy sensible radiant internal gain is none') - self.assertIsNotNone(occupancy.occupancy_schedules, 'occupancy schedule is none') + self.assertIsNone(occupancy.latent_internal_gain, 'occupancy latent internal gain is none') + self.assertIsNone(occupancy.sensible_convective_internal_gain, + 'occupancy sensible convective internal gain is not none') + self.assertIsNone(occupancy.sensible_radiative_internal_gain, + 'occupancy sensible radiant internal gain is not none') + self.assertIsNone(occupancy.occupancy_schedules, 'occupancy schedule is not none') self.assertIsNone(occupancy.occupants, 'occupancy density is not none') - self.assertIsNotNone(usage_zone.lighting, 'lighting is none') - lighting = usage_zone.lighting - self.assertIsNotNone(lighting.lighting_density, 'lighting density is none') - self.assertIsNotNone(lighting.latent_fraction, 'lighting latent fraction is none') - self.assertIsNotNone(lighting.convective_fraction, 'lighting convective fraction is none') - self.assertIsNotNone(lighting.radiative_fraction, 'lighting radiant fraction is none') - self.assertIsNotNone(lighting.schedules, 'lighting schedule is none') - self.assertIsNotNone(usage_zone.appliances, 'appliances is none') - appliances = usage_zone.appliances - self.assertIsNotNone(appliances.appliances_density, 'appliances density is none') - self.assertIsNotNone(appliances.latent_fraction, 'appliances latent fraction is none') - self.assertIsNotNone(appliances.convective_fraction, 'appliances convective fraction is none') - self.assertIsNotNone(appliances.radiative_fraction, 'appliances radiant fraction is none') - self.assertIsNotNone(appliances.schedules, 'appliances schedule is none') + self.assertIsNone(usage_zone.lighting, 'lighting is not none') + self.assertIsNone(usage_zone.appliances, 'appliances is not none') +