forked from s_ranjbar/city_retrofit
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
2385827d0a
|
@ -4,10 +4,10 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
|
|||
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from typing import Union
|
||||
import numpy as np
|
||||
import pyny3d.geoms as pn
|
||||
from helpers.geometry import Geometry
|
||||
from typing import Union
|
||||
|
||||
|
||||
class Surface:
|
||||
|
@ -207,9 +207,9 @@ class Surface:
|
|||
|
||||
def _is_almost_same_terrain(self, terrain_points, ground_points):
|
||||
equal = 0
|
||||
for t in terrain_points:
|
||||
for g in ground_points:
|
||||
if self._geometry.almost_equal(t, g):
|
||||
for terrain_point in terrain_points:
|
||||
for ground_point in ground_points:
|
||||
if self._geometry.almost_equal(terrain_point, ground_point):
|
||||
equal += 1
|
||||
return equal == len(terrain_points)
|
||||
|
||||
|
@ -250,8 +250,8 @@ class Surface:
|
|||
"""
|
||||
if self._normal is None:
|
||||
points = self.points
|
||||
n = np.cross(points[1] - points[0], points[2] - points[0])
|
||||
self._normal = n / np.linalg.norm(n)
|
||||
cross_product = np.cross(points[1] - points[0], points[2] - points[0])
|
||||
self._normal = cross_product / np.linalg.norm(cross_product)
|
||||
return self._normal
|
||||
|
||||
@property
|
||||
|
@ -283,7 +283,7 @@ class Surface:
|
|||
"""
|
||||
if self._type is None:
|
||||
grad = np.rad2deg(self.inclination)
|
||||
if 170 <= grad:
|
||||
if grad >= 170:
|
||||
self._type = 'Ground'
|
||||
elif 80 <= grad <= 100:
|
||||
self._type = 'Wall'
|
||||
|
@ -307,7 +307,7 @@ class Surface:
|
|||
:param surface: Surface
|
||||
:return: None
|
||||
"""
|
||||
if self.type is not 'Wall' or surface.type is not 'Wall':
|
||||
if self.type != 'Wall' or surface.type != 'Wall':
|
||||
return
|
||||
if self._geometry.is_almost_same_surface(self, surface):
|
||||
intersection_area = self.intersect(surface).area
|
||||
|
|
|
@ -3,14 +3,17 @@ ThermalBoundary module
|
|||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
from typing import List
|
||||
from city_model_structure.thermal_opening import ThermalOpening
|
||||
from city_model_structure.thermal_zone import ThermalZone
|
||||
from city_model_structure.layer import Layer
|
||||
from helpers.configuration import Configuration
|
||||
from typing import List
|
||||
|
||||
|
||||
class ThermalBoundary:
|
||||
"""
|
||||
ThermalBoundary class
|
||||
"""
|
||||
def __init__(self, surface, delimits):
|
||||
self._surface = surface
|
||||
self._delimits = delimits
|
||||
|
@ -27,83 +30,169 @@ class ThermalBoundary:
|
|||
|
||||
@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
|
||||
"""
|
||||
return self._surface.area
|
||||
|
||||
@property
|
||||
def area_above_ground(self):
|
||||
"""
|
||||
Thermal boundary area above ground in square meters
|
||||
:return: float
|
||||
"""
|
||||
return self._surface.area_above_ground
|
||||
|
||||
@property
|
||||
def area_below_ground(self):
|
||||
"""
|
||||
Thermal boundary area below ground in square meters
|
||||
:return: float
|
||||
"""
|
||||
return self._surface.area_below_ground
|
||||
|
||||
@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
|
||||
:return: None
|
||||
"""
|
||||
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
|
||||
:return: None
|
||||
"""
|
||||
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
|
||||
:return: None
|
||||
"""
|
||||
self._outside_visible_absorptance = value
|
||||
|
||||
@property
|
||||
def thermal_openings(self) -> List[ThermalOpening]:
|
||||
"""
|
||||
Get thermal boundary thermal openings
|
||||
:return: [ThermalOpening]
|
||||
"""
|
||||
return self._thermal_openings
|
||||
|
||||
@thermal_openings.setter
|
||||
def thermal_openings(self, value):
|
||||
"""
|
||||
Set thermal boundary thermal openings
|
||||
:param value: [ThermalOpening]
|
||||
:return: None
|
||||
"""
|
||||
self._thermal_openings = 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]
|
||||
:return: None
|
||||
"""
|
||||
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
|
||||
:return: None
|
||||
"""
|
||||
self._window_ratio = value
|
||||
|
||||
@property
|
||||
def window_area(self):
|
||||
"""
|
||||
Thermal boundary window area in square meters
|
||||
:return: float
|
||||
"""
|
||||
if self._window_area is None:
|
||||
try:
|
||||
self._window_area = float(self._surface.area) * float(self.window_ratio)
|
||||
|
@ -113,6 +202,11 @@ class ThermalBoundary:
|
|||
|
||||
@property
|
||||
def u_value(self):
|
||||
"""
|
||||
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 = Configuration().h_i
|
||||
h_e = Configuration().h_e
|
||||
|
@ -130,9 +224,18 @@ class ThermalBoundary:
|
|||
|
||||
@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
|
||||
:return:
|
||||
"""
|
||||
self._shortwave_reflectance = value
|
||||
self._outside_solar_absorptance = 1.0 - float(value)
|
||||
|
|
|
@ -7,90 +7,165 @@ from helpers.configuration import Configuration
|
|||
|
||||
|
||||
class ThermalOpening:
|
||||
"""
|
||||
ThermalOpening class
|
||||
"""
|
||||
def __init__(self):
|
||||
self._openable_ratio = None
|
||||
self._conductivity_w_mk = None
|
||||
self._conductivity = None
|
||||
self._frame_ratio = Configuration().frame_ratio
|
||||
self._g_value = None
|
||||
self._thickness_m = None
|
||||
self._thickness = None
|
||||
self._front_side_solar_transmittance_at_normal_incidence = None
|
||||
self._back_side_solar_transmittance_at_normal_incidence = None
|
||||
self._overall_u_value = None
|
||||
|
||||
@property
|
||||
def openable_ratio(self):
|
||||
"""
|
||||
Get thermal opening openable ratio, NOT IMPLEMENTED
|
||||
:return: Exception
|
||||
"""
|
||||
raise Exception('Not implemented')
|
||||
|
||||
@openable_ratio.setter
|
||||
def openable_ratio(self, value):
|
||||
"""
|
||||
Set thermal opening openable ratio, NOT IMPLEMENTED
|
||||
:param value: Any
|
||||
:return: Exception
|
||||
"""
|
||||
raise Exception('Not implemented')
|
||||
|
||||
@property
|
||||
def conductivity_w_mk(self):
|
||||
return self._conductivity_w_mk
|
||||
def conductivity(self):
|
||||
"""
|
||||
Get thermal opening conductivity in W/mK
|
||||
:return: float
|
||||
"""
|
||||
return self._conductivity
|
||||
|
||||
@conductivity_w_mk.setter
|
||||
def conductivity_w_mk(self, value):
|
||||
@conductivity.setter
|
||||
def conductivity(self, value):
|
||||
"""
|
||||
Get thermal opening conductivity in W/mK
|
||||
:param value: float
|
||||
:return: None
|
||||
"""
|
||||
# The code to calculate overall_u_value is duplicated here and in thickness_m.
|
||||
# This ensures a more robust code that returns the overall_u_value regardless the order the parameters are read.
|
||||
self._conductivity_w_mk = value
|
||||
if self._overall_u_value is None and self.thickness_m is not None:
|
||||
self._conductivity = value
|
||||
if self._overall_u_value is None and self.thickness is not None:
|
||||
h_i = Configuration().h_i
|
||||
h_e = Configuration().h_e
|
||||
r_value = 1 / h_i + 1 / h_e + float(self.conductivity_w_mk) / float(self.thickness_m)
|
||||
r_value = 1 / h_i + 1 / h_e + float(self.conductivity) / float(self.thickness)
|
||||
self._overall_u_value = 1 / r_value
|
||||
|
||||
@property
|
||||
def frame_ratio(self):
|
||||
"""
|
||||
Get thermal opening frame ratio
|
||||
:return: float
|
||||
"""
|
||||
return self._frame_ratio
|
||||
|
||||
@frame_ratio.setter
|
||||
def frame_ratio(self, value):
|
||||
"""
|
||||
Set thermal opening frame ratio
|
||||
:param value: float
|
||||
:return: None
|
||||
"""
|
||||
self._frame_ratio = value
|
||||
|
||||
@property
|
||||
def g_value(self):
|
||||
"""
|
||||
Get thermal opening g value
|
||||
:return: float
|
||||
"""
|
||||
return self._g_value
|
||||
|
||||
@g_value.setter
|
||||
def g_value(self, value):
|
||||
"""
|
||||
Set thermal opening g value
|
||||
:param value:
|
||||
:return:
|
||||
"""
|
||||
self._g_value = value
|
||||
|
||||
@property
|
||||
def thickness_m(self):
|
||||
return self._thickness_m
|
||||
def thickness(self):
|
||||
"""
|
||||
Get thermal opening thickness in meters
|
||||
:return:
|
||||
"""
|
||||
return self._thickness
|
||||
|
||||
@thickness_m.setter
|
||||
def thickness_m(self, value):
|
||||
# The code to calculate overall_u_value is duplicated here and in conductivity_w_mk.
|
||||
@thickness.setter
|
||||
def thickness(self, value):
|
||||
"""
|
||||
Set thermal opening thickness in meters
|
||||
:param value: float
|
||||
:return: None
|
||||
"""
|
||||
# The code to calculate overall_u_value is duplicated here and in conductivity.
|
||||
# This ensures a more robust code that returns the overall_u_value regardless the order the parameters are read.
|
||||
self._thickness_m = value
|
||||
if self._overall_u_value is None and self.conductivity_w_mk is not None:
|
||||
self._thickness = value
|
||||
if self._overall_u_value is None and self.conductivity is not None:
|
||||
h_i = Configuration().h_i
|
||||
h_e = Configuration().h_e
|
||||
r_value = 1 / h_i + 1 / h_e + float(self.conductivity_w_mk) / float(self.thickness_m)
|
||||
r_value = 1 / h_i + 1 / h_e + float(self.conductivity) / float(self.thickness)
|
||||
self._overall_u_value = 1 / r_value
|
||||
|
||||
@property
|
||||
def front_side_solar_transmittance_at_normal_incidence(self):
|
||||
"""
|
||||
Get thermal opening front side solar transmittance at normal incidence
|
||||
:return: float
|
||||
"""
|
||||
return self._front_side_solar_transmittance_at_normal_incidence
|
||||
|
||||
@front_side_solar_transmittance_at_normal_incidence.setter
|
||||
def front_side_solar_transmittance_at_normal_incidence(self, value):
|
||||
"""
|
||||
Set thermal opening front side solar transmittance at normal incidence
|
||||
:param value: float
|
||||
:return: None
|
||||
"""
|
||||
self._front_side_solar_transmittance_at_normal_incidence = value
|
||||
|
||||
@property
|
||||
def back_side_solar_transmittance_at_normal_incidence(self):
|
||||
"""
|
||||
Get thermal opening back side solar transmittance at normal incidence
|
||||
:return: float
|
||||
"""
|
||||
return self._back_side_solar_transmittance_at_normal_incidence
|
||||
|
||||
@back_side_solar_transmittance_at_normal_incidence.setter
|
||||
def back_side_solar_transmittance_at_normal_incidence(self, value):
|
||||
"""
|
||||
Set thermal opening back side solar transmittance at normal incidence
|
||||
:param value: float
|
||||
:return: None
|
||||
"""
|
||||
self._back_side_solar_transmittance_at_normal_incidence = value
|
||||
|
||||
@property
|
||||
def overall_u_value(self):
|
||||
"""
|
||||
Get thermal opening overall u value
|
||||
:return: float
|
||||
"""
|
||||
return self._overall_u_value
|
||||
|
||||
@overall_u_value.setter
|
||||
def overall_u_value(self, value):
|
||||
"""
|
||||
Get thermal opening overall u value
|
||||
:param value: float
|
||||
:return: None
|
||||
"""
|
||||
self._overall_u_value = value
|
||||
|
|
|
@ -3,8 +3,8 @@ UsageZone module
|
|||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
from city_model_structure.internal_gains import InternalGains
|
||||
from typing import List
|
||||
from city_model_structure.internal_gains import InternalGains
|
||||
|
||||
|
||||
class UsageZone:
|
||||
|
@ -41,59 +41,118 @@ class UsageZone:
|
|||
@property
|
||||
def heating_setpoint(self):
|
||||
"""
|
||||
|
||||
:return:
|
||||
Get usage zone heating set point in celsius grads
|
||||
:return: float
|
||||
"""
|
||||
return self._heating_setpoint
|
||||
|
||||
@heating_setpoint.setter
|
||||
def heating_setpoint(self, value):
|
||||
"""
|
||||
Set usage zone heating set point in celsius grads
|
||||
:param value: float
|
||||
:return: None
|
||||
"""
|
||||
self._heating_setpoint = value
|
||||
|
||||
@property
|
||||
def heating_setback(self):
|
||||
"""
|
||||
Get usage zone heating setback in celsius grads
|
||||
:return: float
|
||||
"""
|
||||
return self._heating_setback
|
||||
|
||||
@heating_setback.setter
|
||||
def heating_setback(self, value):
|
||||
"""
|
||||
Set usage zone heating setback in celsius grads
|
||||
:param value: float
|
||||
:return: None
|
||||
"""
|
||||
self._heating_setback = value
|
||||
|
||||
@property
|
||||
def cooling_setpoint(self):
|
||||
"""
|
||||
Get usage zone cooling setpoint in celsius grads
|
||||
:return: float
|
||||
"""
|
||||
return self._cooling_setpoint
|
||||
|
||||
@cooling_setpoint.setter
|
||||
def cooling_setpoint(self, value):
|
||||
"""
|
||||
Set usage zone cooling setpoint in celsius grads
|
||||
:param value: float
|
||||
:return: None
|
||||
"""
|
||||
self._cooling_setpoint = value
|
||||
|
||||
@property
|
||||
def hours_day(self):
|
||||
"""
|
||||
Get usage zone usage hours per day
|
||||
:return: float
|
||||
"""
|
||||
return self._hours_day
|
||||
|
||||
@hours_day.setter
|
||||
def hours_day(self, value):
|
||||
"""
|
||||
Set usage zone usage hours per day
|
||||
:param value: float
|
||||
:return: float
|
||||
"""
|
||||
self._hours_day = value
|
||||
|
||||
@property
|
||||
def days_year(self):
|
||||
"""
|
||||
Get usage zone usage days per year
|
||||
:return: float
|
||||
"""
|
||||
return self._days_year
|
||||
|
||||
@days_year.setter
|
||||
def days_year(self, value):
|
||||
"""
|
||||
Set usage zone usage days per year
|
||||
:param value: float
|
||||
:return: None
|
||||
"""
|
||||
self._days_year = value
|
||||
|
||||
@property
|
||||
def mechanical_air_change(self):
|
||||
"""
|
||||
Set usage zone mechanical air change in air change per hour
|
||||
:return: float
|
||||
"""
|
||||
return self._mechanical_air_change
|
||||
|
||||
@mechanical_air_change.setter
|
||||
def mechanical_air_change(self, value):
|
||||
"""
|
||||
Get usage zone mechanical air change in air change per hour
|
||||
:param value: float
|
||||
:return: None
|
||||
"""
|
||||
self._mechanical_air_change = value
|
||||
|
||||
@property
|
||||
def usage(self):
|
||||
"""
|
||||
Get usage zone usage
|
||||
:return: str
|
||||
"""
|
||||
return self._usage
|
||||
|
||||
@usage.setter
|
||||
def usage(self, value):
|
||||
"""
|
||||
Get usage zone usage
|
||||
:param value: str
|
||||
:return: None
|
||||
"""
|
||||
self._usage = value
|
||||
|
|
|
@ -9,21 +9,25 @@ from city_model_structure.city import City
|
|||
|
||||
class GeometryFactory:
|
||||
def __init__(self, file_type, path):
|
||||
self._file_type = file_type.lower()
|
||||
self._file_type = '_' + file_type.lower()
|
||||
self._path = path
|
||||
|
||||
@property
|
||||
def citygml(self):
|
||||
def _citygml(self):
|
||||
return CityGml(self._path).city
|
||||
|
||||
@property
|
||||
def geojson(self):
|
||||
def _geojson(self):
|
||||
raise Exception('Not implemented')
|
||||
|
||||
@property
|
||||
def bim(self):
|
||||
def _bim(self):
|
||||
raise Exception('Not implemented')
|
||||
|
||||
@property
|
||||
def city(self) -> City:
|
||||
"""
|
||||
Geometry factory city model structure with geometry
|
||||
:return: City
|
||||
"""
|
||||
return getattr(self, self._file_type, lambda: None)
|
||||
|
|
|
@ -12,6 +12,9 @@ from helpers.geometry import Geometry
|
|||
|
||||
|
||||
class CityGml:
|
||||
"""
|
||||
CityGml class
|
||||
"""
|
||||
def __init__(self, path):
|
||||
self._city = None
|
||||
with open(path) as gml:
|
||||
|
@ -42,10 +45,18 @@ class CityGml:
|
|||
|
||||
@property
|
||||
def content(self):
|
||||
"""
|
||||
CityGml raw content
|
||||
:return: str
|
||||
"""
|
||||
return self._gml
|
||||
|
||||
@property
|
||||
def city(self):
|
||||
def city(self) -> City:
|
||||
"""
|
||||
City model structure enriched with the geometry information
|
||||
:return: City
|
||||
"""
|
||||
if self._city is None:
|
||||
self._city = City(self._lower_corner, self._upper_corner, self._srs_name)
|
||||
for o in self._gml['CityModel']['cityObjectMember']:
|
||||
|
|
|
@ -8,6 +8,9 @@ from pathlib import Path
|
|||
|
||||
|
||||
class Configuration:
|
||||
"""
|
||||
Configuration class
|
||||
"""
|
||||
def __init__(self):
|
||||
base_path = Path().resolve().parent
|
||||
config_file = Path(base_path / 'libs/config/configuration.ini').resolve()
|
||||
|
@ -16,36 +19,73 @@ class Configuration:
|
|||
|
||||
@property
|
||||
def h_i(self):
|
||||
"""
|
||||
Configured internal convective coefficient in W/m2K
|
||||
:return: float
|
||||
"""
|
||||
return self._config.getfloat('convective_fluxes', 'h_i')
|
||||
|
||||
@property
|
||||
def h_e(self):
|
||||
"""
|
||||
Configured external convective coefficient in W/m2K
|
||||
:return: float
|
||||
"""
|
||||
return self._config.getfloat('convective_fluxes', 'h_e')
|
||||
|
||||
@property
|
||||
def frame_ratio(self):
|
||||
"""
|
||||
Configured frame ratio
|
||||
:return: float
|
||||
"""
|
||||
return self._config.getfloat('windows', 'frame_ratio')
|
||||
|
||||
@property
|
||||
def heated(self):
|
||||
"""
|
||||
Configured heated flag
|
||||
:return: Boolean
|
||||
"""
|
||||
return self._config.getboolean('thermal_zones', 'heated')
|
||||
|
||||
@property
|
||||
def cooled(self):
|
||||
"""
|
||||
Configured cooled flag
|
||||
:return: Boolean
|
||||
"""
|
||||
return self._config.getboolean('thermal_zones', 'cooled')
|
||||
|
||||
@property
|
||||
def additional_thermal_bridge_u_value(self):
|
||||
"""
|
||||
Configured additional thermal bridge u value W/m2K
|
||||
:return:
|
||||
"""
|
||||
return self._config.getfloat('thermal_zones', 'additional_thermal_bridge_u_value')
|
||||
|
||||
@property
|
||||
def indirectly_heated_area_ratio(self):
|
||||
"""
|
||||
Configured indirectly heated area ratio
|
||||
:return: float
|
||||
"""
|
||||
|
||||
return self._config.getfloat('thermal_zones', 'indirectly_heated_area_ratio')
|
||||
|
||||
@property
|
||||
def infiltration_rate_system_on(self):
|
||||
"""
|
||||
Configured infiltration rate system on in air change per hour
|
||||
:return: float
|
||||
"""
|
||||
return self._config.getfloat('thermal_zones', 'infiltration_rate_system_on')
|
||||
|
||||
@property
|
||||
def outside_solar_absorptance(self):
|
||||
"""
|
||||
Configured infiltration rate system off in air change per hour
|
||||
:return: float
|
||||
"""
|
||||
return self._config.getfloat('thermal_zones', 'outside_solar_absorptance')
|
||||
|
|
|
@ -11,14 +11,29 @@ import open3d as o3d
|
|||
|
||||
|
||||
class Geometry:
|
||||
"""
|
||||
Geometry helper class
|
||||
"""
|
||||
def __init__(self, delta=0.5):
|
||||
self._delta = delta
|
||||
|
||||
def almost_equal(self, v1, v2):
|
||||
"""
|
||||
Compare two points and decides if they are almost equal (quadratic error under delta)
|
||||
:param v1: [x,y,z]
|
||||
:param v2: [x,y,z]
|
||||
:return: Boolean
|
||||
"""
|
||||
delta = math.sqrt(pow((v1[0] - v2[0]), 2) + pow((v1[1] - v2[1]), 2) + pow((v1[2] - v2[2]), 2))
|
||||
return delta <= self._delta
|
||||
|
||||
def is_almost_same_surface(self, s1, s2):
|
||||
"""
|
||||
Compare two surfaces and decides if they are almost equal (quadratic error under delta)
|
||||
:param s1: Surface
|
||||
:param s2: Surface
|
||||
:return: Boolean
|
||||
"""
|
||||
# delta is grads an need to be converted into radians
|
||||
delta = np.rad2deg(self._delta)
|
||||
difference = (s1.inclination - s2.inclination) % math.pi
|
||||
|
@ -49,6 +64,12 @@ class Geometry:
|
|||
|
||||
@staticmethod
|
||||
def to_points_matrix(points, remove_last=False):
|
||||
"""
|
||||
Transform a point vector into a point matrix
|
||||
:param points: [x, y, z, x, y, z ...]
|
||||
:param remove_last: Boolean
|
||||
:return: [[x,y,z],[x,y,z]...]
|
||||
"""
|
||||
rows = points.size // 3
|
||||
points = points.reshape(rows, 3)
|
||||
if remove_last:
|
||||
|
@ -107,6 +128,13 @@ class Geometry:
|
|||
|
||||
@staticmethod
|
||||
def divide_mesh_by_plane(mesh, normal_plane, point_plane):
|
||||
"""
|
||||
Divide a mesh by a plane
|
||||
:param mesh: Trimesh
|
||||
:param normal_plane: [x, y, z]
|
||||
:param point_plane: [x, y, z]
|
||||
:return: [Trimesh]
|
||||
"""
|
||||
# The first mesh returns the positive side of the plane and the second the negative side.
|
||||
# If the plane does not divide the mesh (i.e. it does not touch it or it is coplanar with one or more faces),
|
||||
# then it returns only the original mesh.
|
||||
|
|
|
@ -8,26 +8,33 @@ from physics.physics_feeders.us_physics_parameters import UsPhysicsParameters
|
|||
|
||||
|
||||
class PhysicsFactory:
|
||||
"""
|
||||
PhysicsFactor class
|
||||
"""
|
||||
def __init__(self, handler, city, base_path='data/physics'):
|
||||
self._handler = handler.lower().replace(' ', '_')
|
||||
self._handler = '_' + handler.lower().replace(' ', '_')
|
||||
self._city = city
|
||||
self._base_path = base_path
|
||||
self.factory()
|
||||
|
||||
def us_new_york_city(self):
|
||||
def _us_new_york_city(self):
|
||||
UsNewYorkCityPhysicsParameters(self._city, self._base_path)
|
||||
|
||||
def us(self):
|
||||
def _us(self):
|
||||
UsPhysicsParameters(self._city, self._base_path)
|
||||
|
||||
def ca(self):
|
||||
def _ca(self):
|
||||
raise Exception('Not implemented')
|
||||
|
||||
def de(self):
|
||||
def _de(self):
|
||||
raise Exception('Not implemented')
|
||||
|
||||
def es(self):
|
||||
def _es(self):
|
||||
raise Exception('Not implemented')
|
||||
|
||||
def factory(self):
|
||||
"""
|
||||
Enrich the city with the physics information
|
||||
:return: None
|
||||
"""
|
||||
getattr(self, self._handler, lambda: None)()
|
||||
|
|
|
@ -6,6 +6,9 @@ Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc
|
|||
|
||||
|
||||
class UsPlutoToFunction:
|
||||
"""
|
||||
UsPlutoToFunction
|
||||
"""
|
||||
building_function = {
|
||||
'A0': 'single family house',
|
||||
'A1': 'single family house',
|
||||
|
|
|
@ -5,7 +5,10 @@ Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc
|
|||
"""
|
||||
|
||||
|
||||
class UsToLibraryTypes(object):
|
||||
class UsToLibraryTypes:
|
||||
"""
|
||||
UsToLibraryTypes
|
||||
"""
|
||||
standards = {
|
||||
'ASHRAE Std189': 1,
|
||||
'ASHRAE 90.1-2004': 2
|
||||
|
@ -44,6 +47,11 @@ class UsToLibraryTypes(object):
|
|||
|
||||
@staticmethod
|
||||
def yoc_to_standard(year_of_construction):
|
||||
"""
|
||||
Year of construction to standard
|
||||
:param year_of_construction: int
|
||||
:return: str
|
||||
"""
|
||||
if int(year_of_construction) < 2009:
|
||||
standard = 'ASHRAE 90.1_2004'
|
||||
else:
|
||||
|
@ -52,6 +60,11 @@ class UsToLibraryTypes(object):
|
|||
|
||||
@staticmethod
|
||||
def city_to_reference_city(city):
|
||||
"""
|
||||
City name to reference city
|
||||
:param city: str
|
||||
:return: str
|
||||
"""
|
||||
# ToDo: Dummy function that need to be implemented
|
||||
reference_city = 'Baltimore'
|
||||
if city is not None:
|
||||
|
@ -60,5 +73,10 @@ class UsToLibraryTypes(object):
|
|||
|
||||
@staticmethod
|
||||
def city_to_climate_zone(city):
|
||||
"""
|
||||
City name to climate zone
|
||||
:param city: str
|
||||
:return: str
|
||||
"""
|
||||
reference_city = UsToLibraryTypes.city_to_reference_city(city)
|
||||
return UsToLibraryTypes.reference_city_climate_zone[reference_city]
|
||||
|
|
|
@ -4,14 +4,17 @@ UsPhysicsParameters as base class
|
|||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
from pathlib import Path
|
||||
import xmltodict
|
||||
from city_model_structure.layer import Layer
|
||||
from city_model_structure.material import Material
|
||||
from pathlib import Path
|
||||
from physics.physics_feeders.helpers.us_to_library_types import UsToLibraryTypes
|
||||
|
||||
|
||||
class UsBasePhysicsParameters:
|
||||
"""
|
||||
UsBasePhysicsParameters class
|
||||
"""
|
||||
def __init__(self, climate_zone, city_objects, function_to_type, base_path):
|
||||
self._climate_zone = climate_zone
|
||||
self._city_objects = city_objects
|
||||
|
@ -29,7 +32,7 @@ class UsBasePhysicsParameters:
|
|||
building_type = function_to_type(city_object.function)
|
||||
if building_type is None:
|
||||
return
|
||||
archetype = self.search_archetype(building_type,
|
||||
archetype = self._search_archetype(building_type,
|
||||
UsToLibraryTypes.yoc_to_standard(city_object.year_of_construction),
|
||||
self._climate_zone)
|
||||
# ToDo:remove this in the future
|
||||
|
@ -50,9 +53,9 @@ class UsBasePhysicsParameters:
|
|||
thermal_zone.infiltration_rate_system_on = archetype['infiltration_rate_for_ventilation_system_on']['#text']
|
||||
for thermal_boundary in thermal_zone.bounded:
|
||||
construction_type = UsToLibraryTypes.construction_types[thermal_boundary.type]
|
||||
construction = UsBasePhysicsParameters.search_construction_in_archetype(archetype, construction_type)
|
||||
construction = UsBasePhysicsParameters._search_construction_in_archetype(archetype, construction_type)
|
||||
construction_id = construction['@id']
|
||||
c_lib = self.search_construction_type('construction', construction_id)
|
||||
c_lib = self._search_construction_type('construction', construction_id)
|
||||
if 'outside_solar_absorptance' in c_lib:
|
||||
thermal_boundary.outside_solar_absorptance = c_lib['outside_solar_absorptance']['#text']
|
||||
thermal_boundary.outside_thermal_absorptance = c_lib['outside_thermal_absorptance']['#text']
|
||||
|
@ -63,7 +66,7 @@ class UsBasePhysicsParameters:
|
|||
layer = Layer()
|
||||
if 'thickness' in current_layer:
|
||||
layer.thickness = current_layer['thickness']['#text']
|
||||
material_lib = self.search_construction_type('material', current_layer['material'])
|
||||
material_lib = self._search_construction_type('material', current_layer['material'])
|
||||
material = Material()
|
||||
if 'conductivity' in material_lib:
|
||||
material.conductivity = material_lib['conductivity']['#text']
|
||||
|
@ -80,8 +83,8 @@ class UsBasePhysicsParameters:
|
|||
for opening in thermal_boundary.thermal_openings:
|
||||
if construction['window'] is None:
|
||||
continue
|
||||
w_lib = self.search_construction_type('window', construction['window'])
|
||||
opening.conductivity_w_mk = w_lib['conductivity']['#text']
|
||||
w_lib = self._search_construction_type('window', construction['window'])
|
||||
opening.conductivity = w_lib['conductivity']['#text']
|
||||
opening.frame_ratio = w_lib['frame_ratio']['#text']
|
||||
opening.g_value = w_lib['solar_transmittance_at_normal_incidence']['#text']
|
||||
opening.thickness = w_lib['thickness']['#text']
|
||||
|
@ -90,7 +93,7 @@ class UsBasePhysicsParameters:
|
|||
opening.front_side_solar_transmittance_at_normal_incidence = \
|
||||
w_lib['front_side_solar_transmittance_at_normal_incidence']['#text']
|
||||
|
||||
def search_archetype(self, building_type, standard, climate_zone):
|
||||
def _search_archetype(self, building_type, standard, climate_zone):
|
||||
for archetype in self._archetypes['archetypes']['archetype']:
|
||||
a_yc = str(archetype['@reference_standard'])
|
||||
a_bt = str(archetype['@building_type'])
|
||||
|
@ -99,14 +102,14 @@ class UsBasePhysicsParameters:
|
|||
return archetype
|
||||
return None
|
||||
|
||||
def search_construction_type(self, construction_type, construction_id):
|
||||
def _search_construction_type(self, construction_type, construction_id):
|
||||
for c_lib in self._library['library'][construction_type + 's'][construction_type]:
|
||||
if construction_id == c_lib['@id']:
|
||||
return c_lib
|
||||
raise Exception('Archetype definition contains elements that does not exist in the library')
|
||||
|
||||
@staticmethod
|
||||
def search_construction_in_archetype(archetype, construction_type):
|
||||
def _search_construction_in_archetype(archetype, construction_type):
|
||||
for construction in archetype['constructions']['construction']:
|
||||
if construction['@type'] == construction_type:
|
||||
return construction
|
||||
|
|
|
@ -8,6 +8,9 @@ from physics.physics_feeders.helpers.us_pluto_to_function import UsPlutoToFuncti
|
|||
|
||||
|
||||
class UsNewYorkCityPhysicsParameters(UsBasePhysicsParameters):
|
||||
"""
|
||||
UsNewYorkCityPhysicsParameters class
|
||||
"""
|
||||
def __init__(self, city, base_path):
|
||||
self._city = city
|
||||
climate_zone = 'ASHRAE_2004:4A'
|
||||
|
|
|
@ -8,6 +8,9 @@ from physics.physics_feeders.helpers.us_to_library_types import UsToLibraryTypes
|
|||
|
||||
|
||||
class UsPhysicsParameters(UsBasePhysicsParameters):
|
||||
"""
|
||||
UsPhysicsParameters class
|
||||
"""
|
||||
def __init__(self, city, base_path):
|
||||
self._city = city
|
||||
self._climate_zone = UsToLibraryTypes.city_to_climate_zone(city.name)
|
||||
|
|
3
pylintrc
3
pylintrc
|
@ -140,6 +140,9 @@ disable=print-statement,
|
|||
exception-escape,
|
||||
comprehension-escape,
|
||||
import-error,
|
||||
parse-error,
|
||||
syntax-error,
|
||||
bad-continuation,
|
||||
no-name-in-module
|
||||
|
||||
# Enable the message, report, category or checker with the given id(s). You can
|
||||
|
|
|
@ -7,15 +7,25 @@ from unittest import TestCase
|
|||
from pathlib import Path
|
||||
from geometry.geometry_factory import GeometryFactory
|
||||
import os
|
||||
from city_model_structure.surface import Surface
|
||||
|
||||
|
||||
class TestGeometryFactory(TestCase):
|
||||
"""
|
||||
TestGeometryFactory TestCase
|
||||
"""
|
||||
def setUp(self) -> None:
|
||||
"""
|
||||
Test setup
|
||||
:return: None
|
||||
"""
|
||||
self._city_gml = None
|
||||
self._example_path = (Path(__file__).parent.parent / 'tests_data').resolve()
|
||||
|
||||
def get_citygml(self):
|
||||
"""
|
||||
Retrieve the test city gml
|
||||
:return: City
|
||||
"""
|
||||
if self._city_gml is None:
|
||||
file_path = (self._example_path / '2050 bp_2buildings.gml').resolve()
|
||||
self._city_gml = GeometryFactory('citygml', file_path).city
|
||||
|
@ -23,6 +33,10 @@ class TestGeometryFactory(TestCase):
|
|||
return self._city_gml
|
||||
|
||||
def test_citygml_city(self):
|
||||
"""
|
||||
Test the City parsing
|
||||
:return: None
|
||||
"""
|
||||
city = self.get_citygml()
|
||||
self.assertIsNotNone(city.city_objects, 'city_objects is none')
|
||||
for city_object in city.city_objects:
|
||||
|
@ -34,6 +48,10 @@ class TestGeometryFactory(TestCase):
|
|||
self.assertIsNotNone(city.country_code, 'country code is none')
|
||||
|
||||
def test_citygml_city_objects(self):
|
||||
"""
|
||||
Test city objects in the city
|
||||
:return: None
|
||||
"""
|
||||
city = self.get_citygml()
|
||||
for city_object in city.city_objects:
|
||||
self.assertIsNotNone(city_object.name, 'city_object name is none')
|
||||
|
@ -58,6 +76,10 @@ class TestGeometryFactory(TestCase):
|
|||
os.remove(Path(self._example_path, city_object.name + '.stl').resolve())
|
||||
|
||||
def test_citygml_surfaces(self):
|
||||
"""
|
||||
Test surfaces in city objects
|
||||
:return: None
|
||||
"""
|
||||
city = self.get_citygml()
|
||||
for city_object in city.city_objects:
|
||||
for surface in city_object.surfaces:
|
||||
|
@ -85,6 +107,10 @@ class TestGeometryFactory(TestCase):
|
|||
self.assertIsNotNone(surface.intersect(surface), 'self intersection is none')
|
||||
|
||||
def test_citygml_thermal_zone(self):
|
||||
"""
|
||||
Test thermal zones in city objects
|
||||
:return: None
|
||||
"""
|
||||
city = self.get_citygml()
|
||||
for city_object in city.city_objects:
|
||||
for thermal_zone in city_object.thermal_zones:
|
||||
|
@ -97,8 +123,8 @@ class TestGeometryFactory(TestCase):
|
|||
'thermal_zone additional_thermal_bridge_u_value is not none')
|
||||
self.assertIsNone(thermal_zone.effective_thermal_capacity,
|
||||
'thermal_zone effective_thermal_capacity is not none')
|
||||
self.assertIsNone(thermal_zone.indirectly_heated_area_ratio
|
||||
, 'thermal_zone indirectly_heated_area_ratio is not none')
|
||||
self.assertIsNone(thermal_zone.indirectly_heated_area_ratio,
|
||||
'thermal_zone indirectly_heated_area_ratio is not none')
|
||||
self.assertIsNone(thermal_zone.infiltration_rate_system_off,
|
||||
'thermal_zone infiltration_rate_system_off is not none')
|
||||
self.assertIsNone(thermal_zone.infiltration_rate_system_on,
|
||||
|
@ -107,6 +133,10 @@ class TestGeometryFactory(TestCase):
|
|||
'thermal_zone usage_zones are not none')
|
||||
|
||||
def test_citygml_thermal_boundary(self):
|
||||
"""
|
||||
Test thermal boundaries in thermal zones
|
||||
:return: None
|
||||
"""
|
||||
city = self.get_citygml()
|
||||
for city_object in city.city_objects:
|
||||
for thermal_zone in city_object.thermal_zones:
|
||||
|
@ -133,6 +163,10 @@ class TestGeometryFactory(TestCase):
|
|||
self.assertIsNone(thermal_boundary.window_ratio, 'thermal_boundary window_ratio was initialized')
|
||||
|
||||
def test_citygml_thermal_opening(self):
|
||||
"""
|
||||
Test thermal openings in thermal zones
|
||||
:return: None
|
||||
"""
|
||||
city = self.get_citygml()
|
||||
for city_object in city.city_objects:
|
||||
for thermal_zone in city_object.thermal_zones:
|
||||
|
@ -141,11 +175,11 @@ class TestGeometryFactory(TestCase):
|
|||
self.assertIsNone(thermal_opening.area, 'thermal_opening area was initialized')
|
||||
self.assertTrue(thermal_opening.frame_ratio == 0, 'thermal_opening frame_ratio was not 0')
|
||||
self.assertIsNone(thermal_opening.g_value, 'thermal_opening g_value was initialized')
|
||||
self.assertIsNone(thermal_opening.conductivity_w_mk, 'thermal_opening conductivity_w_mk was initialized')
|
||||
self.assertIsNone(thermal_opening.conductivity, 'thermal_opening conductivity_w_mk was initialized')
|
||||
self.assertIsNone(thermal_opening.inside_reflectance, 'thermal_opening inside_reflectance was initialized')
|
||||
self.assertRaises(Exception, lambda: thermal_opening.openable_ratio,
|
||||
'thermal_opening openable_ratio is not raising an exception')
|
||||
self.assertIsNone(thermal_opening.outside_reflectance,
|
||||
'thermal_opening outside_reflectance was initialized')
|
||||
self.assertIsNone(thermal_opening.thickness_m, 'thermal_opening thickness_m was initialized')
|
||||
self.assertIsNone(thermal_opening.thickness, 'thermal_opening thickness_m was initialized')
|
||||
self.assertRaises(Exception, lambda: thermal_opening.u_value, 'thermal_opening u_value was initialized')
|
||||
|
|
|
@ -10,7 +10,10 @@ from physics.physics_factory import PhysicsFactory
|
|||
|
||||
|
||||
class TestPhysicsFactory(TestCase):
|
||||
def setUp(self) -> None:
|
||||
"""
|
||||
TestPhysicsFactory TestCase
|
||||
"""
|
||||
def setup(self) -> None:
|
||||
self._city_gml = None
|
||||
self._nyc_with_physics = None
|
||||
self._example_path = (Path(__file__).parent.parent / 'tests_data').resolve()
|
||||
|
|
Loading…
Reference in New Issue
Block a user