From f3208f1735228c89d1ff38e952c330db967d3017 Mon Sep 17 00:00:00 2001 From: p_monsalvete Date: Tue, 25 Jul 2023 11:40:47 -0400 Subject: [PATCH 1/6] starting the process of changing thermal_zones generation, NOT working version --- hub/city_model_structure/building.py | 2 +- .../building_demand/construction.py | 134 ++++++++++++ .../building_demand/internal_zone.py | 7 +- .../building_demand/layer.py | 180 ++++++++++++++-- .../building_demand/material.py | 193 ----------------- .../building_demand/thermal_archetype.py | 126 +++++++++++ hub/helpers/thermal_zones_creation.py | 138 ++++++++++++ .../construction/eilat_physics_parameters.py | 199 ++++-------------- .../construction/nrcan_physics_parameters.py | 187 ++++------------ .../construction/nrel_physics_parameters.py | 167 ++++----------- tests/test_exports.py | 1 + 11 files changed, 682 insertions(+), 652 deletions(-) create mode 100644 hub/city_model_structure/building_demand/construction.py delete mode 100644 hub/city_model_structure/building_demand/material.py create mode 100644 hub/city_model_structure/building_demand/thermal_archetype.py create mode 100644 hub/helpers/thermal_zones_creation.py diff --git a/hub/city_model_structure/building.py b/hub/city_model_structure/building.py index 67f2add3..3c318232 100644 --- a/hub/city_model_structure/building.py +++ b/hub/city_model_structure/building.py @@ -114,7 +114,7 @@ class Building(CityObject): :return: [InternalZone] """ if self._internal_zones is None: - self._internal_zones = [InternalZone(self.surfaces, self.floor_area)] + self._internal_zones = [InternalZone(self.surfaces, self.floor_area, self.volume)] return self._internal_zones @property diff --git a/hub/city_model_structure/building_demand/construction.py b/hub/city_model_structure/building_demand/construction.py new file mode 100644 index 00000000..ad82999d --- /dev/null +++ b/hub/city_model_structure/building_demand/construction.py @@ -0,0 +1,134 @@ +""" +Construction thermal parameters +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2023 Concordia CERC group +Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" + +from hub.city_model_structure.building_demand.layer import Layer + + +class Construction: + """ + Construction class + """ + def __init__(self): + self._type = None + self._layers = None + self._window_ratio = None + self._window_frame_ratio = None + self._window_g_value = None + self._window_overall_u_value = None + self._window_type = None + + @property + def type(self): + """ + Get construction type + :return: str + """ + return self._type + + @type.setter + def type(self, value): + """ + Set construction type + :param value: str + """ + self._type = value + + @property + def layers(self) -> [Layer]: + """ + Get layers + :return: [layer] + """ + return self._layers + + @layers.setter + def layers(self, value): + """ + Set layers + :param value: [layer] + """ + self._layers = value + + @property + def window_ratio(self): + """ + Get window ratio + :return: dict + """ + return self._window_ratio + + @window_ratio.setter + def window_ratio(self, value): + """ + Set window ratio + :param value: dict + """ + self._window_ratio = value + + @property + def window_frame_ratio(self): + """ + Get window frame ratio + :return: float + """ + return self._window_frame_ratio + + @window_frame_ratio.setter + def window_frame_ratio(self, value): + """ + Set window frame ratio + :param value: float + """ + self._window_frame_ratio = value + + @property + def window_g_value(self): + """ + Get transparent surface g-value + :return: float + """ + return self._window_g_value + + @window_g_value.setter + def window_g_value(self, value): + """ + Set transparent surface g-value + :param value: float + """ + self._window_g_value = value + + @property + def window_overall_u_value(self): + """ + Get transparent surface overall U-value in W/m2K + :return: float + """ + return self._window_overall_u_value + + @window_overall_u_value.setter + def window_overall_u_value(self, value): + """ + Set transparent surface overall U-value in W/m2K + :param value: float + """ + self._window_overall_u_value = value + + @property + def window_type(self): + """ + Get transparent surface type, 'window' or 'skylight' + :return: str + """ + return self._window_type + + @window_type.setter + def window_type(self, value): + """ + Set transparent surface type, 'window' or 'skylight' + :return: str + """ + self._window_type = value diff --git a/hub/city_model_structure/building_demand/internal_zone.py b/hub/city_model_structure/building_demand/internal_zone.py index c8669652..6bc2f776 100644 --- a/hub/city_model_structure/building_demand/internal_zone.py +++ b/hub/city_model_structure/building_demand/internal_zone.py @@ -11,17 +11,18 @@ from hub.city_model_structure.building_demand.usage import Usage from hub.city_model_structure.building_demand.thermal_zone import ThermalZone from hub.city_model_structure.attributes.polyhedron import Polyhedron from hub.city_model_structure.energy_systems.hvac_system import HvacSystem +from hub.helpers.thermal_zones_creation import ThermalZonesCreation class InternalZone: """ InternalZone class """ - def __init__(self, surfaces, area): + def __init__(self, surfaces, area, volume): self._surfaces = surfaces self._id = None self._geometry = None - self._volume = None + self._volume = volume self._area = area self._thermal_zones = None self._usages = None @@ -64,7 +65,7 @@ class InternalZone: Get internal zone volume in cubic meters :return: float """ - return self.geometry.volume + return self._volume @property def area(self): diff --git a/hub/city_model_structure/building_demand/layer.py b/hub/city_model_structure/building_demand/layer.py index 09853c62..3075f0bc 100644 --- a/hub/city_model_structure/building_demand/layer.py +++ b/hub/city_model_structure/building_demand/layer.py @@ -4,9 +4,9 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2022 Concordia CERC group Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ + import uuid from typing import Union -from hub.city_model_structure.building_demand.material import Material class Layer: @@ -14,9 +14,17 @@ class Layer: Layer class """ def __init__(self): - self._material = None self._thickness = None self._id = None + self._name = None + self._conductivity = None + self._specific_heat = None + self._density = None + self._solar_absorptance = None + self._thermal_absorptance = None + self._visible_absorptance = None + self._no_mass = False + self._thermal_resistance = None @property def id(self): @@ -28,22 +36,6 @@ class Layer: self._id = uuid.uuid4() return self._id - @property - def material(self) -> Material: - """ - Get layer material - :return: Material - """ - return self._material - - @material.setter - def material(self, value): - """ - Set layer material - :param value: Material - """ - self._material = value - @property def thickness(self) -> Union[None, float]: """ @@ -60,3 +52,155 @@ class Layer: """ if value is not None: self._thickness = float(value) + + @property + def name(self): + """ + Get material name + :return: str + """ + return self._name + + @name.setter + def name(self, value): + """ + Set material name + :param value: string + """ + self._name = str(value) + + @property + def conductivity(self) -> Union[None, float]: + """ + Get material conductivity in W/mK + :return: None or float + """ + return self._conductivity + + @conductivity.setter + def conductivity(self, value): + """ + Set material conductivity in W/mK + :param value: float + """ + if value is not None: + self._conductivity = float(value) + + @property + def specific_heat(self) -> Union[None, float]: + """ + Get material conductivity in J/kgK + :return: None or float + """ + return self._specific_heat + + @specific_heat.setter + def specific_heat(self, value): + """ + Get material conductivity in J/kgK + :param value: float + """ + if value is not None: + self._specific_heat = float(value) + + @property + def density(self) -> Union[None, float]: + """ + Get material density in kg/m3 + :return: None or float + """ + return self._density + + @density.setter + def density(self, value): + """ + Set material density + :param value: float + """ + if value is not None: + self._density = float(value) + + @property + def solar_absorptance(self) -> Union[None, float]: + """ + Get material solar absorptance + :return: None or float + """ + return self._solar_absorptance + + @solar_absorptance.setter + def solar_absorptance(self, value): + """ + Set material solar absorptance + :param value: float + """ + if value is not None: + self._solar_absorptance = float(value) + + @property + def thermal_absorptance(self) -> Union[None, float]: + """ + Get material thermal absorptance + :return: None or float + """ + return self._thermal_absorptance + + @thermal_absorptance.setter + def thermal_absorptance(self, value): + """ + Set material thermal absorptance + :param value: float + """ + if value is not None: + self._thermal_absorptance = float(value) + + @property + def visible_absorptance(self) -> Union[None, float]: + """ + Get material visible absorptance + :return: None or float + """ + return self._visible_absorptance + + @visible_absorptance.setter + def visible_absorptance(self, value): + """ + Set material visible absorptance + :param value: float + """ + if value is not None: + self._visible_absorptance = float(value) + + @property + def no_mass(self) -> Union[None, bool]: + """ + Get material no mass flag + :return: None or Boolean + """ + return self._no_mass + + @no_mass.setter + def no_mass(self, value): + """ + Set material no mass flag + :param value: Boolean + """ + if value is not None: + self._no_mass = value + + @property + def thermal_resistance(self) -> Union[None, float]: + """ + Get material thermal resistance in m2K/W + :return: None or float + """ + return self._thermal_resistance + + @thermal_resistance.setter + def thermal_resistance(self, value): + """ + Set material thermal resistance in m2K/W + :param value: float + """ + if value is not None: + self._thermal_resistance = float(value) diff --git a/hub/city_model_structure/building_demand/material.py b/hub/city_model_structure/building_demand/material.py deleted file mode 100644 index ebf97ced..00000000 --- a/hub/city_model_structure/building_demand/material.py +++ /dev/null @@ -1,193 +0,0 @@ -""" -Material module -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Concordia CERC group -Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca -""" - -from typing import Union - - -class Material: - """ - Material class - """ - def __init__(self): - self._id = None - self._name = None - self._conductivity = None - self._specific_heat = None - self._density = None - self._solar_absorptance = None - self._thermal_absorptance = None - self._visible_absorptance = None - self._no_mass = False - self._thermal_resistance = None - - @property - def id(self): - """ - Get material id - :return: str - """ - return self._id - - @id.setter - def id(self, value): - """ - Set material id - :param value: str - """ - self._id = value - - @property - def name(self): - """ - Get material name - :return: str - """ - return self._name - - @name.setter - def name(self, value): - """ - Set material name - :param value: string - """ - self._name = str(value) - - @property - def conductivity(self) -> Union[None, float]: - """ - Get material conductivity in W/mK - :return: None or float - """ - return self._conductivity - - @conductivity.setter - def conductivity(self, value): - """ - Set material conductivity in W/mK - :param value: float - """ - if value is not None: - self._conductivity = float(value) - - @property - def specific_heat(self) -> Union[None, float]: - """ - Get material conductivity in J/kgK - :return: None or float - """ - return self._specific_heat - - @specific_heat.setter - def specific_heat(self, value): - """ - Get material conductivity in J/kgK - :param value: float - """ - if value is not None: - self._specific_heat = float(value) - - @property - def density(self) -> Union[None, float]: - """ - Get material density in kg/m3 - :return: None or float - """ - return self._density - - @density.setter - def density(self, value): - """ - Set material density - :param value: float - """ - if value is not None: - self._density = float(value) - - @property - def solar_absorptance(self) -> Union[None, float]: - """ - Get material solar absorptance - :return: None or float - """ - return self._solar_absorptance - - @solar_absorptance.setter - def solar_absorptance(self, value): - """ - Set material solar absorptance - :param value: float - """ - if value is not None: - self._solar_absorptance = float(value) - - @property - def thermal_absorptance(self) -> Union[None, float]: - """ - Get material thermal absorptance - :return: None or float - """ - return self._thermal_absorptance - - @thermal_absorptance.setter - def thermal_absorptance(self, value): - """ - Set material thermal absorptance - :param value: float - """ - if value is not None: - self._thermal_absorptance = float(value) - - @property - def visible_absorptance(self) -> Union[None, float]: - """ - Get material visible absorptance - :return: None or float - """ - return self._visible_absorptance - - @visible_absorptance.setter - def visible_absorptance(self, value): - """ - Set material visible absorptance - :param value: float - """ - if value is not None: - self._visible_absorptance = float(value) - - @property - def no_mass(self) -> Union[None, bool]: - """ - Get material no mass flag - :return: None or Boolean - """ - return self._no_mass - - @no_mass.setter - def no_mass(self, value): - """ - Set material no mass flag - :param value: Boolean - """ - if value is not None: - self._no_mass = value - - @property - def thermal_resistance(self) -> Union[None, float]: - """ - Get material thermal resistance in m2K/W - :return: None or float - """ - return self._thermal_resistance - - @thermal_resistance.setter - def thermal_resistance(self, value): - """ - Set material thermal resistance in m2K/W - :param value: float - """ - if value is not None: - self._thermal_resistance = float(value) diff --git a/hub/city_model_structure/building_demand/thermal_archetype.py b/hub/city_model_structure/building_demand/thermal_archetype.py new file mode 100644 index 00000000..47daf8a0 --- /dev/null +++ b/hub/city_model_structure/building_demand/thermal_archetype.py @@ -0,0 +1,126 @@ +""" +Thermal archetype module +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Concordia CERC group +Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" + +from hub.city_model_structure.building_demand.construction import Construction + + +class ThermalArchetype: + """ + ThermalArchetype class + """ + def __init__(self): + self._constructions = None + self._average_storey_height = None + self._thermal_capacity = None + self._extra_loses_due_to_thermal_bridges = None + self._indirect_heated_ratio = None + self._infiltration_rate_for_ventilation_system_off = None + self._infiltration_rate_for_ventilation_system_on = None + + @property + def constructions(self) -> [Construction]: + """ + Get archetype constructions + :return: [Construction] + """ + return self._constructions + + @property + def average_storey_height(self): + """ + Get average storey height in m + :return: float + """ + return self._average_storey_height + + @average_storey_height.setter + def average_storey_height(self, value): + """ + Set average storey height in m + :param value: float + """ + self._average_storey_height = value + + @property + def thermal_capacity(self): + """ + Get thermal capacity in J/m3K + :return: float + """ + return self._thermal_capacity + + @thermal_capacity.setter + def thermal_capacity(self, value): + """ + Set thermal capacity in J/m3K + :param value: float + """ + self._thermal_capacity = value + + @property + def extra_loses_due_to_thermal_bridges(self): + """ + Get extra loses due to thermal bridges in W/m2K + :return: float + """ + return self._extra_loses_due_to_thermal_bridges + + @extra_loses_due_to_thermal_bridges.setter + def extra_loses_due_to_thermal_bridges(self, value): + """ + Set extra loses due to thermal bridges in W/m2K + :param value: float + """ + self._extra_loses_due_to_thermal_bridges = value + + @property + def indirect_heated_ratio(self): + """ + Get indirect heated area ratio + :return: float + """ + return self._indirect_heated_ratio + + @indirect_heated_ratio.setter + def indirect_heated_ratio(self, value): + """ + Set indirect heated area ratio + :param value: float + """ + self._indirect_heated_ratio = value + + @property + def infiltration_rate_for_ventilation_system_off(self): + """ + Get infiltration rate for ventilation system off in ACH + :return: float + """ + return self._infiltration_rate_for_ventilation_system_off + + @infiltration_rate_for_ventilation_system_off.setter + def infiltration_rate_for_ventilation_system_off(self, value): + """ + Set infiltration rate for ventilation system off in ACH + :param value: float + """ + self._infiltration_rate_for_ventilation_system_off = value + + @property + def infiltration_rate_for_ventilation_system_on(self): + """ + Get infiltration rate for ventilation system on in ACH + :return: float + """ + return self._infiltration_rate_for_ventilation_system_on + + @infiltration_rate_for_ventilation_system_on.setter + def infiltration_rate_for_ventilation_system_on(self, value): + """ + Set infiltration rate for ventilation system on in ACH + :param value: float + """ + self._infiltration_rate_for_ventilation_system_on = value diff --git a/hub/helpers/thermal_zones_creation.py b/hub/helpers/thermal_zones_creation.py new file mode 100644 index 00000000..1f5edc2e --- /dev/null +++ b/hub/helpers/thermal_zones_creation.py @@ -0,0 +1,138 @@ +""" +Thermal zones creation module +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2023 Concordia CERC group +Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" + +from hub.imports.construction.helpers.storeys_generation import StoreysGeneration +from hub.city_model_structure.building_demand.thermal_zone import ThermalZone +from hub.city_model_structure.building_demand.thermal_boundary import ThermalBoundary + + +class ThermalZonesCreation: + """ + PeakLoads class + """ + def __init__(self, building=None): + self._building = building + + # todo: ATTENTION!! +# try: +# thermal_boundary.window_ratio = catalog_construction.window_ratio +# except ValueError: +# # This is the normal operation way when the windows are defined in the geometry +# continue + +# # The agreement is that the layers are defined from outside to inside +# external_layer = catalog_construction.layers[0] +# external_surface = thermal_boundary.parent_surface +# external_surface.short_wave_reflectance = 1 - external_layer.material.solar_absorptance +# external_surface.long_wave_emittance = 1 - external_layer.material.solar_absorptance +# internal_layer = catalog_construction.layers[len(catalog_construction.layers) - 1] +# internal_surface = thermal_boundary.internal_surface +# internal_surface.short_wave_reflectance = 1 - internal_layer.material.solar_absorptance +# internal_surface.long_wave_emittance = 1 - internal_layer.material.solar_absorptance + +# if thermal_boundary.type in (cte.WALL, cte.ROOF): +# if catalog_construction.window is not None: +# if -math.sqrt(2) / 2 < math.sin(thermal_boundary.parent_surface.azimuth) < math.sqrt(2) / 2: +# if 0 < math.cos(thermal_boundary.parent_surface.azimuth): +# thermal_boundary.window_ratio = \ +# float(catalog_construction.window_ratio['north']) / 100 +# else: +# thermal_boundary.window_ratio = \ +# float(catalog_construction.window_ratio['south']) / 100 +# elif math.sqrt(2) / 2 <= math.sin(thermal_boundary.parent_surface.azimuth): +# thermal_boundary.window_ratio = \ +# float(catalog_construction.window_ratio['east']) / 100 +# else: +# thermal_boundary.window_ratio = \ +# float(catalog_construction.window_ratio['west']) / 100 + + @property + def thermal_zones_from_internal_zones(self) -> [ThermalZone]: + """ + Create and get thermal zones as 1 per each internal zone + :return: [ThermalZone] + """ + _thermal_zones = [] + _thermal_boundaries = [] + for internal_zone in self._building.internal_zones: + for surface in internal_zone.surfaces: + if surface.holes_polygons is None: + windows_areas = None + else: + windows_areas = [] + for hole in surface.holes_polygons: + windows_areas.append(hole.area) + _thermal_boundary = ThermalBoundary(surface, surface.solid_polygon.area, windows_areas) + _thermal_boundaries.append(_thermal_boundary) + _thermal_zone = ThermalZone(_thermal_boundaries, internal_zone, internal_zone.volume, internal_zone.floor_area) + _thermal_zones.append(_thermal_zone) + return _thermal_zones + + @property + def thermal_zones_from_storeys(self): + """ + Create and get thermal zones as 1 per each storey + :return: [ThermalZone] + """ + raise NotImplementedError + + @staticmethod + def _create_storeys(building, archetype, divide_in_storeys): + building.average_storey_height = archetype.average_storey_height + thermal_zones = StoreysGeneration(building, building.internal_zones[0], + divide_in_storeys=divide_in_storeys).thermal_zones + building.internal_zones[0].thermal_zones = thermal_zones + + # 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 + + @staticmethod + def _search_construction_in_archetype(archetype, construction_type): + construction_archetypes = archetype.constructions + for construction_archetype in construction_archetypes: + if str(construction_type) == str(construction_archetype.type): + return construction_archetype + return None diff --git a/hub/imports/construction/eilat_physics_parameters.py b/hub/imports/construction/eilat_physics_parameters.py index 607af32b..664ea121 100644 --- a/hub/imports/construction/eilat_physics_parameters.py +++ b/hub/imports/construction/eilat_physics_parameters.py @@ -6,15 +6,13 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ import logging -import math -import hub.helpers.constants as cte from hub.catalog_factories.construction_catalog_factory import ConstructionCatalogFactory +from hub.city_model_structure.building_demand.thermal_archetype import ThermalArchetype +from hub.city_model_structure.building_demand.construction import Construction from hub.city_model_structure.building_demand.layer import Layer -from hub.city_model_structure.building_demand.material import Material from hub.helpers.dictionaries import Dictionaries from hub.imports.construction.helpers.construction_helper import ConstructionHelper -from hub.imports.construction.helpers.storeys_generation import StoreysGeneration class EilatPhysicsParameters: @@ -35,7 +33,7 @@ class EilatPhysicsParameters: eilat_catalog = ConstructionCatalogFactory('eilat').catalog for building in city.buildings: if building.function not in Dictionaries().hub_function_to_eilat_construction_function.keys(): - logging.error(f'Building %s has an unknown building function %s', building.name, building.function ) + logging.error(f'Building %s has an unknown building function %s', building.name, building.function) continue function = Dictionaries().hub_function_to_eilat_construction_function[building.function] try: @@ -46,29 +44,8 @@ class EilatPhysicsParameters: f'[%s], building year of construction: %s and climate zone %s', building.name, function, building.function, building.year_of_construction, self._climate_zone) continue - - # if building has no thermal zones defined from geometry, and the building will be divided in storeys, - # one thermal zone per storey is assigned - - if len(building.internal_zones) == 1: - if building.internal_zones[0].thermal_zones is None: - self._create_storeys(building, archetype, self._divide_in_storeys) - if self._divide_in_storeys: - for internal_zone in building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: - thermal_zone.total_floor_area = thermal_zone.footprint_area - else: - number_of_storeys = int(building.eave_height / building.average_storey_height) - thermal_zone = building.internal_zones[0].thermal_zones[0] - thermal_zone.total_floor_area = thermal_zone.footprint_area * number_of_storeys - else: - for internal_zone in building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: - thermal_zone.total_floor_area = thermal_zone.footprint_area - for internal_zone in building.internal_zones: - self._assign_values(internal_zone.thermal_zones, archetype) - for thermal_zone in internal_zone.thermal_zones: - self._calculate_view_factors(thermal_zone) + thermal_archetype = ThermalArchetype() + self._assign_values(thermal_archetype, archetype) @staticmethod def _search_archetype(nrcan_catalog, function, year_of_construction, climate_zone): @@ -81,133 +58,43 @@ class EilatPhysicsParameters: raise KeyError('archetype not found') @staticmethod - def _search_construction_in_archetype(archetype, construction_type): - construction_archetypes = archetype.constructions - for construction_archetype in construction_archetypes: - if str(construction_type) == str(construction_archetype.type): - return construction_archetype - return None + def _assign_values(thermal_archetype, catalog_archetype): + thermal_archetype.extra_loses_due_to_thermal_bridges = catalog_archetype.extra_loses_due_to_thermal_bridges + thermal_archetype.indirect_heated_ratio = 0 + thermal_archetype.infiltration_rate_for_ventilation_system_on = catalog_archetype.infiltration_rate_for_ventilation_system_on + thermal_archetype.infiltration_rate_for_ventilation_system_off = catalog_archetype.infiltration_rate_for_ventilation_system_off + effective_thermal_capacity = 0 + for catalog_construction in catalog_archetype.constructions: + construction = Construction() + construction.window_ratio = catalog_construction.window_ratio + _layers = [] + total_thickness = 0 + for layer_archetype in catalog_construction.layers: + layer = Layer() + layer.thickness = layer_archetype.thickness + total_thickness += layer_archetype.thickness + archetype_material = layer_archetype.material + layer.name = archetype_material.name + layer.no_mass = archetype_material.no_mass + if archetype_material.no_mass: + layer.thermal_resistance = archetype_material.thermal_resistance + else: + layer.density = archetype_material.density + layer.conductivity = archetype_material.conductivity + layer.specific_heat = archetype_material.specific_heat + effective_thermal_capacity += archetype_material.specific_heat \ + * archetype_material.density * layer_archetype.thickness + layer.solar_absorptance = archetype_material.solar_absorptance + layer.thermal_absorptance = archetype_material.thermal_absorptance + layer.visible_absorptance = archetype_material.visible_absorptance + _layers.append(layer) + construction.layers = _layers + effective_thermal_capacity = effective_thermal_capacity / total_thickness - def _assign_values(self, thermal_zones, archetype): - for thermal_zone in thermal_zones: - thermal_zone.additional_thermal_bridge_u_value = archetype.extra_loses_due_to_thermal_bridges - effective_thermal_capacity = 0 - thermal_zone.indirectly_heated_area_ratio = 0 - thermal_zone.infiltration_rate_system_on = archetype.infiltration_rate_for_ventilation_system_on - thermal_zone.infiltration_rate_system_off = archetype.infiltration_rate_for_ventilation_system_off - for thermal_boundary in thermal_zone.thermal_boundaries: - construction_archetype = self._search_construction_in_archetype(archetype, thermal_boundary.type) - thermal_boundary.construction_name = construction_archetype.name - try: - thermal_boundary.window_ratio = 0 - if thermal_boundary.type in (cte.WALL, cte.ROOF): - if construction_archetype.window is not None: - if -math.sqrt(2) / 2 < math.sin(thermal_boundary.parent_surface.azimuth) < math.sqrt(2) / 2: - if 0 < math.cos(thermal_boundary.parent_surface.azimuth): - thermal_boundary.window_ratio = \ - float(construction_archetype.window_ratio['north']) / 100 - else: - thermal_boundary.window_ratio = \ - float(construction_archetype.window_ratio['south']) / 100 - elif math.sqrt(2) / 2 <= math.sin(thermal_boundary.parent_surface.azimuth): - thermal_boundary.window_ratio = \ - float(construction_archetype.window_ratio['east']) / 100 - else: - thermal_boundary.window_ratio = \ - float(construction_archetype.window_ratio['west']) / 100 - except ValueError: - # This is the normal operation way when the windows are defined in the geometry - continue - thermal_boundary.layers = [] - total_thickness = 0 - for layer_archetype in construction_archetype.layers: - layer = Layer() - layer.thickness = layer_archetype.thickness - total_thickness += layer_archetype.thickness - material = Material() - archetype_material = layer_archetype.material - material.name = archetype_material.name - material.id = archetype_material.id - material.no_mass = archetype_material.no_mass - if archetype_material.no_mass: - material.thermal_resistance = archetype_material.thermal_resistance - else: - material.density = archetype_material.density - material.conductivity = archetype_material.conductivity - material.specific_heat = archetype_material.specific_heat - effective_thermal_capacity += archetype_material.specific_heat \ - * archetype_material.density * layer_archetype.thickness - material.solar_absorptance = archetype_material.solar_absorptance - material.thermal_absorptance = archetype_material.thermal_absorptance - material.visible_absorptance = archetype_material.visible_absorptance - layer.material = material - thermal_boundary.layers.append(layer) + if catalog_construction.window is not None: + window_archetype = catalog_construction.window + construction.window_frame_ratio = window_archetype.frame_ratio + construction.window_g_value = window_archetype.g_value + construction.window_overall_u_value = window_archetype.overall_u_value - effective_thermal_capacity = effective_thermal_capacity / total_thickness - # The agreement is that the layers are defined from outside to inside - external_layer = construction_archetype.layers[0] - external_surface = thermal_boundary.parent_surface - external_surface.short_wave_reflectance = 1 - external_layer.material.solar_absorptance - external_surface.long_wave_emittance = 1 - external_layer.material.solar_absorptance - internal_layer = construction_archetype.layers[len(construction_archetype.layers) - 1] - internal_surface = thermal_boundary.internal_surface - internal_surface.short_wave_reflectance = 1 - internal_layer.material.solar_absorptance - internal_surface.long_wave_emittance = 1 - internal_layer.material.solar_absorptance - - for thermal_opening in thermal_boundary.thermal_openings: - if construction_archetype.window is not None: - window_archetype = construction_archetype.window - thermal_opening.construction_name = window_archetype.name - thermal_opening.frame_ratio = window_archetype.frame_ratio - thermal_opening.g_value = window_archetype.g_value - thermal_opening.overall_u_value = window_archetype.overall_u_value - - thermal_zone.effective_thermal_capacity = effective_thermal_capacity - - @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 - - @staticmethod - def _create_storeys(building, archetype, divide_in_storeys): - building.average_storey_height = archetype.average_storey_height - thermal_zones = StoreysGeneration(building, building.internal_zones[0], - divide_in_storeys=divide_in_storeys).thermal_zones - building.internal_zones[0].thermal_zones = thermal_zones + thermal_archetype.thermal_capacity = effective_thermal_capacity diff --git a/hub/imports/construction/nrcan_physics_parameters.py b/hub/imports/construction/nrcan_physics_parameters.py index cf4debcf..523243cc 100644 --- a/hub/imports/construction/nrcan_physics_parameters.py +++ b/hub/imports/construction/nrcan_physics_parameters.py @@ -6,15 +6,13 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ import logging -import math -import hub.helpers.constants as cte from hub.catalog_factories.construction_catalog_factory import ConstructionCatalogFactory +from hub.city_model_structure.building_demand.thermal_archetype import ThermalArchetype +from hub.city_model_structure.building_demand.construction import Construction from hub.city_model_structure.building_demand.layer import Layer -from hub.city_model_structure.building_demand.material import Material from hub.helpers.dictionaries import Dictionaries from hub.imports.construction.helpers.construction_helper import ConstructionHelper -from hub.imports.construction.helpers.storeys_generation import StoreysGeneration class NrcanPhysicsParameters: @@ -35,7 +33,7 @@ class NrcanPhysicsParameters: nrcan_catalog = ConstructionCatalogFactory('nrcan').catalog for building in city.buildings: if building.function not in Dictionaries().hub_function_to_nrcan_construction_function.keys(): - logging.error(f'Building %s has an unknown building function %s', building.name, building.function ) + logging.error(f'Building %s has an unknown building function %s', building.name, building.function) continue function = Dictionaries().hub_function_to_nrcan_construction_function[building.function] try: @@ -46,29 +44,8 @@ class NrcanPhysicsParameters: f'[%s], building year of construction: %s and climate zone %s', building.name, function, building.function, building.year_of_construction, self._climate_zone) continue - - # if building has no thermal zones defined from geometry, and the building will be divided in storeys, - # one thermal zone per storey is assigned - - if len(building.internal_zones) == 1: - if building.internal_zones[0].thermal_zones is None: - self._create_storeys(building, archetype, self._divide_in_storeys) - if self._divide_in_storeys: - for internal_zone in building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: - thermal_zone.total_floor_area = thermal_zone.footprint_area - else: - number_of_storeys = int(building.eave_height / building.average_storey_height) - thermal_zone = building.internal_zones[0].thermal_zones[0] - thermal_zone.total_floor_area = thermal_zone.footprint_area * number_of_storeys - else: - for internal_zone in building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: - thermal_zone.total_floor_area = thermal_zone.footprint_area - for internal_zone in building.internal_zones: - self._assign_values(internal_zone.thermal_zones, archetype) - for thermal_zone in internal_zone.thermal_zones: - self._calculate_view_factors(thermal_zone) + thermal_archetype = ThermalArchetype() + self._assign_values(thermal_archetype, archetype) @staticmethod def _search_archetype(nrcan_catalog, function, year_of_construction, climate_zone): @@ -81,126 +58,36 @@ class NrcanPhysicsParameters: raise KeyError('archetype not found') @staticmethod - def _search_construction_in_archetype(archetype, construction_type): - construction_archetypes = archetype.constructions - for construction_archetype in construction_archetypes: - if str(construction_type) == str(construction_archetype.type): - return construction_archetype - return None + def _assign_values(thermal_archetype, catalog_archetype): + thermal_archetype.extra_loses_due_to_thermal_bridges = catalog_archetype.extra_loses_due_to_thermal_bridges + thermal_archetype.thermal_capacity = catalog_archetype.thermal_capacity + thermal_archetype.indirect_heated_ratio = 0 + thermal_archetype.infiltration_rate_for_ventilation_system_on = catalog_archetype.infiltration_rate_for_ventilation_system_on + thermal_archetype.infiltration_rate_for_ventilation_system_off = catalog_archetype.infiltration_rate_for_ventilation_system_off + for catalog_construction in catalog_archetype.constructions: + construction = Construction() + construction.window_ratio = catalog_construction.window_ratio + _layers = [] + for layer_archetype in catalog_construction.layers: + layer = Layer() + layer.thickness = layer_archetype.thickness + archetype_material = layer_archetype.material + layer.name = archetype_material.name + layer.no_mass = archetype_material.no_mass + if archetype_material.no_mass: + layer.thermal_resistance = archetype_material.thermal_resistance + else: + layer.density = archetype_material.density + layer.conductivity = archetype_material.conductivity + layer.specific_heat = archetype_material.specific_heat + layer.solar_absorptance = archetype_material.solar_absorptance + layer.thermal_absorptance = archetype_material.thermal_absorptance + layer.visible_absorptance = archetype_material.visible_absorptance + _layers.append(layer) + construction.layers = _layers - def _assign_values(self, thermal_zones, archetype): - for thermal_zone in thermal_zones: - thermal_zone.additional_thermal_bridge_u_value = archetype.extra_loses_due_to_thermal_bridges - thermal_zone.effective_thermal_capacity = archetype.thermal_capacity - thermal_zone.indirectly_heated_area_ratio = 0 - thermal_zone.infiltration_rate_system_on = archetype.infiltration_rate_for_ventilation_system_on - thermal_zone.infiltration_rate_system_off = archetype.infiltration_rate_for_ventilation_system_off - for thermal_boundary in thermal_zone.thermal_boundaries: - construction_archetype = self._search_construction_in_archetype(archetype, thermal_boundary.type) - thermal_boundary.construction_name = construction_archetype.name - try: - thermal_boundary.window_ratio = 0 - if thermal_boundary.type in ( cte.WALL, cte.ROOF): - if construction_archetype.window is not None: - if -math.sqrt(2) / 2 < math.sin(thermal_boundary.parent_surface.azimuth) < math.sqrt(2) / 2: - if 0 < math.cos(thermal_boundary.parent_surface.azimuth): - thermal_boundary.window_ratio = \ - float(construction_archetype.window_ratio['north']) / 100 - else: - thermal_boundary.window_ratio = \ - float(construction_archetype.window_ratio['south']) / 100 - elif math.sqrt(2) / 2 <= math.sin(thermal_boundary.parent_surface.azimuth): - thermal_boundary.window_ratio = \ - float(construction_archetype.window_ratio['east']) / 100 - else: - thermal_boundary.window_ratio = \ - float(construction_archetype.window_ratio['west']) / 100 - except ValueError: - # This is the normal operation way when the windows are defined in the geometry - continue - thermal_boundary.layers = [] - for layer_archetype in construction_archetype.layers: - layer = Layer() - layer.thickness = layer_archetype.thickness - material = Material() - archetype_material = layer_archetype.material - material.name = archetype_material.name - material.id = archetype_material.id - material.no_mass = archetype_material.no_mass - if archetype_material.no_mass: - material.thermal_resistance = archetype_material.thermal_resistance - else: - material.density = archetype_material.density - material.conductivity = archetype_material.conductivity - material.specific_heat = archetype_material.specific_heat - material.solar_absorptance = archetype_material.solar_absorptance - material.thermal_absorptance = archetype_material.thermal_absorptance - material.visible_absorptance = archetype_material.visible_absorptance - layer.material = material - thermal_boundary.layers.append(layer) - # The agreement is that the layers are defined from outside to inside - external_layer = construction_archetype.layers[0] - external_surface = thermal_boundary.parent_surface - external_surface.short_wave_reflectance = 1 - external_layer.material.solar_absorptance - external_surface.long_wave_emittance = 1 - external_layer.material.solar_absorptance - internal_layer = construction_archetype.layers[len(construction_archetype.layers) - 1] - internal_surface = thermal_boundary.internal_surface - internal_surface.short_wave_reflectance = 1 - internal_layer.material.solar_absorptance - internal_surface.long_wave_emittance = 1 - internal_layer.material.solar_absorptance - - for thermal_opening in thermal_boundary.thermal_openings: - if construction_archetype.window is not None: - window_archetype = construction_archetype.window - thermal_opening.construction_name = window_archetype.name - thermal_opening.frame_ratio = window_archetype.frame_ratio - thermal_opening.g_value = window_archetype.g_value - thermal_opening.overall_u_value = window_archetype.overall_u_value - - # 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 - - @staticmethod - def _create_storeys(building, archetype, divide_in_storeys): - building.average_storey_height = archetype.average_storey_height - thermal_zones = StoreysGeneration(building, building.internal_zones[0], - divide_in_storeys=divide_in_storeys).thermal_zones - building.internal_zones[0].thermal_zones = thermal_zones + if catalog_construction.window is not None: + window_archetype = catalog_construction.window + construction.window_frame_ratio = window_archetype.frame_ratio + construction.window_g_value = window_archetype.g_value + construction.window_overall_u_value = window_archetype.overall_u_value diff --git a/hub/imports/construction/nrel_physics_parameters.py b/hub/imports/construction/nrel_physics_parameters.py index e01e3cbf..3e1139fa 100644 --- a/hub/imports/construction/nrel_physics_parameters.py +++ b/hub/imports/construction/nrel_physics_parameters.py @@ -8,11 +8,11 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord import logging from hub.catalog_factories.construction_catalog_factory import ConstructionCatalogFactory +from hub.city_model_structure.building_demand.thermal_archetype import ThermalArchetype +from hub.city_model_structure.building_demand.construction import Construction from hub.city_model_structure.building_demand.layer import Layer -from hub.city_model_structure.building_demand.material import Material from hub.helpers.dictionaries import Dictionaries from hub.imports.construction.helpers.construction_helper import ConstructionHelper -from hub.imports.construction.helpers.storeys_generation import StoreysGeneration class NrelPhysicsParameters: @@ -44,28 +44,8 @@ class NrelPhysicsParameters: f' and climate zone {self._climate_zone}\n') continue - # if building has no thermal zones defined from geometry, and the building will be divided in storeys, - # one thermal zone per storey is assigned - if len(building.internal_zones) == 1: - if building.internal_zones[0].thermal_zones is None: - self._create_storeys(building, archetype, self._divide_in_storeys) - if self._divide_in_storeys: - for internal_zone in building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: - thermal_zone.total_floor_area = thermal_zone.footprint_area - else: - number_of_storeys = int(building.eave_height / building.average_storey_height) - thermal_zone = building.internal_zones[0].thermal_zones[0] - thermal_zone.total_floor_area = thermal_zone.footprint_area * number_of_storeys - else: - for internal_zone in building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: - thermal_zone.total_floor_area = thermal_zone.footprint_area - - for internal_zone in building.internal_zones: - self._assign_values(internal_zone.thermal_zones, archetype) - for thermal_zone in internal_zone.thermal_zones: - self._calculate_view_factors(thermal_zone) + thermal_archetype = ThermalArchetype() + self._assign_values(thermal_archetype, archetype) @staticmethod def _search_archetype(nrel_catalog, function, year_of_construction, climate_zone): @@ -80,111 +60,36 @@ class NrelPhysicsParameters: raise KeyError('archetype not found') @staticmethod - def _search_construction_in_archetype(archetype, construction_type): - construction_archetypes = archetype.constructions - for construction_archetype in construction_archetypes: - if str(construction_type) == str(construction_archetype.type): - return construction_archetype - return None + def _assign_values(thermal_archetype, catalog_archetype): + thermal_archetype.extra_loses_due_to_thermal_bridges = catalog_archetype.extra_loses_due_to_thermal_bridges + thermal_archetype.thermal_capacity = catalog_archetype.thermal_capacity + thermal_archetype.indirect_heated_ratio = catalog_archetype.indirect_heated_ratio + thermal_archetype.infiltration_rate_for_ventilation_system_on = catalog_archetype.infiltration_rate_for_ventilation_system_on + thermal_archetype.infiltration_rate_for_ventilation_system_off = catalog_archetype.infiltration_rate_for_ventilation_system_off + for catalog_construction in catalog_archetype.constructions: + construction = Construction() + construction.window_ratio = catalog_construction.window_ratio + _layers = [] + for layer_archetype in catalog_construction.layers: + layer = Layer() + layer.thickness = layer_archetype.thickness + archetype_material = layer_archetype.material + layer.name = archetype_material.name + layer.no_mass = archetype_material.no_mass + if archetype_material.no_mass: + layer.thermal_resistance = archetype_material.thermal_resistance + else: + layer.density = archetype_material.density + layer.conductivity = archetype_material.conductivity + layer.specific_heat = archetype_material.specific_heat + layer.solar_absorptance = archetype_material.solar_absorptance + layer.thermal_absorptance = archetype_material.thermal_absorptance + layer.visible_absorptance = archetype_material.visible_absorptance + _layers.append(layer) + construction.layers = _layers - def _assign_values(self, thermal_zones, archetype): - for thermal_zone in thermal_zones: - thermal_zone.additional_thermal_bridge_u_value = archetype.extra_loses_due_to_thermal_bridges - thermal_zone.effective_thermal_capacity = archetype.thermal_capacity - thermal_zone.indirectly_heated_area_ratio = archetype.indirect_heated_ratio - thermal_zone.infiltration_rate_system_on = archetype.infiltration_rate_for_ventilation_system_on - thermal_zone.infiltration_rate_system_off = archetype.infiltration_rate_for_ventilation_system_off - for thermal_boundary in thermal_zone.thermal_boundaries: - construction_archetype = self._search_construction_in_archetype(archetype, thermal_boundary.type) - thermal_boundary.construction_name = construction_archetype.name - try: - thermal_boundary.window_ratio = construction_archetype.window_ratio - except ValueError: - # This is the normal operation way when the windows are defined in the geometry - continue - thermal_boundary.layers = [] - for layer_archetype in construction_archetype.layers: - layer = Layer() - layer.thickness = layer_archetype.thickness - material = Material() - archetype_material = layer_archetype.material - material.name = archetype_material.name - material.id = archetype_material.id - material.no_mass = archetype_material.no_mass - if archetype_material.no_mass: - material.thermal_resistance = archetype_material.thermal_resistance - else: - material.density = archetype_material.density - material.conductivity = archetype_material.conductivity - material.specific_heat = archetype_material.specific_heat - material.solar_absorptance = archetype_material.solar_absorptance - material.thermal_absorptance = archetype_material.thermal_absorptance - material.visible_absorptance = archetype_material.visible_absorptance - layer.material = material - thermal_boundary.layers.append(layer) - # The agreement is that the layers are defined from outside to inside - external_layer = construction_archetype.layers[0] - external_surface = thermal_boundary.parent_surface - external_surface.short_wave_reflectance = 1 - external_layer.material.solar_absorptance - external_surface.long_wave_emittance = 1 - external_layer.material.solar_absorptance - internal_layer = construction_archetype.layers[len(construction_archetype.layers) - 1] - internal_surface = thermal_boundary.internal_surface - internal_surface.short_wave_reflectance = 1 - internal_layer.material.solar_absorptance - internal_surface.long_wave_emittance = 1 - internal_layer.material.solar_absorptance - - for thermal_opening in thermal_boundary.thermal_openings: - if construction_archetype.window is not None: - window_archetype = construction_archetype.window - thermal_opening.construction_name = window_archetype.name - thermal_opening.frame_ratio = window_archetype.frame_ratio - thermal_opening.g_value = window_archetype.g_value - thermal_opening.overall_u_value = window_archetype.overall_u_value - - # 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 - - @staticmethod - def _create_storeys(building, archetype, divide_in_storeys): - building.average_storey_height = archetype.average_storey_height - thermal_zones = StoreysGeneration(building, building.internal_zones[0], - divide_in_storeys=divide_in_storeys).thermal_zones - building.internal_zones[0].thermal_zones = thermal_zones + if catalog_construction.window is not None: + window_archetype = catalog_construction.window + construction.window_frame_ratio = window_archetype.frame_ratio + construction.window_g_value = window_archetype.g_value + construction.window_overall_u_value = window_archetype.overall_u_value diff --git a/tests/test_exports.py b/tests/test_exports.py index 7607c964..ff0ef9c0 100644 --- a/tests/test_exports.py +++ b/tests/test_exports.py @@ -120,6 +120,7 @@ class TestExports(TestCase): EnergyBuildingsExportsFactory('idf', city, self._output_path).export() UsageFactory('nrcan', city).enrich() WeatherFactory('epw', city).enrich() + print(self._output_path) try: EnergyBuildingsExportsFactory('idf', city, self._output_path).export() except Exception: From a147afe76fbf9deac752f373b1503b52ea27eee6 Mon Sep 17 00:00:00 2001 From: p_monsalvete Date: Mon, 31 Jul 2023 17:01:35 -0400 Subject: [PATCH 2/6] modified thermal zones to be created in different ways depending on the user needs --- hub/city_model_structure/building.py | 23 +++---- .../building_demand/internal_zone.py | 30 +++++--- .../building_demand/thermal_zone.py | 44 +++++++++--- hub/exports/building_energy/energy_ade.py | 2 +- hub/exports/building_energy/idf.py | 14 ++-- .../insel/insel_monthly_energy_balance.py | 10 +-- .../peak_calculation/loads_calculation.py | 12 ++-- hub/helpers/thermal_zones_creation.py | 68 +------------------ .../results/insel_monthly_energry_balance.py | 4 +- hub/persistence/models/city_object.py | 2 +- tests/test_construction_factory.py | 20 +++--- tests/test_custom_insel_block.py | 2 +- tests/test_enrichement.py | 12 ++-- tests/test_geometry_factory.py | 2 +- tests/test_insel_exports.py | 2 +- 15 files changed, 109 insertions(+), 138 deletions(-) diff --git a/hub/city_model_structure/building.py b/hub/city_model_structure/building.py index 3c318232..29a2862a 100644 --- a/hub/city_model_structure/building.py +++ b/hub/city_model_structure/building.py @@ -41,7 +41,7 @@ class Building(CityObject): self._floor_area = None self._roof_type = None self._internal_zones = None - self._thermal_zones = None + self._thermal_zones_from_internal_zones = None self._shell = None self._aliases = [] self._type = 'building' @@ -118,22 +118,19 @@ class Building(CityObject): return self._internal_zones @property - def thermal_zones(self) -> Union[None, List[ThermalZone]]: + def thermal_zones_from_internal_zones(self) -> Union[None, List[ThermalZone]]: """ Get building thermal zones - For Lod up to 3, there can be more than one thermal zone per internal zone. - In LoD 4, there can be more than one internal zone, and therefore, only one thermal zone per internal zone :return: [ThermalZone] """ - if self._thermal_zones is None: - self._thermal_zones = [] + if self._thermal_zones_from_internal_zones is None: + self._thermal_zones_from_internal_zones = [] for internal_zone in self.internal_zones: - if internal_zone.thermal_zones is None: - self._thermal_zones = None - return self._thermal_zones - for thermal_zone in internal_zone.thermal_zones: - self._thermal_zones.append(thermal_zone) - return self._thermal_zones + if internal_zone.thermal_zones_from_internal_zones is None: + self._thermal_zones_from_internal_zones = None + return self._thermal_zones_from_internal_zones + self._thermal_zones_from_internal_zones.append(internal_zone.thermal_zones_from_internal_zones[0]) + return self._thermal_zones_from_internal_zones @property def grounds(self) -> List[Surface]: @@ -621,7 +618,7 @@ class Building(CityObject): def _calculate_working_hours(self): _working_hours = {} for internal_zone in self.internal_zones: - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: _working_hours_per_thermal_zone = {} for schedule in thermal_zone.thermal_control.hvac_availability_schedules: _working_hours_per_schedule = [0] * len(schedule.values) diff --git a/hub/city_model_structure/building_demand/internal_zone.py b/hub/city_model_structure/building_demand/internal_zone.py index 6bc2f776..aa2cefa5 100644 --- a/hub/city_model_structure/building_demand/internal_zone.py +++ b/hub/city_model_structure/building_demand/internal_zone.py @@ -9,9 +9,9 @@ import uuid from typing import Union, List from hub.city_model_structure.building_demand.usage import Usage from hub.city_model_structure.building_demand.thermal_zone import ThermalZone +from hub.city_model_structure.building_demand.thermal_boundary import ThermalBoundary from hub.city_model_structure.attributes.polyhedron import Polyhedron from hub.city_model_structure.energy_systems.hvac_system import HvacSystem -from hub.helpers.thermal_zones_creation import ThermalZonesCreation class InternalZone: @@ -24,7 +24,7 @@ class InternalZone: self._geometry = None self._volume = volume self._area = area - self._thermal_zones = None + self._thermal_zones_from_internal_zones = None self._usages = None self._hvac_system = None @@ -108,17 +108,29 @@ class InternalZone: self._hvac_system = value @property - def thermal_zones(self) -> Union[None, List[ThermalZone]]: + def thermal_zones_from_internal_zones(self) -> Union[None, List[ThermalZone]]: """ - Get building thermal zones + Get building thermal zones as one per internal zone :return: [ThermalZone] """ - return self._thermal_zones + _thermal_boundaries = [] + for surface in self.surfaces: + if surface.holes_polygons is None: + windows_areas = None + else: + windows_areas = [] + for hole in surface.holes_polygons: + windows_areas.append(hole.area) + _thermal_boundary = ThermalBoundary(surface, surface.solid_polygon.area, windows_areas) + _thermal_boundaries.append(_thermal_boundary) + _thermal_zone = ThermalZone(_thermal_boundaries, self, self.volume, self.area) + self._thermal_zones_from_internal_zones = [_thermal_zone] + return self._thermal_zones_from_internal_zones - @thermal_zones.setter - def thermal_zones(self, value): + @thermal_zones_from_internal_zones.setter + def thermal_zones_from_internal_zones(self, value): """ - Set city object thermal zones + Set city object thermal zones as one per internal zone :param value: [ThermalZone] """ - self._thermal_zones = value + self._thermal_zones_from_internal_zones = value diff --git a/hub/city_model_structure/building_demand/thermal_zone.py b/hub/city_model_structure/building_demand/thermal_zone.py index 8d4886d4..42cbfa78 100644 --- a/hub/city_model_structure/building_demand/thermal_zone.py +++ b/hub/city_model_structure/building_demand/thermal_zone.py @@ -221,15 +221,43 @@ class ThermalZone: Get thermal zone view factors matrix :return: [[float]] """ - return self._view_factors_matrix + # todo: review method if windows not in window_ratio but in geometry + if self._view_factors_matrix is None: + total_area = 0 + for thermal_boundary in self.thermal_boundaries: + total_area += thermal_boundary.opaque_area + for thermal_opening in thermal_boundary.thermal_openings: + total_area += thermal_opening.area - @view_factors_matrix.setter - def view_factors_matrix(self, value): - """ - Set thermal zone view factors matrix - :param value: [[float]] - """ - self._view_factors_matrix = value + view_factors_matrix = [] + for thermal_boundary_1 in self.thermal_boundaries: + values = [] + for thermal_boundary_2 in self.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 self.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 self.thermal_boundaries: + values = [] + for thermal_opening_1 in thermal_boundary_1.thermal_openings: + for thermal_boundary_2 in self.thermal_boundaries: + value = thermal_boundary_2.opaque_area / (total_area - thermal_opening_1.area) + values.append(value) + for thermal_boundary in self.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) + self._view_factors_matrix = view_factors_matrix + return self._view_factors_matrix @property def usage_name(self) -> Union[None, str]: diff --git a/hub/exports/building_energy/energy_ade.py b/hub/exports/building_energy/energy_ade.py index bd9dc735..f1365fed 100644 --- a/hub/exports/building_energy/energy_ade.py +++ b/hub/exports/building_energy/energy_ade.py @@ -335,7 +335,7 @@ class EnergyAde: def _thermal_zones(self, building, city): thermal_zones = [] for internal_zone in building.internal_zones: - for index, thermal_zone in enumerate(internal_zone.thermal_zones): + for index, thermal_zone in enumerate(internal_zone.thermal_zones_from_internal_zones): usages = [] for usage in internal_zone.usages: usages.append({'@xlink:href': f'#GML_{usage.id}'}) diff --git a/hub/exports/building_energy/idf.py b/hub/exports/building_energy/idf.py index 898beb55..5d07e709 100644 --- a/hub/exports/building_energy/idf.py +++ b/hub/exports/building_energy/idf.py @@ -510,9 +510,9 @@ class Idf: for building in self._city.buildings: print('building name', building.name) for internal_zone in building.internal_zones: - if internal_zone.thermal_zones is None: + if internal_zone.thermal_zones_from_internal_zones is None: continue - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: for thermal_boundary in thermal_zone.thermal_boundaries: self._add_construction(thermal_boundary) if thermal_boundary.parent_surface.vegetation is not None: @@ -556,7 +556,7 @@ class Idf: self._add_dhw(thermal_zone, building.name) if self._export_type == "Surfaces": if building.name in self._target_buildings or building.name in self._adjacent_buildings: - if building.internal_zones[0].thermal_zones is not None: + if building.internal_zones[0].thermal_zones_from_internal_zones is not None: self._add_surfaces(building, building.name) else: self._add_pure_geometry(building, building.name) @@ -611,7 +611,7 @@ class Idf: num_stories=int(building.storeys_above_ground)) for surface in self._idf.idfobjects[self._SURFACE]: - for thermal_zone in building.thermal_zones: + for thermal_zone in building.thermal_zones_from_internal_zones: for boundary in thermal_zone.thermal_boundaries: if surface.Type == self.idf_surfaces[boundary.surface.type]: surface.Construction_Name = boundary.construction_name @@ -664,7 +664,7 @@ class Idf: idf_surface.setcoords(coordinates) if self._lod >= 3: for internal_zone in building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: for boundary in thermal_zone.thermal_boundaries: self._add_windows_by_vertices(boundary) else: @@ -674,7 +674,7 @@ class Idf: def _add_surfaces(self, building, zone_name): for internal_zone in building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: for boundary in thermal_zone.thermal_boundaries: idf_surface_type = self.idf_surfaces[boundary.parent_surface.type] outside_boundary_condition = 'Outdoors' @@ -711,7 +711,7 @@ class Idf: if self._lod >= 3: for internal_zone in building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: for boundary in thermal_zone.thermal_boundaries: self._add_windows_by_vertices(boundary) else: diff --git a/hub/exports/building_energy/insel/insel_monthly_energy_balance.py b/hub/exports/building_energy/insel/insel_monthly_energy_balance.py index 7055f55a..3509c7c6 100644 --- a/hub/exports/building_energy/insel/insel_monthly_energy_balance.py +++ b/hub/exports/building_energy/insel/insel_monthly_energy_balance.py @@ -44,7 +44,7 @@ class InselMonthlyEnergyBalance: self._insel_files_paths.append(building.name + '.insel') file_name_out = building.name + '.out' output_path = Path(self._path / file_name_out).resolve() - if building.thermal_zones is None: + if building.thermal_zones_from_internal_zones is None: logging.warning('Building %s has missing values. Monthly Energy Balance cannot be processed', building.name) self._contents.append( @@ -126,7 +126,7 @@ class InselMonthlyEnergyBalance: # todo: this method and the insel model have to be reviewed for more than one internal zone internal_zone = building.internal_zones[0] - thermal_zone = internal_zone.thermal_zones[0] + thermal_zone = internal_zone.thermal_zones_from_internal_zones[0] parameters.append(f'{thermal_zone.indirectly_heated_area_ratio} % BP(6) Indirectly heated area ratio') parameters.append(f'{thermal_zone.effective_thermal_capacity / 3600 / building.average_storey_height}' f' % BP(7) Effective heat capacity (Wh/m2K)') @@ -139,7 +139,7 @@ class InselMonthlyEnergyBalance: for i, usage in enumerate(internal_zone.usages): percentage_usage = usage.percentage - parameters.append(f'{internal_zone.thermal_zones[0].total_floor_area * percentage_usage} ' + parameters.append(f'{internal_zone.thermal_zones_from_internal_zones[0].total_floor_area * percentage_usage} ' f'% BP(11) #1 Area of zone {i + 1} (m2)') total_internal_gain = 0 for i_gain in usage.internal_gains: @@ -169,11 +169,11 @@ class InselMonthlyEnergyBalance: infiltration_day = 0 for value in schedule.values: if value == 0: - infiltration_day += internal_zone.thermal_zones[0].infiltration_rate_system_off / 24 + infiltration_day += internal_zone.thermal_zones_from_internal_zones[0].infiltration_rate_system_off / 24 ventilation_day += 0 else: ventilation_value = usage.mechanical_air_change * value - infiltration_value = internal_zone.thermal_zones[0].infiltration_rate_system_off * value + infiltration_value = internal_zone.thermal_zones_from_internal_zones[0].infiltration_rate_system_off * value if ventilation_value >= infiltration_value: ventilation_day += ventilation_value / 24 infiltration_day += 0 diff --git a/hub/helpers/peak_calculation/loads_calculation.py b/hub/helpers/peak_calculation/loads_calculation.py index 5a18867b..63f1ed87 100644 --- a/hub/helpers/peak_calculation/loads_calculation.py +++ b/hub/helpers/peak_calculation/loads_calculation.py @@ -65,7 +65,7 @@ class LoadsCalculation: """ heating_load_transmitted = 0 for internal_zone in self._building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: internal_temperature = thermal_zone.thermal_control.mean_heating_set_point heating_load_transmitted += self._get_load_transmitted(thermal_zone, internal_temperature, ambient_temperature, ground_temperature) @@ -78,7 +78,7 @@ class LoadsCalculation: """ cooling_load_transmitted = 0 for internal_zone in self._building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: internal_temperature = thermal_zone.thermal_control.mean_cooling_set_point cooling_load_transmitted += self._get_load_transmitted(thermal_zone, internal_temperature, ambient_temperature, ground_temperature) @@ -91,7 +91,7 @@ class LoadsCalculation: """ heating_ventilation_load = 0 for internal_zone in self._building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: internal_temperature = thermal_zone.thermal_control.mean_heating_set_point heating_ventilation_load += self._get_load_ventilation(thermal_zone, internal_temperature, ambient_temperature) return heating_ventilation_load @@ -103,7 +103,7 @@ class LoadsCalculation: """ cooling_ventilation_load = 0 for internal_zone in self._building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: internal_temperature = thermal_zone.thermal_control.mean_cooling_set_point cooling_ventilation_load += self._get_load_ventilation(thermal_zone, internal_temperature, ambient_temperature) return cooling_ventilation_load @@ -117,7 +117,7 @@ class LoadsCalculation: cooling_load_lighting = 0 cooling_load_equipment_sensible = 0 for internal_zone in self._building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: cooling_load_occupancy_sensible += (thermal_zone.occupancy.sensible_convective_internal_gain + thermal_zone.occupancy.sensible_radiative_internal_gain) \ * thermal_zone.footprint_area @@ -139,7 +139,7 @@ class LoadsCalculation: """ cooling_load_radiation = 0 for internal_zone in self._building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: for thermal_boundary in thermal_zone.thermal_boundaries: for thermal_opening in thermal_boundary.thermal_openings: radiation = thermal_boundary.parent_surface.global_irradiance[cte.HOUR][irradiance_format][hour] diff --git a/hub/helpers/thermal_zones_creation.py b/hub/helpers/thermal_zones_creation.py index 1f5edc2e..31398ae9 100644 --- a/hub/helpers/thermal_zones_creation.py +++ b/hub/helpers/thermal_zones_creation.py @@ -6,8 +6,6 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ from hub.imports.construction.helpers.storeys_generation import StoreysGeneration -from hub.city_model_structure.building_demand.thermal_zone import ThermalZone -from hub.city_model_structure.building_demand.thermal_boundary import ThermalBoundary class ThermalZonesCreation: @@ -50,28 +48,6 @@ class ThermalZonesCreation: # thermal_boundary.window_ratio = \ # float(catalog_construction.window_ratio['west']) / 100 - @property - def thermal_zones_from_internal_zones(self) -> [ThermalZone]: - """ - Create and get thermal zones as 1 per each internal zone - :return: [ThermalZone] - """ - _thermal_zones = [] - _thermal_boundaries = [] - for internal_zone in self._building.internal_zones: - for surface in internal_zone.surfaces: - if surface.holes_polygons is None: - windows_areas = None - else: - windows_areas = [] - for hole in surface.holes_polygons: - windows_areas.append(hole.area) - _thermal_boundary = ThermalBoundary(surface, surface.solid_polygon.area, windows_areas) - _thermal_boundaries.append(_thermal_boundary) - _thermal_zone = ThermalZone(_thermal_boundaries, internal_zone, internal_zone.volume, internal_zone.floor_area) - _thermal_zones.append(_thermal_zone) - return _thermal_zones - @property def thermal_zones_from_storeys(self): """ @@ -85,49 +61,7 @@ class ThermalZonesCreation: building.average_storey_height = archetype.average_storey_height thermal_zones = StoreysGeneration(building, building.internal_zones[0], divide_in_storeys=divide_in_storeys).thermal_zones - building.internal_zones[0].thermal_zones = thermal_zones - - # 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 + building.internal_zones[0].thermal_zones_from_internal_zones = thermal_zones @staticmethod def _search_construction_in_archetype(archetype, construction_type): diff --git a/hub/imports/results/insel_monthly_energry_balance.py b/hub/imports/results/insel_monthly_energry_balance.py index 4450991d..6cd1409b 100644 --- a/hub/imports/results/insel_monthly_energry_balance.py +++ b/hub/imports/results/insel_monthly_energry_balance.py @@ -45,12 +45,12 @@ class InselMonthlyEnergyBalance: domestic_hot_water_demand = [] lighting_demand = [] appliances_demand = [] - if building.internal_zones[0].thermal_zones is None: + if building.internal_zones[0].thermal_zones_from_internal_zones is None: domestic_hot_water_demand = [0] * 12 lighting_demand = [0] * 12 appliances_demand = [0] * 12 else: - thermal_zone = building.internal_zones[0].thermal_zones[0] + thermal_zone = building.internal_zones[0].thermal_zones_from_internal_zones[0] area = thermal_zone.total_floor_area cold_water = building.cold_water_temperature[cte.MONTH]['epw'] peak_flow = thermal_zone.domestic_hot_water.peak_flow diff --git a/hub/persistence/models/city_object.py b/hub/persistence/models/city_object.py index b7f2ace9..d6044e71 100644 --- a/hub/persistence/models/city_object.py +++ b/hub/persistence/models/city_object.py @@ -58,7 +58,7 @@ class CityObject(Models): self.wall_area = wall_area window_ratio = 0 for internal_zone in building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: for thermal_boundary in thermal_zone.thermal_boundaries: window_ratio = thermal_boundary.window_ratio break diff --git a/tests/test_construction_factory.py b/tests/test_construction_factory.py index 8a3fe556..d56441c2 100644 --- a/tests/test_construction_factory.py +++ b/tests/test_construction_factory.py @@ -106,7 +106,7 @@ class TestConstructionFactory(TestCase): self.assertIsNotNone(building.shell, 'building shell is none') def _check_thermal_zones(self, internal_zone): - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: self.assertIsNotNone(thermal_zone.id, 'thermal_zone id is none') self.assertIsNotNone(thermal_zone.footprint_area, 'thermal_zone floor area is none') self.assertTrue(len(thermal_zone.thermal_boundaries) > 0, 'thermal_zone thermal_boundaries not defined') @@ -133,7 +133,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 no thermal zone') + self.assertIsNotNone(thermal_boundary.thermal_zones_from_internal_zones, 'thermal_boundary delimits no thermal zone') self.assertIsNotNone(thermal_boundary.opaque_area, 'thermal_boundary area is none') self.assertIsNotNone(thermal_boundary.thickness, 'thermal_boundary thickness is none') self.assertIsNotNone(thermal_boundary.type, 'thermal_boundary type is none') @@ -184,7 +184,7 @@ class TestConstructionFactory(TestCase): for building in city.buildings: for internal_zone in building.internal_zones: self._check_thermal_zones(internal_zone) - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: self._check_thermal_boundaries(thermal_zone) for thermal_boundary in thermal_zone.thermal_boundaries: self.assertIsNotNone(thermal_boundary.layers, 'layers is none') @@ -202,7 +202,7 @@ class TestConstructionFactory(TestCase): for building in city.buildings: for internal_zone in building.internal_zones: self._check_thermal_zones(internal_zone) - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: self._check_thermal_boundaries(thermal_zone) for thermal_boundary in thermal_zone.thermal_boundaries: self.assertIsNotNone(thermal_boundary.layers, 'layers is none') @@ -220,7 +220,7 @@ class TestConstructionFactory(TestCase): for building in city.buildings: for internal_zone in building.internal_zones: self._check_thermal_zones(internal_zone) - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: self._check_thermal_boundaries(thermal_zone) for thermal_boundary in thermal_zone.thermal_boundaries: self.assertIsNotNone(thermal_boundary.layers, 'layers is none') @@ -238,7 +238,7 @@ class TestConstructionFactory(TestCase): for building in city.buildings: for internal_zone in building.internal_zones: self._check_thermal_zones(internal_zone) - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: self._check_thermal_boundaries(thermal_zone) for thermal_boundary in thermal_zone.thermal_boundaries: self.assertIsNotNone(thermal_boundary.layers, 'layers is none') @@ -256,7 +256,7 @@ class TestConstructionFactory(TestCase): for building in city.buildings: for internal_zone in building.internal_zones: self._check_thermal_zones(internal_zone) - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: self._check_thermal_boundaries(thermal_zone) for thermal_boundary in thermal_zone.thermal_boundaries: self.assertIsNotNone(thermal_boundary.layers, 'layers is none') @@ -277,7 +277,7 @@ class TestConstructionFactory(TestCase): for building in city.buildings: for internal_zone in building.internal_zones: self._check_thermal_zones(internal_zone) - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: self._check_thermal_boundaries(thermal_zone) for thermal_boundary in thermal_zone.thermal_boundaries: self.assertIsNotNone(thermal_boundary.layers, 'layers is none') @@ -299,7 +299,7 @@ class TestConstructionFactory(TestCase): for building in city.buildings: for internal_zone in building.internal_zones: self._check_thermal_zones(internal_zone) - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: self._check_thermal_boundaries(thermal_zone) for thermal_boundary in thermal_zone.thermal_boundaries: self.assertIsNotNone(thermal_boundary.layers, 'layers is none') @@ -321,7 +321,7 @@ class TestConstructionFactory(TestCase): for building in city.buildings: for internal_zone in building.internal_zones: self._check_thermal_zones(internal_zone) - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: self._check_thermal_boundaries(thermal_zone) for thermal_boundary in thermal_zone.thermal_boundaries: self.assertIsNotNone(thermal_boundary.layers, 'layers is none') diff --git a/tests/test_custom_insel_block.py b/tests/test_custom_insel_block.py index b8683bd2..1b46995d 100644 --- a/tests/test_custom_insel_block.py +++ b/tests/test_custom_insel_block.py @@ -115,7 +115,7 @@ class TestExports(TestCase): self.assertIsNotNone(building.basement_heated, f'building {building.name} basement_heated is none') for internal_zone in building.internal_zones: self.assertIsNotNone(internal_zone.area, f'internal zone {internal_zone.id} area is none') - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: self.assertIsNotNone(thermal_zone.indirectly_heated_area_ratio, f'thermal zone {thermal_zone.id} ' f'indirectly_heated_area_ratio is none') self.assertIsNotNone(thermal_zone.effective_thermal_capacity, f'thermal zone {thermal_zone.id} ' diff --git a/tests/test_enrichement.py b/tests/test_enrichement.py index 4dd6d0e4..531acd6a 100644 --- a/tests/test_enrichement.py +++ b/tests/test_enrichement.py @@ -38,7 +38,7 @@ class TestGeometryFactory(TestCase): self.assertIsNot(len(internal_zone.usages), 0, 'no building usages defined') for usage in internal_zone.usages: self.assertIsNotNone(usage.id, 'usage id is none') - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: self._check_thermal_zone(thermal_zone) def _check_buildings(self, city): @@ -46,7 +46,7 @@ class TestGeometryFactory(TestCase): self.assertIsNotNone(building.internal_zones, 'no internal zones created') for internal_zone in building.internal_zones: self.assertIsNotNone(internal_zone.usages, 'usage zones are not defined') - self.assertIsNotNone(internal_zone.thermal_zones, 'thermal zones are not defined') + self.assertIsNotNone(internal_zone.thermal_zones_from_internal_zones, 'thermal zones are not defined') self.assertIsNone(building.basement_heated, 'building basement_heated is not none') self.assertIsNone(building.attic_heated, 'building attic_heated is not none') self.assertIsNotNone(building.average_storey_height, 'building average_storey_height is none') @@ -105,7 +105,7 @@ class TestGeometryFactory(TestCase): if usage_key == 'comnet': for building in city.buildings: for internal_zone in building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: self._check_extra_thermal_zone(thermal_zone) # usage factory called first city = self._get_citygml(file) @@ -117,7 +117,7 @@ class TestGeometryFactory(TestCase): if usage_key == 'comnet': for building in city.buildings: for internal_zone in building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: self._check_extra_thermal_zone(thermal_zone) def _test_pluto(self, file): @@ -135,7 +135,7 @@ class TestGeometryFactory(TestCase): if usage_key == 'comnet': for building in city.buildings: for internal_zone in building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: self._check_extra_thermal_zone(thermal_zone) # usage factory called first city = self._get_citygml(file) @@ -147,7 +147,7 @@ class TestGeometryFactory(TestCase): if usage_key == 'comnet': for building in city.buildings: for internal_zone in building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: self._check_extra_thermal_zone(thermal_zone) def test_enrichment(self): diff --git a/tests/test_geometry_factory.py b/tests/test_geometry_factory.py index c4ade93b..c098748a 100644 --- a/tests/test_geometry_factory.py +++ b/tests/test_geometry_factory.py @@ -62,7 +62,7 @@ class TestGeometryFactory(TestCase): self.assertIsNotNone(building.internal_zones, 'building internal zones is none') for internal_zone in building.internal_zones: self.assertIsNone(internal_zone.usages, 'usage zones are defined') - self.assertIsNone(internal_zone.thermal_zones, 'thermal zones are defined') + self.assertIsNone(internal_zone.thermal_zones_from_internal_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') diff --git a/tests/test_insel_exports.py b/tests/test_insel_exports.py index ef987000..57604b07 100644 --- a/tests/test_insel_exports.py +++ b/tests/test_insel_exports.py @@ -115,7 +115,7 @@ class TestExports(TestCase): self.assertIsNotNone(building.basement_heated, f'building {building.name} basement_heated is none') for internal_zone in building.internal_zones: self.assertIsNotNone(internal_zone.area, f'internal zone {internal_zone.id} area is none') - for thermal_zone in internal_zone.thermal_zones: + for thermal_zone in internal_zone.thermal_zones_from_internal_zones: self.assertIsNotNone(thermal_zone.indirectly_heated_area_ratio, f'thermal zone {thermal_zone.id} ' f'indirectly_heated_area_ratio is none') self.assertIsNotNone(thermal_zone.effective_thermal_capacity, f'thermal zone {thermal_zone.id} ' From 4b5c037ffa4af65f971fa11049641bbbb66ef21a Mon Sep 17 00:00:00 2001 From: p_monsalvete Date: Wed, 2 Aug 2023 14:46:17 -0400 Subject: [PATCH 3/6] small bug --- hub/city_model_structure/building.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hub/city_model_structure/building.py b/hub/city_model_structure/building.py index 754e7b60..9b085a2a 100644 --- a/hub/city_model_structure/building.py +++ b/hub/city_model_structure/building.py @@ -393,7 +393,7 @@ class Building(CityObject): """ results = {} peak_lighting = 0 - for thermal_zone in self.thermal_zones: + for thermal_zone in self.thermal_zones_from_internal_zones: lighting = thermal_zone.lighting for schedule in lighting.schedules: peak = max(schedule.values) * lighting.density * thermal_zone.total_floor_area @@ -411,7 +411,7 @@ class Building(CityObject): """ results = {} peak_appliances = 0 - for thermal_zone in self.thermal_zones: + for thermal_zone in self.thermal_zones_from_internal_zones: appliances = thermal_zone.appliances for schedule in appliances.schedules: peak = max(schedule.values) * appliances.density * thermal_zone.total_floor_area From fe8c79d7c2330184e3dd51cb6a839199b627200f Mon Sep 17 00:00:00 2001 From: p_monsalvete Date: Thu, 3 Aug 2023 13:49:57 -0400 Subject: [PATCH 4/6] solving bugs from adding thermal_archetypes to the data model --- hub/city_model_structure/building.py | 11 ++- .../building_demand/internal_zone.py | 37 ++++++++- .../building_demand/thermal_archetype.py | 8 ++ .../building_demand/thermal_boundary.py | 75 ++++++++++--------- .../building_demand/thermal_zone.py | 73 ++++++------------ hub/helpers/thermal_zones_creation.py | 8 -- .../construction/eilat_physics_parameters.py | 1 + .../helpers/construction_helper.py | 2 - .../construction/nrcan_physics_parameters.py | 11 +++ .../construction/nrel_physics_parameters.py | 1 + tests/test_construction_factory.py | 3 +- 11 files changed, 124 insertions(+), 106 deletions(-) diff --git a/hub/city_model_structure/building.py b/hub/city_model_structure/building.py index 9b085a2a..ee5f86f3 100644 --- a/hub/city_model_structure/building.py +++ b/hub/city_model_structure/building.py @@ -114,7 +114,8 @@ class Building(CityObject): :return: [InternalZone] """ if self._internal_zones is None: - self._internal_zones = [InternalZone(self.surfaces, self.floor_area, self.volume)] + _number_of_storeys = self.eave_height * self.volume / self.floor_area + self._internal_zones = [InternalZone(self.surfaces, self.floor_area, self.volume, _number_of_storeys)] return self._internal_zones @property @@ -258,6 +259,12 @@ class Building(CityObject): Get building average storey height in meters :return: None or float """ + if len(self.internal_zones) > 1: + self._average_storey_height = 0 + for internal_zone in self.internal_zones: + self._average_storey_height += internal_zone.mean_height / len(self.internal_zones) + else: + self._average_storey_height = self.internal_zones[0].thermal_archetype.average_storey_height return self._average_storey_height @average_storey_height.setter @@ -393,6 +400,7 @@ class Building(CityObject): """ results = {} peak_lighting = 0 + peak = 0 for thermal_zone in self.thermal_zones_from_internal_zones: lighting = thermal_zone.lighting for schedule in lighting.schedules: @@ -411,6 +419,7 @@ class Building(CityObject): """ results = {} peak_appliances = 0 + peak = 0 for thermal_zone in self.thermal_zones_from_internal_zones: appliances = thermal_zone.appliances for schedule in appliances.schedules: diff --git a/hub/city_model_structure/building_demand/internal_zone.py b/hub/city_model_structure/building_demand/internal_zone.py index aa2cefa5..30c214ad 100644 --- a/hub/city_model_structure/building_demand/internal_zone.py +++ b/hub/city_model_structure/building_demand/internal_zone.py @@ -8,6 +8,7 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca import uuid from typing import Union, List from hub.city_model_structure.building_demand.usage import Usage +from hub.city_model_structure.building_demand.thermal_archetype import ThermalArchetype from hub.city_model_structure.building_demand.thermal_zone import ThermalZone from hub.city_model_structure.building_demand.thermal_boundary import ThermalBoundary from hub.city_model_structure.attributes.polyhedron import Polyhedron @@ -18,14 +19,16 @@ class InternalZone: """ InternalZone class """ - def __init__(self, surfaces, area, volume): + def __init__(self, surfaces, area, volume, number_of_storeys=None): self._surfaces = surfaces self._id = None self._geometry = None self._volume = volume self._area = area + self._number_of_storeys = number_of_storeys self._thermal_zones_from_internal_zones = None self._usages = None + self._thermal_archetype = None self._hvac_system = None @property @@ -75,10 +78,18 @@ class InternalZone: """ return self._area + @property + def mean_height(self): + """ + Get internal zone mean height in meters + :return: float + """ + return self.volume / self.area + @property def usages(self) -> [Usage]: """ - Get internal zone usage zones + Get usage archetypes :return: [Usage] """ return self._usages @@ -86,11 +97,27 @@ class InternalZone: @usages.setter def usages(self, value): """ - Set internal zone usage zones + Set usage archetypes :param value: [Usage] """ self._usages = value + @property + def thermal_archetype(self) -> ThermalArchetype: + """ + Get thermal archetype parameters + :return: ThermalArchetype + """ + return self._thermal_archetype + + @thermal_archetype.setter + def thermal_archetype(self, value): + """ + Set thermal archetype parameters + :param value: ThermalArchetype + """ + self._thermal_archetype = value + @property def hvac_system(self) -> Union[None, HvacSystem]: """ @@ -123,7 +150,9 @@ class InternalZone: windows_areas.append(hole.area) _thermal_boundary = ThermalBoundary(surface, surface.solid_polygon.area, windows_areas) _thermal_boundaries.append(_thermal_boundary) - _thermal_zone = ThermalZone(_thermal_boundaries, self, self.volume, self.area) + _thermal_zone = ThermalZone(_thermal_boundaries, self, self.volume, self.area, self._number_of_storeys) + for thermal_boundary in _thermal_zone.thermal_boundaries: + thermal_boundary.thermal_zones = [_thermal_zone] self._thermal_zones_from_internal_zones = [_thermal_zone] return self._thermal_zones_from_internal_zones diff --git a/hub/city_model_structure/building_demand/thermal_archetype.py b/hub/city_model_structure/building_demand/thermal_archetype.py index 47daf8a0..cd70912a 100644 --- a/hub/city_model_structure/building_demand/thermal_archetype.py +++ b/hub/city_model_structure/building_demand/thermal_archetype.py @@ -29,6 +29,14 @@ class ThermalArchetype: """ return self._constructions + @constructions.setter + def constructions(self, value): + """ + Set archetype constructions + :param value: [Construction] + """ + self._constructions = value + @property def average_storey_height(self): """ diff --git a/hub/city_model_structure/building_demand/thermal_boundary.py b/hub/city_model_structure/building_demand/thermal_boundary.py index d17a5b45..5858007c 100644 --- a/hub/city_model_structure/building_demand/thermal_boundary.py +++ b/hub/city_model_structure/building_demand/thermal_boundary.py @@ -7,7 +7,9 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord """ import uuid +import math from typing import List, Union, TypeVar +import logging from hub.helpers.configuration_helper import ConfigurationHelper as ch import hub.helpers.constants as cte from hub.city_model_structure.building_demand.layer import Layer @@ -92,7 +94,7 @@ class ThermalBoundary: self._thickness = 0.0 if self.layers is not None: for layer in self.layers: - if not layer.material.no_mass: + if not layer.no_mass: self._thickness += layer.thickness return self._thickness @@ -148,24 +150,21 @@ class ThermalBoundary: else: _area = self.opaque_area * self.window_ratio / (1-self.window_ratio) _thermal_opening.area = _area + self._thermal_openings = [_thermal_opening] + for thermal_opening in self._thermal_openings: + thermal_opening.g_value = self._construction_archetype.window_g_value + thermal_opening.overall_u_value = self._construction_archetype.window_overall_u_value + thermal_opening.frame_ratio = self._construction_archetype.window_frame_ratio + thermal_opening.construction_name = self._construction_archetype.window_type return self._thermal_openings @property - def construction_name(self) -> Union[None, str]: - """ - Get construction name - :return: None or str - """ - return self._construction_name - - @construction_name.setter - def construction_name(self, value): - """ - Set construction name - :param value: str - """ - if value is not None: - self._construction_name = str(value) + def _construction_archetype(self): + construction_archetypes = self.thermal_zones[0].parent_internal_zone.thermal_archetype.constructions + for construction_archetype in construction_archetypes: + if str(self.type) == str(construction_archetype.type): + return construction_archetype + return None @property def layers(self) -> List[Layer]: @@ -173,16 +172,13 @@ class ThermalBoundary: Get thermal boundary layers :return: [Layers] """ + if self._construction_archetype is not None: + self._layers = self._construction_archetype.layers + else: + logging.error('Layers not defined\n') + raise ValueError('Layers not defined') return self._layers - @layers.setter - def layers(self, value): - """ - Set thermal boundary layers - :param value: [Layer] - """ - self._layers = value - @property def type(self): """ @@ -209,18 +205,23 @@ class ThermalBoundary: for window_area in self.windows_areas: total_window_area += window_area self._window_ratio = total_window_area / (self.opaque_area + total_window_area) + else: + if self.type in (cte.WALL, cte.ROOF): + if -math.sqrt(2) / 2 < math.sin(self.parent_surface.azimuth) < math.sqrt(2) / 2: + if 0 < math.cos(self.parent_surface.azimuth): + self._window_ratio = \ + float(self._construction_archetype.window_ratio['north']) / 100 + else: + self._window_ratio = \ + float(self._construction_archetype.window_ratio['south']) / 100 + elif math.sqrt(2) / 2 <= math.sin(self._parent_surface.azimuth): + self._window_ratio = \ + float(self._construction_archetype.window_ratio['east']) / 100 + else: + self._window_ratio = \ + float(self._construction_archetype.window_ratio['west']) / 100 return self._window_ratio - @window_ratio.setter - def window_ratio(self, value): - """ - Set thermal boundary window ratio - :param value: str - """ - if self._window_ratio_to_be_calculated: - raise ValueError('Window ratio cannot be assigned when the windows are defined in the geometry.') - self._window_ratio = float(value) - @property def windows_areas(self) -> [float]: """ @@ -245,10 +246,10 @@ class ThermalBoundary: r_value = 1.0/h_i + 1.0/h_e try: for layer in self.layers: - if layer.material.no_mass: - r_value += float(layer.material.thermal_resistance) + if layer.no_mass: + r_value += float(layer.thermal_resistance) else: - r_value += float(layer.thickness) / float(layer.material.conductivity) + r_value += float(layer.thickness) / float(layer.conductivity) self._u_value = 1.0/r_value except TypeError: raise TypeError('Constructions layers are not initialized') from TypeError diff --git a/hub/city_model_structure/building_demand/thermal_zone.py b/hub/city_model_structure/building_demand/thermal_zone.py index 42cbfa78..50179cf4 100644 --- a/hub/city_model_structure/building_demand/thermal_zone.py +++ b/hub/city_model_structure/building_demand/thermal_zone.py @@ -29,7 +29,12 @@ class ThermalZone: ThermalZone class """ - def __init__(self, thermal_boundaries, parent_internal_zone, volume, footprint_area, usage_name=None): + def __init__(self, thermal_boundaries, + parent_internal_zone, + volume, + footprint_area, + number_of_storeys, + usage_name=None): self._id = None self._parent_internal_zone = parent_internal_zone self._footprint_area = footprint_area @@ -43,6 +48,7 @@ class ThermalZone: self._ordinate_number = None self._view_factors_matrix = None self._total_floor_area = None + self._number_of_storeys = number_of_storeys self._usage_name = usage_name self._usage_from_parent = False if usage_name is None: @@ -58,6 +64,14 @@ class ThermalZone: self._domestic_hot_water = None self._usages = None + @property + def parent_internal_zone(self) -> InternalZone: + """ + Get the internal zone to which this thermal zone belongs + :return: InternalZone + """ + return self._parent_internal_zone + @property def usages(self): """ @@ -113,83 +127,45 @@ class ThermalZone: Get thermal zone additional thermal bridge u value per footprint area W/m2K :return: None or float """ + self._additional_thermal_bridge_u_value = self.parent_internal_zone.thermal_archetype.extra_loses_due_to_thermal_bridges return self._additional_thermal_bridge_u_value - @additional_thermal_bridge_u_value.setter - def additional_thermal_bridge_u_value(self, value): - """ - Set thermal zone additional thermal bridge u value per footprint area W/m2K - :param value: float - """ - if value is not None: - self._additional_thermal_bridge_u_value = float(value) - @property def effective_thermal_capacity(self) -> Union[None, float]: """ Get thermal zone effective thermal capacity in J/m3K :return: None or float """ + self._effective_thermal_capacity = self._parent_internal_zone.thermal_archetype.thermal_capacity return self._effective_thermal_capacity - @effective_thermal_capacity.setter - def effective_thermal_capacity(self, value): - """ - Set thermal zone effective thermal capacity in J/m3K - :param value: float - """ - if value is not None: - self._effective_thermal_capacity = float(value) - @property def indirectly_heated_area_ratio(self) -> Union[None, float]: """ Get thermal zone indirectly heated area ratio :return: None or float """ + self._indirectly_heated_area_ratio = self._parent_internal_zone.thermal_archetype.indirect_heated_ratio return self._indirectly_heated_area_ratio - @indirectly_heated_area_ratio.setter - def indirectly_heated_area_ratio(self, value): - """ - Set thermal zone indirectly heated area ratio - :param value: float - """ - if value is not None: - self._indirectly_heated_area_ratio = float(value) - @property def infiltration_rate_system_on(self): """ Get thermal zone infiltration rate system on in air changes per hour (ACH) :return: None or float """ + self._infiltration_rate_system_on = self._parent_internal_zone.thermal_archetype.infiltration_rate_for_ventilation_system_on return self._infiltration_rate_system_on - @infiltration_rate_system_on.setter - def infiltration_rate_system_on(self, value): - """ - Set thermal zone infiltration rate system on in air changes per hour (ACH) - :param value: float - """ - self._infiltration_rate_system_on = value - @property def infiltration_rate_system_off(self): """ Get thermal zone infiltration rate system off in air changes per hour (ACH) :return: None or float """ + self._infiltration_rate_system_off = self._parent_internal_zone.thermal_archetype.infiltration_rate_for_ventilation_system_off return self._infiltration_rate_system_off - @infiltration_rate_system_off.setter - def infiltration_rate_system_off(self, value): - """ - Set thermal zone infiltration rate system on in air changes per hour (ACH) - :param value: float - """ - self._infiltration_rate_system_off = value - @property def volume(self): """ @@ -684,12 +660,5 @@ class ThermalZone: Get the total floor area of this thermal zone :return: float """ + self._total_floor_area = self.footprint_area * self._number_of_storeys return self._total_floor_area - - @total_floor_area.setter - def total_floor_area(self, value): - """ - Set the total floor area of this thermal zone - :param value: float - """ - self._total_floor_area = value diff --git a/hub/helpers/thermal_zones_creation.py b/hub/helpers/thermal_zones_creation.py index 31398ae9..f116a272 100644 --- a/hub/helpers/thermal_zones_creation.py +++ b/hub/helpers/thermal_zones_creation.py @@ -62,11 +62,3 @@ class ThermalZonesCreation: thermal_zones = StoreysGeneration(building, building.internal_zones[0], divide_in_storeys=divide_in_storeys).thermal_zones building.internal_zones[0].thermal_zones_from_internal_zones = thermal_zones - - @staticmethod - def _search_construction_in_archetype(archetype, construction_type): - construction_archetypes = archetype.constructions - for construction_archetype in construction_archetypes: - if str(construction_type) == str(construction_archetype.type): - return construction_archetype - return None diff --git a/hub/imports/construction/eilat_physics_parameters.py b/hub/imports/construction/eilat_physics_parameters.py index 664ea121..d048dbd1 100644 --- a/hub/imports/construction/eilat_physics_parameters.py +++ b/hub/imports/construction/eilat_physics_parameters.py @@ -59,6 +59,7 @@ class EilatPhysicsParameters: @staticmethod def _assign_values(thermal_archetype, catalog_archetype): + thermal_archetype.average_storey_height = catalog_archetype.average_storey_height thermal_archetype.extra_loses_due_to_thermal_bridges = catalog_archetype.extra_loses_due_to_thermal_bridges thermal_archetype.indirect_heated_ratio = 0 thermal_archetype.infiltration_rate_for_ventilation_system_on = catalog_archetype.infiltration_rate_for_ventilation_system_on diff --git a/hub/imports/construction/helpers/construction_helper.py b/hub/imports/construction/helpers/construction_helper.py index 9ed66da2..5d697354 100644 --- a/hub/imports/construction/helpers/construction_helper.py +++ b/hub/imports/construction/helpers/construction_helper.py @@ -5,8 +5,6 @@ Copyright © 2022 Concordia CERC group Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ -from hub.helpers import constants as cte - class ConstructionHelper: """ diff --git a/hub/imports/construction/nrcan_physics_parameters.py b/hub/imports/construction/nrcan_physics_parameters.py index 523243cc..b2c40fee 100644 --- a/hub/imports/construction/nrcan_physics_parameters.py +++ b/hub/imports/construction/nrcan_physics_parameters.py @@ -46,6 +46,8 @@ class NrcanPhysicsParameters: continue thermal_archetype = ThermalArchetype() self._assign_values(thermal_archetype, archetype) + for internal_zone in building.internal_zones: + internal_zone.thermal_archetype = thermal_archetype @staticmethod def _search_archetype(nrcan_catalog, function, year_of_construction, climate_zone): @@ -59,13 +61,20 @@ class NrcanPhysicsParameters: @staticmethod def _assign_values(thermal_archetype, catalog_archetype): + thermal_archetype.average_storey_height = catalog_archetype.average_storey_height thermal_archetype.extra_loses_due_to_thermal_bridges = catalog_archetype.extra_loses_due_to_thermal_bridges thermal_archetype.thermal_capacity = catalog_archetype.thermal_capacity thermal_archetype.indirect_heated_ratio = 0 thermal_archetype.infiltration_rate_for_ventilation_system_on = catalog_archetype.infiltration_rate_for_ventilation_system_on thermal_archetype.infiltration_rate_for_ventilation_system_off = catalog_archetype.infiltration_rate_for_ventilation_system_off + _constructions = [] for catalog_construction in catalog_archetype.constructions: construction = Construction() + construction.type = catalog_construction.type + if catalog_construction.window_ratio is not None: + for _orientation in catalog_construction.window_ratio: + if catalog_construction.window_ratio[_orientation] is None: + catalog_construction.window_ratio[_orientation] = 0 construction.window_ratio = catalog_construction.window_ratio _layers = [] for layer_archetype in catalog_construction.layers: @@ -91,3 +100,5 @@ class NrcanPhysicsParameters: construction.window_frame_ratio = window_archetype.frame_ratio construction.window_g_value = window_archetype.g_value construction.window_overall_u_value = window_archetype.overall_u_value + _constructions.append(construction) + thermal_archetype.constructions = _constructions diff --git a/hub/imports/construction/nrel_physics_parameters.py b/hub/imports/construction/nrel_physics_parameters.py index 3e1139fa..9a5503cf 100644 --- a/hub/imports/construction/nrel_physics_parameters.py +++ b/hub/imports/construction/nrel_physics_parameters.py @@ -61,6 +61,7 @@ class NrelPhysicsParameters: @staticmethod def _assign_values(thermal_archetype, catalog_archetype): + thermal_archetype.average_storey_height = catalog_archetype.average_storey_height thermal_archetype.extra_loses_due_to_thermal_bridges = catalog_archetype.extra_loses_due_to_thermal_bridges thermal_archetype.thermal_capacity = catalog_archetype.thermal_capacity thermal_archetype.indirect_heated_ratio = catalog_archetype.indirect_heated_ratio diff --git a/tests/test_construction_factory.py b/tests/test_construction_factory.py index d56441c2..02d6a0c2 100644 --- a/tests/test_construction_factory.py +++ b/tests/test_construction_factory.py @@ -133,12 +133,11 @@ 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_from_internal_zones, 'thermal_boundary delimits no thermal zone') + 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.thickness, 'thermal_boundary thickness is none') self.assertIsNotNone(thermal_boundary.type, 'thermal_boundary type is none') self.assertIsNotNone(thermal_boundary.thermal_openings, 'thermal_openings is none') - self.assertIsNotNone(thermal_boundary.construction_name, 'construction_name is none') self.assertIsNotNone(thermal_boundary.window_ratio, 'window_ratio is none') self.assertIsNone(thermal_boundary.windows_areas, 'windows_areas is not none') self.assertIsNotNone(thermal_boundary.u_value, 'u_value is none') From 212b4b0621fbd1fc55e91e835744fa980ec28635 Mon Sep 17 00:00:00 2001 From: p_monsalvete Date: Thu, 3 Aug 2023 14:51:54 -0400 Subject: [PATCH 5/6] solving bugs from adding thermal_archetypes to the data model --- .../building_demand/thermal_boundary.py | 17 ++++++++++- hub/helpers/thermal_zones_creation.py | 23 --------------- .../construction/eilat_physics_parameters.py | 11 +++++++- .../construction/nrel_physics_parameters.py | 13 ++++++++- tests/test_construction_factory.py | 28 ++++--------------- 5 files changed, 44 insertions(+), 48 deletions(-) diff --git a/hub/city_model_structure/building_demand/thermal_boundary.py b/hub/city_model_structure/building_demand/thermal_boundary.py index 5858007c..c3ee0128 100644 --- a/hub/city_model_structure/building_demand/thermal_boundary.py +++ b/hub/city_model_structure/building_demand/thermal_boundary.py @@ -37,6 +37,7 @@ class ThermalBoundary: self._construction_name = None self._thickness = None self._internal_surface = None + self._external_surface = None self._window_ratio = None self._window_ratio_to_be_calculated = False if self._windows_areas is not None: @@ -55,7 +56,7 @@ class ThermalBoundary: @property def parent_surface(self) -> Surface: """ - Get the surface that belongs to the thermal boundary + Get the surface that belongs to the thermal boundary, considered the external surface of that boundary :return: Surface """ return self._parent_surface @@ -306,4 +307,18 @@ class ThermalBoundary: """ if self._internal_surface is None: self._internal_surface = self.parent_surface.inverse + # The agreement is that the layers are defined from outside to inside + internal_layer = self.layers[len(self.layers) - 1] + self._internal_surface.short_wave_reflectance = 1 - internal_layer.solar_absorptance + self._internal_surface.long_wave_emittance = 1 - internal_layer.solar_absorptance + return self._internal_surface + + @property + def external_surface(self) -> Surface: + if self._external_surface is None: + # The agreement is that the layers are defined from outside to inside + self._external_surface = self.parent_surface + self._external_surface.short_wave_reflectance = 1 - self.layers[0].solar_absorptance + self._external_surface.long_wave_emittance = 1 - self.layers[0].solar_absorptance + return self._external_surface diff --git a/hub/helpers/thermal_zones_creation.py b/hub/helpers/thermal_zones_creation.py index f116a272..636eab06 100644 --- a/hub/helpers/thermal_zones_creation.py +++ b/hub/helpers/thermal_zones_creation.py @@ -15,13 +15,6 @@ class ThermalZonesCreation: def __init__(self, building=None): self._building = building - # todo: ATTENTION!! -# try: -# thermal_boundary.window_ratio = catalog_construction.window_ratio -# except ValueError: -# # This is the normal operation way when the windows are defined in the geometry -# continue - # # The agreement is that the layers are defined from outside to inside # external_layer = catalog_construction.layers[0] # external_surface = thermal_boundary.parent_surface @@ -32,22 +25,6 @@ class ThermalZonesCreation: # internal_surface.short_wave_reflectance = 1 - internal_layer.material.solar_absorptance # internal_surface.long_wave_emittance = 1 - internal_layer.material.solar_absorptance -# if thermal_boundary.type in (cte.WALL, cte.ROOF): -# if catalog_construction.window is not None: -# if -math.sqrt(2) / 2 < math.sin(thermal_boundary.parent_surface.azimuth) < math.sqrt(2) / 2: -# if 0 < math.cos(thermal_boundary.parent_surface.azimuth): -# thermal_boundary.window_ratio = \ -# float(catalog_construction.window_ratio['north']) / 100 -# else: -# thermal_boundary.window_ratio = \ -# float(catalog_construction.window_ratio['south']) / 100 -# elif math.sqrt(2) / 2 <= math.sin(thermal_boundary.parent_surface.azimuth): -# thermal_boundary.window_ratio = \ -# float(catalog_construction.window_ratio['east']) / 100 -# else: -# thermal_boundary.window_ratio = \ -# float(catalog_construction.window_ratio['west']) / 100 - @property def thermal_zones_from_storeys(self): """ diff --git a/hub/imports/construction/eilat_physics_parameters.py b/hub/imports/construction/eilat_physics_parameters.py index d048dbd1..51760e03 100644 --- a/hub/imports/construction/eilat_physics_parameters.py +++ b/hub/imports/construction/eilat_physics_parameters.py @@ -46,6 +46,8 @@ class EilatPhysicsParameters: continue thermal_archetype = ThermalArchetype() self._assign_values(thermal_archetype, archetype) + for internal_zone in building.internal_zones: + internal_zone.thermal_archetype = thermal_archetype @staticmethod def _search_archetype(nrcan_catalog, function, year_of_construction, climate_zone): @@ -65,8 +67,14 @@ class EilatPhysicsParameters: thermal_archetype.infiltration_rate_for_ventilation_system_on = catalog_archetype.infiltration_rate_for_ventilation_system_on thermal_archetype.infiltration_rate_for_ventilation_system_off = catalog_archetype.infiltration_rate_for_ventilation_system_off effective_thermal_capacity = 0 + _constructions = [] for catalog_construction in catalog_archetype.constructions: construction = Construction() + construction.type = catalog_construction.type + if catalog_construction.window_ratio is not None: + for _orientation in catalog_construction.window_ratio: + if catalog_construction.window_ratio[_orientation] is None: + catalog_construction.window_ratio[_orientation] = 0 construction.window_ratio = catalog_construction.window_ratio _layers = [] total_thickness = 0 @@ -97,5 +105,6 @@ class EilatPhysicsParameters: construction.window_frame_ratio = window_archetype.frame_ratio construction.window_g_value = window_archetype.g_value construction.window_overall_u_value = window_archetype.overall_u_value - + _constructions.append(construction) + thermal_archetype.constructions = _constructions thermal_archetype.thermal_capacity = effective_thermal_capacity diff --git a/hub/imports/construction/nrel_physics_parameters.py b/hub/imports/construction/nrel_physics_parameters.py index 9a5503cf..8fd2e46f 100644 --- a/hub/imports/construction/nrel_physics_parameters.py +++ b/hub/imports/construction/nrel_physics_parameters.py @@ -46,6 +46,8 @@ class NrelPhysicsParameters: continue thermal_archetype = ThermalArchetype() self._assign_values(thermal_archetype, archetype) + for internal_zone in building.internal_zones: + internal_zone.thermal_archetype = thermal_archetype @staticmethod def _search_archetype(nrel_catalog, function, year_of_construction, climate_zone): @@ -67,9 +69,16 @@ class NrelPhysicsParameters: thermal_archetype.indirect_heated_ratio = catalog_archetype.indirect_heated_ratio thermal_archetype.infiltration_rate_for_ventilation_system_on = catalog_archetype.infiltration_rate_for_ventilation_system_on thermal_archetype.infiltration_rate_for_ventilation_system_off = catalog_archetype.infiltration_rate_for_ventilation_system_off + _constructions = [] for catalog_construction in catalog_archetype.constructions: construction = Construction() - construction.window_ratio = catalog_construction.window_ratio + construction.type = catalog_construction.type + if catalog_construction.window_ratio is not None: + construction.window_ratio = {'north': catalog_construction.window_ratio, + 'east': catalog_construction.window_ratio, + 'south': catalog_construction.window_ratio, + 'west': catalog_construction.window_ratio + } _layers = [] for layer_archetype in catalog_construction.layers: layer = Layer() @@ -94,3 +103,5 @@ class NrelPhysicsParameters: construction.window_frame_ratio = window_archetype.frame_ratio construction.window_g_value = window_archetype.g_value construction.window_overall_u_value = window_archetype.overall_u_value + _constructions.append(construction) + thermal_archetype.constructions = _constructions \ No newline at end of file diff --git a/tests/test_construction_factory.py b/tests/test_construction_factory.py index 02d6a0c2..49d93d7e 100644 --- a/tests/test_construction_factory.py +++ b/tests/test_construction_factory.py @@ -10,6 +10,7 @@ from unittest import TestCase from hub.imports.geometry_factory import GeometryFactory from hub.imports.construction_factory import ConstructionFactory from hub.helpers.dictionaries import Dictionaries +import hub.helpers.constants as cte class TestConstructionFactory(TestCase): @@ -138,7 +139,10 @@ class TestConstructionFactory(TestCase): self.assertIsNotNone(thermal_boundary.thickness, 'thermal_boundary thickness is none') self.assertIsNotNone(thermal_boundary.type, 'thermal_boundary type is none') self.assertIsNotNone(thermal_boundary.thermal_openings, 'thermal_openings is none') - self.assertIsNotNone(thermal_boundary.window_ratio, 'window_ratio is none') + if thermal_boundary.type in (cte.WALL, cte.ROOF): + self.assertIsNotNone(thermal_boundary.window_ratio, 'window_ratio is none') + else: + self.assertIsNone(thermal_boundary.window_ratio, 'window_ratio is not none') self.assertIsNone(thermal_boundary.windows_areas, 'windows_areas is not none') self.assertIsNotNone(thermal_boundary.u_value, 'u_value is none') self.assertIsNotNone(thermal_boundary.hi, 'hi is none') @@ -149,17 +153,15 @@ class TestConstructionFactory(TestCase): def _check_thermal_openings(self, thermal_boundary): for thermal_opening in thermal_boundary.thermal_openings: self.assertIsNotNone(thermal_opening.id, 'thermal opening id is not none') - self.assertIsNotNone(thermal_opening.construction_name, 'thermal opening construction is none') self.assertIsNotNone(thermal_opening.area, 'thermal opening area is not none') self.assertIsNotNone(thermal_opening.frame_ratio, 'thermal opening frame_ratio is none') self.assertIsNotNone(thermal_opening.g_value, 'thermal opening g_value is none') self.assertIsNotNone(thermal_opening.overall_u_value, 'thermal opening overall_u_value is none') self.assertIsNotNone(thermal_opening.hi, 'thermal opening hi is none') self.assertIsNotNone(thermal_opening.he, 'thermal opening he is none') - self.assertIsNotNone(thermal_opening.construction_name, 'thermal opening construction_name is none') def _check_surfaces(self, thermal_boundary): - external_surface = thermal_boundary.parent_surface + external_surface = thermal_boundary.external_surface internal_surface = thermal_boundary.internal_surface self.assertIsNotNone(external_surface.short_wave_reflectance, 'external surface short_wave_reflectance id is not none') @@ -197,24 +199,6 @@ class TestConstructionFactory(TestCase): building.function = self._internal_function('pluto', building.function) ConstructionFactory('nrcan', city).enrich() - self._check_buildings(city) - for building in city.buildings: - for internal_zone in building.internal_zones: - self._check_thermal_zones(internal_zone) - for thermal_zone in internal_zone.thermal_zones_from_internal_zones: - self._check_thermal_boundaries(thermal_zone) - for thermal_boundary in thermal_zone.thermal_boundaries: - self.assertIsNotNone(thermal_boundary.layers, 'layers is none') - self._check_thermal_openings(thermal_boundary) - self._check_surfaces(thermal_boundary) - - file = 'one_building_in_kelowna.gml' - city = self._get_citygml(file) - for building in city.buildings: - building.year_of_construction = 2006 - building.function = self._internal_function('hft', building.function) - ConstructionFactory('nrel', city).enrich() - self._check_buildings(city) for building in city.buildings: for internal_zone in building.internal_zones: From 1e9afa28a80edcaa23abb1c27e97863efca19b88 Mon Sep 17 00:00:00 2001 From: p_monsalvete Date: Thu, 3 Aug 2023 17:00:59 -0400 Subject: [PATCH 6/6] all tests passed --- hub/city_model_structure/building.py | 5 ++++- .../building_demand/thermal_boundary.py | 2 +- tests/test_construction_factory.py | 6 +----- tests/test_custom_insel_block.py | 5 +++-- tests/test_geometry_factory.py | 2 +- tests/test_insel_exports.py | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hub/city_model_structure/building.py b/hub/city_model_structure/building.py index ee5f86f3..2e0f385f 100644 --- a/hub/city_model_structure/building.py +++ b/hub/city_model_structure/building.py @@ -264,7 +264,10 @@ class Building(CityObject): for internal_zone in self.internal_zones: self._average_storey_height += internal_zone.mean_height / len(self.internal_zones) else: - self._average_storey_height = self.internal_zones[0].thermal_archetype.average_storey_height + if self.internal_zones[0].thermal_archetype is None: + self._average_storey_height = None + else: + self._average_storey_height = self.internal_zones[0].thermal_archetype.average_storey_height return self._average_storey_height @average_storey_height.setter diff --git a/hub/city_model_structure/building_demand/thermal_boundary.py b/hub/city_model_structure/building_demand/thermal_boundary.py index c3ee0128..66746037 100644 --- a/hub/city_model_structure/building_demand/thermal_boundary.py +++ b/hub/city_model_structure/building_demand/thermal_boundary.py @@ -38,7 +38,7 @@ class ThermalBoundary: self._thickness = None self._internal_surface = None self._external_surface = None - self._window_ratio = None + self._window_ratio = 0 self._window_ratio_to_be_calculated = False if self._windows_areas is not None: self._window_ratio_to_be_calculated = True diff --git a/tests/test_construction_factory.py b/tests/test_construction_factory.py index 49d93d7e..710894bd 100644 --- a/tests/test_construction_factory.py +++ b/tests/test_construction_factory.py @@ -10,7 +10,6 @@ from unittest import TestCase from hub.imports.geometry_factory import GeometryFactory from hub.imports.construction_factory import ConstructionFactory from hub.helpers.dictionaries import Dictionaries -import hub.helpers.constants as cte class TestConstructionFactory(TestCase): @@ -139,10 +138,7 @@ class TestConstructionFactory(TestCase): self.assertIsNotNone(thermal_boundary.thickness, 'thermal_boundary thickness is none') self.assertIsNotNone(thermal_boundary.type, 'thermal_boundary type is none') self.assertIsNotNone(thermal_boundary.thermal_openings, 'thermal_openings is none') - if thermal_boundary.type in (cte.WALL, cte.ROOF): - self.assertIsNotNone(thermal_boundary.window_ratio, 'window_ratio is none') - else: - self.assertIsNone(thermal_boundary.window_ratio, 'window_ratio is not none') + self.assertIsNotNone(thermal_boundary.window_ratio, 'window_ratio is none') self.assertIsNone(thermal_boundary.windows_areas, 'windows_areas is not none') self.assertIsNotNone(thermal_boundary.u_value, 'u_value is none') self.assertIsNotNone(thermal_boundary.hi, 'hi is none') diff --git a/tests/test_custom_insel_block.py b/tests/test_custom_insel_block.py index 1b46995d..25a8f942 100644 --- a/tests/test_custom_insel_block.py +++ b/tests/test_custom_insel_block.py @@ -128,11 +128,12 @@ class TestExports(TestCase): for thermal_boundary in thermal_zone.thermal_boundaries: self.assertIsNotNone(thermal_boundary.type) self.assertIsNotNone(thermal_boundary.opaque_area) - self.assertIsNotNone(thermal_boundary.window_ratio) + if thermal_boundary.type in (cte.WALL, cte.ROOF): + self.assertIsNotNone(thermal_boundary.window_ratio) self.assertIsNotNone(thermal_boundary.u_value) self.assertIsNotNone(thermal_boundary.thermal_openings) if thermal_boundary.type is not cte.GROUND: - self.assertIsNotNone(thermal_boundary.parent_surface.short_wave_reflectance) + self.assertIsNotNone(thermal_boundary.external_surface.short_wave_reflectance) for usage in internal_zone.usages: self.assertIsNotNone(usage.percentage, f'usage zone {usage.name} percentage is none') diff --git a/tests/test_geometry_factory.py b/tests/test_geometry_factory.py index 73d815ff..3b5bd8f8 100644 --- a/tests/test_geometry_factory.py +++ b/tests/test_geometry_factory.py @@ -62,7 +62,7 @@ class TestGeometryFactory(TestCase): self.assertIsNotNone(building.internal_zones, 'building internal zones is none') for internal_zone in building.internal_zones: self.assertIsNone(internal_zone.usages, 'usage zones are defined') - self.assertIsNone(internal_zone.thermal_zones_from_internal_zones, 'thermal zones are defined') + self.assertIsNone(internal_zone.thermal_archetype, 'thermal archetype is 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') diff --git a/tests/test_insel_exports.py b/tests/test_insel_exports.py index 57604b07..f4422363 100644 --- a/tests/test_insel_exports.py +++ b/tests/test_insel_exports.py @@ -132,7 +132,7 @@ class TestExports(TestCase): self.assertIsNotNone(thermal_boundary.u_value) self.assertIsNotNone(thermal_boundary.thermal_openings) if thermal_boundary.type is not cte.GROUND: - self.assertIsNotNone(thermal_boundary.parent_surface.short_wave_reflectance) + self.assertIsNotNone(thermal_boundary.external_surface.short_wave_reflectance) for usage in internal_zone.usages: self.assertIsNotNone(usage.percentage, f'usage zone {usage.name} percentage is none')