diff --git a/hub/city_model_structure/building.py b/hub/city_model_structure/building.py index 14f62e70..1a2775c0 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' @@ -114,26 +114,24 @@ class Building(CityObject): :return: [InternalZone] """ if self._internal_zones is None: - self._internal_zones = [InternalZone(self.surfaces, self.floor_area)] + _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 - 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]: @@ -261,6 +259,15 @@ 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: + 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 @@ -396,7 +403,8 @@ class Building(CityObject): """ results = {} peak_lighting = 0 - for thermal_zone in self.thermal_zones: + peak = 0 + 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 @@ -414,7 +422,8 @@ class Building(CityObject): """ results = {} peak_appliances = 0 - for thermal_zone in self.thermal_zones: + peak = 0 + 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 @@ -657,7 +666,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/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..30c214ad 100644 --- a/hub/city_model_structure/building_demand/internal_zone.py +++ b/hub/city_model_structure/building_demand/internal_zone.py @@ -8,7 +8,9 @@ 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 from hub.city_model_structure.energy_systems.hvac_system import HvacSystem @@ -17,14 +19,16 @@ class InternalZone: """ InternalZone class """ - def __init__(self, surfaces, area): + def __init__(self, surfaces, area, volume, number_of_storeys=None): self._surfaces = surfaces self._id = None self._geometry = None - self._volume = None + self._volume = volume self._area = area - self._thermal_zones = None + 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 @@ -64,7 +68,7 @@ class InternalZone: Get internal zone volume in cubic meters :return: float """ - return self.geometry.volume + return self._volume @property def area(self): @@ -74,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 @@ -85,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]: """ @@ -107,17 +135,31 @@ 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._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 - @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/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..cd70912a --- /dev/null +++ b/hub/city_model_structure/building_demand/thermal_archetype.py @@ -0,0 +1,134 @@ +""" +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 + + @constructions.setter + def constructions(self, value): + """ + Set archetype constructions + :param value: [Construction] + """ + self._constructions = value + + @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/city_model_structure/building_demand/thermal_boundary.py b/hub/city_model_structure/building_demand/thermal_boundary.py index d17a5b45..66746037 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 @@ -35,7 +37,8 @@ class ThermalBoundary: self._construction_name = None self._thickness = None self._internal_surface = None - self._window_ratio = None + self._external_surface = 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 @@ -53,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 @@ -92,7 +95,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 +151,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 +173,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 +206,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 +247,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 @@ -305,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/city_model_structure/building_demand/thermal_zone.py b/hub/city_model_structure/building_demand/thermal_zone.py index 8d4886d4..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): """ @@ -221,15 +197,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]: @@ -656,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/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 df55aa4e..37ac861d 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 d47dc4bb..ce4fb3e6 100644 --- a/hub/exports/building_energy/insel/insel_monthly_energy_balance.py +++ b/hub/exports/building_energy/insel/insel_monthly_energy_balance.py @@ -42,7 +42,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( @@ -124,7 +124,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)') @@ -137,7 +137,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: @@ -167,11 +167,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 75baaf31..7538d1eb 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][hour] diff --git a/hub/helpers/thermal_zones_creation.py b/hub/helpers/thermal_zones_creation.py new file mode 100644 index 00000000..636eab06 --- /dev/null +++ b/hub/helpers/thermal_zones_creation.py @@ -0,0 +1,41 @@ +""" +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 + + +class ThermalZonesCreation: + """ + PeakLoads class + """ + def __init__(self, building=None): + self._building = building + +# # 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 + + @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_from_internal_zones = thermal_zones diff --git a/hub/imports/construction/eilat_physics_parameters.py b/hub/imports/construction/eilat_physics_parameters.py index 181ee97d..51760e03 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,10 @@ 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 + thermal_archetype = ThermalArchetype() + self._assign_values(thermal_archetype, archetype) 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) + internal_zone.thermal_archetype = thermal_archetype @staticmethod def _search_archetype(nrcan_catalog, function, year_of_construction, climate_zone): @@ -81,133 +60,51 @@ 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.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 + 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 + 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 = f'{thermal_boundary.type}_{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) - - 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 + 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 + _constructions.append(construction) + thermal_archetype.constructions = _constructions + thermal_archetype.thermal_capacity = effective_thermal_capacity 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 fd221ba0..b2c40fee 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,10 @@ 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 + thermal_archetype = ThermalArchetype() + self._assign_values(thermal_archetype, archetype) 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) + internal_zone.thermal_archetype = thermal_archetype @staticmethod def _search_archetype(nrcan_catalog, function, year_of_construction, climate_zone): @@ -81,126 +60,45 @@ 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.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: + 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 = f'{thermal_boundary.type}_{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 + _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 e01e3cbf..8fd2e46f 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,10 @@ 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 - + thermal_archetype = ThermalArchetype() + self._assign_values(thermal_archetype, archetype) 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) + internal_zone.thermal_archetype = thermal_archetype @staticmethod def _search_archetype(nrel_catalog, function, year_of_construction, climate_zone): @@ -80,111 +62,46 @@ 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.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 + 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: + 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() + 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 + _constructions.append(construction) + thermal_archetype.constructions = _constructions \ No newline at end of file diff --git a/hub/imports/results/insel_monthly_energry_balance.py b/hub/imports/results/insel_monthly_energry_balance.py index 6dd647ab..31dde60d 100644 --- a/hub/imports/results/insel_monthly_energry_balance.py +++ b/hub/imports/results/insel_monthly_energry_balance.py @@ -43,12 +43,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] 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 1b0b609a..5face637 100644 --- a/hub/persistence/models/city_object.py +++ b/hub/persistence/models/city_object.py @@ -60,7 +60,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..710894bd 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') @@ -138,7 +138,6 @@ 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.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') @@ -150,17 +149,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') @@ -184,7 +181,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,25 +199,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: - 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: - 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 +217,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 +235,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 +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') @@ -299,7 +278,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 +300,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 aeb715ed..15c13140 100644 --- a/tests/test_custom_insel_block.py +++ b/tests/test_custom_insel_block.py @@ -100,7 +100,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} ' @@ -113,11 +113,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_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_exports.py b/tests/test_exports.py index 18aeb1cd..3bb0f91f 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: diff --git a/tests/test_geometry_factory.py b/tests/test_geometry_factory.py index a118bd58..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, '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 bca43173..252ed263 100644 --- a/tests/test_insel_exports.py +++ b/tests/test_insel_exports.py @@ -100,7 +100,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} ' @@ -117,7 +117,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')