hub/city_model_structure/building_demand/thermal_boundary.py

423 lines
12 KiB
Python
Raw Normal View History

2020-10-28 13:42:58 -04:00
"""
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
2020-10-28 13:42:58 -04:00
"""
import uuid
2022-03-08 19:19:52 -05:00
from typing import List, Union
from helpers.configuration_helper import ConfigurationHelper as ch
from city_model_structure.building_demand.layer import Layer
from city_model_structure.building_demand.thermal_opening import ThermalOpening
from city_model_structure.building_demand.thermal_zone import ThermalZone
from city_model_structure.building_demand.surface import Surface
2020-10-28 13:42:58 -04:00
class ThermalBoundary:
"""
ThermalBoundary class
"""
2022-03-08 19:19:52 -05:00
def __init__(self, parent_surface, opaque_area, windows_areas):
self._parent_surface = parent_surface
self._opaque_area = opaque_area
self._windows_areas = windows_areas
self._id = None
self._thermal_zones = None
self._thermal_openings = None
2020-10-28 13:42:58 -04:00
self._layers = None
self._outside_solar_absorptance = None
2020-10-28 13:42:58 -04:00
self._outside_thermal_absorptance = None
self._outside_visible_absorptance = None
self._u_value = None
self._shortwave_reflectance = None
self._construction_name = None
2022-03-08 19:19:52 -05:00
self._hi = ch().convective_heat_transfer_coefficient_interior
self._he = ch().convective_heat_transfer_coefficient_exterior
self._refurbishment_measure = None
self._thickness = None
self._virtual_internal_surface = None
self._inside_emissivity = None
self._alpha_coefficient = None
self._radiative_coefficient = None
2022-03-08 19:19:52 -05:00
self._window_ratio = None
self._calculated = False
2021-03-16 12:19:35 -04:00
@property
def id(self):
"""
Get thermal zone id, an universally unique identifier randomly generated
:return: str
"""
if self._id is None:
self._id = uuid.uuid4()
return self._id
@property
2022-03-08 19:19:52 -05:00
def parent_surface(self) -> Surface:
"""
Get the surface that belongs to the thermal boundary
:return: Surface
"""
2022-03-08 19:19:52 -05:00
return self._parent_surface
2020-10-28 13:42:58 -04:00
@property
2021-08-27 12:51:30 -04:00
def thermal_zones(self) -> List[ThermalZone]:
2020-10-28 13:42:58 -04:00
"""
Get the thermal zones delimited by the thermal boundary
:return: [ThermalZone]
"""
2021-08-27 12:51:30 -04:00
return self._thermal_zones
2020-10-28 13:42:58 -04:00
@thermal_zones.setter
def thermal_zones(self, value):
"""
2022-03-08 19:19:52 -05:00
Get the thermal zones delimited by the thermal boundary
:param value: [ThermalZone]
"""
self._thermal_zones = value
2022-03-08 19:19:52 -05:00
# todo: do I need these two??
2020-10-28 13:42:58 -04:00
@property
def azimuth(self):
"""
Get the thermal boundary azimuth in radians
2020-10-28 13:42:58 -04:00
:return: float
"""
2022-03-08 19:19:52 -05:00
return self.parent_surface.azimuth
2020-10-28 13:42:58 -04:00
@property
def inclination(self):
"""
2022-03-08 19:19:52 -05:00
Get the thermal boundary inclination in radians
2020-10-28 13:42:58 -04:00
:return: float
"""
2022-03-08 19:19:52 -05:00
return self.parent_surface.inclination
2020-10-28 13:42:58 -04:00
@property
2022-03-08 19:19:52 -05:00
def opaque_area(self):
2020-10-28 13:42:58 -04:00
"""
2022-03-08 19:19:52 -05:00
Get the thermal boundary area in square meters
2020-10-28 13:42:58 -04:00
:return: float
"""
2022-03-08 19:19:52 -05:00
return float(self._opaque_area)
2020-10-28 13:42:58 -04:00
@property
def thickness(self):
2020-10-28 13:42:58 -04:00
"""
Get the thermal boundary thickness in meters
2020-10-28 13:42:58 -04:00
:return: float
"""
if self._thickness is None:
self._thickness = 0.0
if self.layers is not None:
for layer in self.layers:
2022-03-08 19:19:52 -05:00
if not layer.material.no_mass:
self._thickness += layer.thickness
return self._thickness
2020-10-28 13:42:58 -04:00
@property
2021-09-14 13:46:48 -04:00
def outside_solar_absorptance(self) -> Union[None, float]:
2020-10-28 13:42:58 -04:00
"""
Get thermal boundary outside solar absorptance
2021-09-14 13:46:48 -04:00
:return: None or float
2020-10-28 13:42:58 -04:00
"""
return self._outside_solar_absorptance
@outside_solar_absorptance.setter
def outside_solar_absorptance(self, value):
"""
Set thermal boundary outside solar absorptance
:param value: float
"""
2021-09-14 13:46:48 -04:00
if value is not None:
self._outside_solar_absorptance = float(value)
self._shortwave_reflectance = 1.0 - float(value)
2020-10-28 13:42:58 -04:00
@property
2021-09-14 13:46:48 -04:00
def outside_thermal_absorptance(self) -> Union[None, float]:
2020-10-28 13:42:58 -04:00
"""
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
"""
2021-09-14 13:46:48 -04:00
if value is not None:
self._outside_thermal_absorptance = float(value)
2020-10-28 13:42:58 -04:00
@property
2021-09-14 13:46:48 -04:00
def outside_visible_absorptance(self) -> Union[None, float]:
2020-10-28 13:42:58 -04:00
"""
Get thermal boundary outside visible absorptance
2021-09-14 13:46:48 -04:00
:return: None or float
2020-10-28 13:42:58 -04:00
"""
return self._outside_visible_absorptance
@outside_visible_absorptance.setter
def outside_visible_absorptance(self, value):
"""
Set thermal boundary outside visible absorptance
:param value: float
"""
2021-09-14 13:46:48 -04:00
if value is not None:
self._outside_visible_absorptance = float(value)
2020-10-28 13:42:58 -04:00
@property
2022-03-08 19:19:52 -05:00
def thermal_openings(self) -> Union[None, List[ThermalOpening]]:
2020-10-28 13:42:58 -04:00
"""
Get thermal boundary thermal openings
:return: [ThermalOpening]
"""
if self._thermal_openings is None:
2022-03-08 19:19:52 -05:00
if self.window_ratio is not None:
if self.window_ratio == 0:
self._thermal_openings = []
else:
thermal_opening = ThermalOpening()
if self.window_ratio == 1:
_area = self.opaque_area
else:
_area = self.opaque_area * self.window_ratio / (1-self.window_ratio)
thermal_opening.area = _area
self._thermal_openings = [thermal_opening]
else:
2022-03-08 19:19:52 -05:00
if len(self.windows_areas) > 0:
self._thermal_openings = []
for window_area in self.windows_areas:
thermal_opening = ThermalOpening()
thermal_opening.area = window_area
self._thermal_openings.append(thermal_opening)
else:
self._thermal_openings = []
2020-10-28 13:42:58 -04:00
return self._thermal_openings
@property
2021-09-14 13:46:48 -04:00
def construction_name(self) -> Union[None, str]:
"""
Get construction name
2021-09-14 13:46:48 -04:00
:return: None or str
"""
return self._construction_name
@construction_name.setter
def construction_name(self, value):
"""
Set construction name
:param value: str
"""
2021-09-14 13:46:48 -04:00
if value is not None:
self._construction_name = str(value)
2020-10-28 13:42:58 -04:00
@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):
"""
Get thermal boundary surface type
2020-10-28 13:42:58 -04:00
:return: str
"""
2022-03-08 19:19:52 -05:00
return self.parent_surface.type
2020-10-28 13:42:58 -04:00
@property
2021-09-14 13:46:48 -04:00
def window_ratio(self) -> Union[None, float]:
2020-10-28 13:42:58 -04:00
"""
Get thermal boundary window ratio
2022-03-08 19:19:52 -05:00
It returns the window ratio calculated as the total windows' areas in a wall divided by
the total (opaque + transparent) area of that wall if windows are defined in the geometry imported.
If not, it returns the window ratio imported from an external source (e.g. construction library, manually assigned).
If none of those sources are available, it returns None.
:return: float
2020-10-28 13:42:58 -04:00
"""
2022-03-08 19:19:52 -05:00
if self.windows_areas is not None:
if not self._calculated:
_calculated = True
if len(self.windows_areas) == 0:
self._window_ratio = 0
else:
total_window_area = 0
for window_area in self.windows_areas:
total_window_area += window_area
self._window_ratio = total_window_area / (self.opaque_area + total_window_area)
2020-10-28 13:42:58 -04:00
return self._window_ratio
@window_ratio.setter
def window_ratio(self, value):
"""
Set thermal boundary window ratio
2022-03-08 19:19:52 -05:00
:param value: str
2020-10-28 13:42:58 -04:00
"""
2022-03-08 19:19:52 -05:00
if self._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]:
"""
Get windows areas
:return: [float]
"""
return self._windows_areas
2020-10-28 13:42:58 -04:00
@property
2021-09-14 13:46:48 -04:00
def u_value(self) -> Union[None, float]:
2020-10-28 13:42:58 -04:00
"""
Get thermal boundary U-value in W/m2K
2020-10-28 13:42:58 -04:00
internal and external convective coefficient in W/m2K values, can be configured at configuration.ini
2021-09-14 13:46:48 -04:00
:return: None or float
2020-10-28 13:42:58 -04:00
"""
if self._u_value is None:
h_i = self.hi
h_e = self.he
2020-10-28 13:42:58 -04:00
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
2020-10-28 13:42:58 -04:00
return self._u_value
2021-01-06 16:42:38 -05:00
@u_value.setter
def u_value(self, value):
"""
Set thermal boundary U-value in W/m2K
:param value: float
"""
2021-09-14 13:46:48 -04:00
if value is not None:
self._u_value = float(value)
2021-01-06 16:42:38 -05:00
2020-10-28 13:42:58 -04:00
@property
2021-09-14 13:46:48 -04:00
def shortwave_reflectance(self) -> Union[None, float]:
2020-10-28 13:42:58 -04:00
"""
Get thermal boundary shortwave reflectance
2021-09-14 13:46:48 -04:00
:return: None or float
2020-10-28 13:42:58 -04:00
"""
return self._shortwave_reflectance
@shortwave_reflectance.setter
def shortwave_reflectance(self, value):
"""
Set thermal boundary shortwave reflectance
:param value: float
"""
2021-09-14 13:46:48 -04:00
if value is not None:
self._shortwave_reflectance = float(value)
self._outside_solar_absorptance = 1.0 - float(value)
@property
2021-09-14 13:46:48 -04:00
def hi(self) -> Union[None, float]:
"""
Get internal convective heat transfer coefficient (W/m2K)
2021-09-14 13:46:48 -04:00
:return: None or float
"""
return self._hi
@hi.setter
def hi(self, value):
"""
Set internal convective heat transfer coefficient (W/m2K)
2021-09-13 15:14:54 -04:00
:param value: float
"""
2021-09-14 13:46:48 -04:00
if value is not None:
2022-03-08 19:19:52 -05:00
self._hi = value
@property
2021-09-14 13:46:48 -04:00
def he(self) -> Union[None, float]:
"""
Get external convective heat transfer coefficient (W/m2K)
2021-09-14 13:46:48 -04:00
:return: None or float
"""
return self._he
@he.setter
def he(self, value):
"""
Set external convective heat transfer coefficient (W/m2K)
:param value: float
"""
2021-09-14 13:46:48 -04:00
if value is not None:
2022-03-08 19:19:52 -05:00
self._he = value
@property
def virtual_internal_surface(self) -> Surface:
"""
Get the internal surface of the thermal boundary
:return: Surface
"""
if self._virtual_internal_surface is None:
2022-03-08 19:19:52 -05:00
self._virtual_internal_surface = self.parent_surface.inverse
return self._virtual_internal_surface
@property
2021-09-14 13:46:48 -04:00
def inside_emissivity(self) -> Union[None, float]:
"""
Get the short wave emissivity factor of the thermal boundary's internal surface (-)
2021-09-14 13:46:48 -04:00
:return: None or 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
"""
2021-09-14 13:46:48 -04:00
if value is not None:
self._inside_emissivity = float(value)
@property
2021-09-14 13:46:48 -04:00
def alpha_coefficient(self) -> Union[None, float]:
"""
Get the long wave emissivity factor of the thermal boundary's internal surface (-)
2021-09-14 13:46:48 -04:00
:return: None or 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
"""
2021-09-14 13:46:48 -04:00
if value is not None:
self._alpha_coefficient = float(value)
@property
2021-09-14 13:46:48 -04:00
def radiative_coefficient(self) -> Union[None, float]:
"""
Get the radiative coefficient of the thermal boundary's external surface (-)
2021-09-14 13:46:48 -04:00
:return: None or 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
"""
2021-09-14 13:46:48 -04:00
if value is not None:
self._radiative_coefficient = float(value)