""" ThermalBoundary module SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Contributors Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ from typing import List, TypeVar, Union from city_model_structure.building_demand.layer import Layer from city_model_structure.building_demand.thermal_opening import ThermalOpening ThermalZone = TypeVar('ThermalZone') Polygon = TypeVar('Polygon') Surface = TypeVar('Surface') class ThermalBoundary: """ ThermalBoundary class """ def __init__(self, surface, delimits): self._surface = surface self._delimits = delimits # ToDo: up to at least LOD2 will be just one thermal opening per Thermal boundary only if window_ratio > 0, # review for LOD3 and LOD4 self._thermal_openings = None self._layers = None self._outside_solar_absorptance = None self._outside_thermal_absorptance = None self._outside_visible_absorptance = None self._u_value = None self._shortwave_reflectance = None self._construction_name = None self._hi = None self._he = None self._window_ratio = None self._refurbishment_measure = None self._surface_geometry = None self._thickness = None self._virtual_internal_surface = None self._inside_emissivity = None self._alpha_coefficient = None self._radiative_coefficient = None @property def surface(self) -> Surface: """ Get the surface that belongs to the thermal boundary :return: Surface """ # todo: in LoD4 this property will be a list of surfaces, not only one return self._surface @property def delimits(self) -> List[ThermalZone]: """ Get the thermal zones delimited by the thermal boundary :return: [ThermalZone] """ return self._delimits @property def azimuth(self): """ Thermal boundary azimuth in radians :return: float """ return self._surface.azimuth @property def inclination(self): """ Thermal boundary inclination in radians :return: float """ return self._surface.inclination @property def area(self): """ Thermal boundary area in square meters :return: float """ # to check the lod without depending on that parameter if float(self.surface.solid_polygon.area) - float(self.surface.perimeter_polygon.area) < 1e-3: area = float(self.surface.perimeter_polygon.area) * (1 - float(self.window_ratio)) else: area = self.surface.solid_polygon.area return area @property def _total_area_including_windows(self): """ Thermal boundary plus windows area in square meters :return: float """ return self.surface.perimeter_polygon.area @property def thickness(self): """ Thermal boundary thickness in meters :return: float """ if self._thickness is None: self._thickness = 0.0 if self.layers is not None: for layer in self.layers: self._thickness += layer.thickness return self._thickness @property def outside_solar_absorptance(self): """ Get thermal boundary outside solar absorptance :return: float """ return self._outside_solar_absorptance @outside_solar_absorptance.setter def outside_solar_absorptance(self, value): """ Set thermal boundary outside solar absorptance :param value: float """ self._outside_solar_absorptance = value self._shortwave_reflectance = 1.0 - float(value) @property def outside_thermal_absorptance(self): """ Get thermal boundary outside thermal absorptance :return: float """ return self._outside_thermal_absorptance @outside_thermal_absorptance.setter def outside_thermal_absorptance(self, value): """ Set thermal boundary outside thermal absorptance :param value: float """ self._outside_thermal_absorptance = value @property def outside_visible_absorptance(self): """ Get thermal boundary outside visible absorptance :return: float """ return self._outside_visible_absorptance @outside_visible_absorptance.setter def outside_visible_absorptance(self, value): """ Set thermal boundary outside visible absorptance :param value: float """ self._outside_visible_absorptance = value @property def thermal_openings(self) -> List[ThermalOpening]: """ Get thermal boundary thermal openings :return: [ThermalOpening] """ if self._thermal_openings is None: if float(self.window_ratio) > 0: thermal_opening = ThermalOpening() thermal_opening.area = float(self._total_area_including_windows) * float(self.window_ratio) thermal_opening.hi = self.hi thermal_opening.he = self.he self._thermal_openings = [thermal_opening] else: self._thermal_openings = [] return self._thermal_openings @thermal_openings.setter def thermal_openings(self, value): """ Set thermal boundary thermal openings :param value: [ThermalOpening] """ self._thermal_openings = value @property def construction_name(self): """ Get construction name :return: str """ return self._construction_name @construction_name.setter def construction_name(self, value): """ Set construction name :param value: str """ self._construction_name = value @property def layers(self) -> List[Layer]: """ Get thermal boundary layers :return: [Layers] """ return self._layers @layers.setter def layers(self, value): """ Set thermal boundary layers :param value: [Layer] """ self._layers = value @property def type(self): """ Thermal boundary surface type :return: str """ return self._surface.type @property def window_ratio(self): """ Get thermal boundary window ratio :return: float """ return self._window_ratio @window_ratio.setter def window_ratio(self, value): """ Set thermal boundary window ratio :param value: float """ self._window_ratio = value # todo: what if I just want to assign a number?? @Guille @property def u_value(self): """ Get thermal boundary U-value in W/m2K internal and external convective coefficient in W/m2K values, can be configured at configuration.ini :return: float """ if self._u_value is None: h_i = self.hi h_e = self.he 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) else: r_value = r_value + float(layer.material.conductivity) / float(layer.thickness) self._u_value = 1.0/r_value except TypeError: raise Exception('Constructions layers are not initialized') from TypeError return self._u_value @u_value.setter def u_value(self, value): """ Set thermal boundary U-value in W/m2K :param value: float """ self._u_value = value @property def shortwave_reflectance(self): """ Get thermal boundary shortwave reflectance :return: float """ return self._shortwave_reflectance @shortwave_reflectance.setter def shortwave_reflectance(self, value): """ Set thermal boundary shortwave reflectance :param value: float """ self._shortwave_reflectance = value self._outside_solar_absorptance = 1.0 - float(value) @property def hi(self): """ Get internal convective heat transfer coefficient (W/m2K) :return: float """ return self._hi @hi.setter def hi(self, value): """ Set internal convective heat transfer coefficient (W/m2K) :param value: internal convective heat transfer coefficient (W/m2K) """ self._hi = value @property def he(self): """ Get external convective heat transfer coefficient (W/m2K) :return: float """ return self._he @he.setter def he(self, value): """ Set external convective heat transfer coefficient (W/m2K) :param value: external convective heat transfer coefficient (W/m2K) """ self._he = value @property def surface_geometry(self) -> Union[NotImplementedError, Polygon]: """ Get the polygon that defines the thermal boundary :return: Polygon """ raise NotImplementedError @property def virtual_internal_surface(self) -> Surface: """ Get the internal surface of the thermal boundary :return: Surface """ if self._virtual_internal_surface is None: self._virtual_internal_surface = self.surface.inverse return self._virtual_internal_surface # todo: need extract information from construction library or assume them at the beginning of workflows @property def inside_emissivity(self): """ Get the short wave emissivity factor of the thermal boundary's internal surface (-) :return: float """ return self._inside_emissivity @inside_emissivity.setter def inside_emissivity(self, value): """ Set short wave emissivity factor of the thermal boundary's internal surface (-) :param value: float """ self._inside_emissivity = value @property def alpha_coefficient(self): """ Get the long wave emissivity factor of the thermal boundary's internal surface (-) :return: float """ return self._alpha_coefficient @alpha_coefficient.setter def alpha_coefficient(self, value): """ Set long wave emissivity factor of the thermal boundary's internal surface (-) :param value: float """ self._alpha_coefficient = value @property def radiative_coefficient(self): """ Get the radiative coefficient of the thermal boundary's external surface (-) :return: float """ return self._radiative_coefficient @radiative_coefficient.setter def radiative_coefficient(self, value): """ Set radiative coefficient of the thermal boundary's external surface (-) :param value: float """ self._radiative_coefficient = value