2020-06-16 15:12:18 -04:00
|
|
|
"""
|
2020-06-16 15:26:55 -04:00
|
|
|
Building module
|
2020-06-16 15:12:18 -04:00
|
|
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
|
|
|
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
2021-06-03 11:23:09 -04:00
|
|
|
contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
2020-06-16 15:12:18 -04:00
|
|
|
"""
|
2020-10-30 13:47:59 -04:00
|
|
|
|
2022-03-08 19:19:52 -05:00
|
|
|
from typing import List, Union, TypeVar
|
2021-08-26 11:00:59 -04:00
|
|
|
import numpy as np
|
2021-08-06 12:28:20 -04:00
|
|
|
from city_model_structure.building_demand.surface import Surface
|
|
|
|
from city_model_structure.building_demand.usage_zone import UsageZone
|
|
|
|
from city_model_structure.building_demand.storey import Storey
|
2020-06-16 15:12:18 -04:00
|
|
|
from city_model_structure.city_object import CityObject
|
2021-10-18 16:07:18 -04:00
|
|
|
from city_model_structure.building_demand.household import Household
|
2022-02-16 15:08:05 -05:00
|
|
|
from city_model_structure.attributes.polyhedron import Polyhedron
|
2021-03-31 14:17:53 -04:00
|
|
|
|
2022-03-08 19:19:52 -05:00
|
|
|
ThermalZone = TypeVar('ThermalZone')
|
|
|
|
|
2020-10-21 15:23:06 -04:00
|
|
|
|
2020-06-16 15:12:18 -04:00
|
|
|
class Building(CityObject):
|
|
|
|
"""
|
2020-06-16 15:26:55 -04:00
|
|
|
Building(CityObject) class
|
2020-06-16 15:12:18 -04:00
|
|
|
"""
|
2022-02-16 15:08:05 -05:00
|
|
|
def __init__(self, name, lod, surfaces, year_of_construction, function, city_lower_corner, terrains=None):
|
2021-06-02 11:56:38 -04:00
|
|
|
super().__init__(name, lod, surfaces, city_lower_corner)
|
2021-10-18 16:07:18 -04:00
|
|
|
self._households = None
|
2021-03-02 18:57:09 -05:00
|
|
|
self._basement_heated = None
|
|
|
|
self._attic_heated = None
|
2020-06-16 15:12:18 -04:00
|
|
|
self._terrains = terrains
|
|
|
|
self._year_of_construction = year_of_construction
|
|
|
|
self._function = function
|
|
|
|
self._average_storey_height = None
|
2021-08-12 11:08:29 -04:00
|
|
|
self._storeys_above_ground = None
|
2021-03-11 14:31:28 -05:00
|
|
|
self._floor_area = None
|
2021-03-25 09:11:30 -04:00
|
|
|
self._roof_type = None
|
2021-08-27 17:20:24 -04:00
|
|
|
self._storeys = None
|
2022-02-16 15:08:05 -05:00
|
|
|
self._geometrical_zones = None
|
2022-03-08 19:19:52 -05:00
|
|
|
self._thermal_zones = None
|
|
|
|
self._usage_zones = None
|
2020-06-16 15:12:18 -04:00
|
|
|
self._type = 'building'
|
2020-10-30 08:32:38 -04:00
|
|
|
self._heating = dict()
|
|
|
|
self._cooling = dict()
|
2021-03-31 14:17:53 -04:00
|
|
|
self._eave_height = None
|
2021-06-09 14:23:45 -04:00
|
|
|
self._grounds = []
|
|
|
|
self._roofs = []
|
|
|
|
self._walls = []
|
|
|
|
self._internal_walls = []
|
2021-04-14 10:41:51 -04:00
|
|
|
for surface_id, surface in enumerate(self.surfaces):
|
2021-06-09 14:23:45 -04:00
|
|
|
self._min_x = min(self._min_x, surface.lower_corner[0])
|
|
|
|
self._min_y = min(self._min_y, surface.lower_corner[1])
|
|
|
|
self._min_z = min(self._min_z, surface.lower_corner[2])
|
2021-04-14 10:41:51 -04:00
|
|
|
surface.id = surface_id
|
2021-08-26 09:36:10 -04:00
|
|
|
# todo: consider all type of surfaces, not only these four
|
2021-03-25 09:11:30 -04:00
|
|
|
if surface.type == 'Ground':
|
|
|
|
self._grounds.append(surface)
|
|
|
|
elif surface.type == 'Wall':
|
|
|
|
self._walls.append(surface)
|
2021-04-06 13:48:18 -04:00
|
|
|
elif surface.type == 'Roof':
|
2021-03-25 09:11:30 -04:00
|
|
|
self._roofs.append(surface)
|
2021-04-06 13:48:18 -04:00
|
|
|
else:
|
|
|
|
self._internal_walls.append(surface)
|
2021-03-25 09:11:30 -04:00
|
|
|
|
2022-02-16 15:08:05 -05:00
|
|
|
@property
|
|
|
|
def geometrical_zones(self) -> List[Polyhedron]:
|
|
|
|
if self._geometrical_zones is None:
|
|
|
|
polygons = []
|
|
|
|
for surface in self.surfaces:
|
|
|
|
polygons.append(surface.perimeter_polygon)
|
|
|
|
self._geometrical_zones = [Polyhedron(polygons)]
|
|
|
|
return self._geometrical_zones
|
|
|
|
|
2021-03-25 09:11:30 -04:00
|
|
|
@property
|
2021-09-01 09:39:27 -04:00
|
|
|
def grounds(self) -> List[Surface]:
|
2021-03-25 09:11:30 -04:00
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Get building ground surfaces
|
2021-09-01 09:39:27 -04:00
|
|
|
:return: [Surface]
|
2021-03-25 09:11:30 -04:00
|
|
|
"""
|
|
|
|
return self._grounds
|
|
|
|
|
|
|
|
@property
|
2021-09-01 09:39:27 -04:00
|
|
|
def roofs(self) -> List[Surface]:
|
2021-03-25 09:11:30 -04:00
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Get building roof surfaces
|
|
|
|
:return: [Surface]
|
2021-03-25 09:11:30 -04:00
|
|
|
"""
|
|
|
|
return self._roofs
|
|
|
|
|
|
|
|
@property
|
2021-09-01 09:39:27 -04:00
|
|
|
def walls(self) -> List[Surface]:
|
2021-03-25 09:11:30 -04:00
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Get building wall surfaces
|
|
|
|
:return: [Surface]
|
2021-03-25 09:11:30 -04:00
|
|
|
"""
|
|
|
|
return self._walls
|
2020-06-16 15:12:18 -04:00
|
|
|
|
|
|
|
@property
|
2022-03-08 19:19:52 -05:00
|
|
|
def usage_zones(self) -> Union[None, List[UsageZone]]:
|
2020-06-16 15:12:18 -04:00
|
|
|
"""
|
2021-06-03 15:56:59 -04:00
|
|
|
Get city object usage zones
|
2020-06-16 15:12:18 -04:00
|
|
|
:return: [UsageZone]
|
|
|
|
"""
|
|
|
|
return self._usage_zones
|
|
|
|
|
2022-03-08 19:19:52 -05:00
|
|
|
@usage_zones.setter
|
|
|
|
def usage_zones(self, value):
|
|
|
|
"""
|
|
|
|
Set city object usage zones
|
|
|
|
:param value: [UsageZone]
|
|
|
|
"""
|
|
|
|
self._usage_zones = value
|
|
|
|
|
2020-06-16 15:12:18 -04:00
|
|
|
@property
|
2022-03-08 19:19:52 -05:00
|
|
|
def terrains(self) -> Union[None, List[Surface]]:
|
2020-06-16 15:12:18 -04:00
|
|
|
"""
|
|
|
|
Get city object terrain surfaces
|
|
|
|
:return: [Surface]
|
|
|
|
"""
|
|
|
|
return self._terrains
|
|
|
|
|
|
|
|
@property
|
2021-09-14 13:46:48 -04:00
|
|
|
def attic_heated(self) -> Union[None, int]:
|
2020-06-16 15:12:18 -04:00
|
|
|
"""
|
|
|
|
Get if the city object attic is heated
|
2021-09-14 13:46:48 -04:00
|
|
|
:return: None or int
|
2020-06-16 15:12:18 -04:00
|
|
|
"""
|
|
|
|
return self._attic_heated
|
|
|
|
|
|
|
|
@attic_heated.setter
|
|
|
|
def attic_heated(self, value):
|
|
|
|
"""
|
|
|
|
Set if the city object attic is heated
|
2021-09-14 13:46:48 -04:00
|
|
|
:param value: int
|
2020-06-16 15:12:18 -04:00
|
|
|
"""
|
2021-09-14 13:46:48 -04:00
|
|
|
if value is not None:
|
|
|
|
self._attic_heated = int(value)
|
2020-06-16 15:12:18 -04:00
|
|
|
|
|
|
|
@property
|
2021-09-14 13:46:48 -04:00
|
|
|
def basement_heated(self) -> Union[None, int]:
|
2020-06-16 15:12:18 -04:00
|
|
|
"""
|
|
|
|
Get if the city object basement is heated
|
2021-09-14 13:46:48 -04:00
|
|
|
:return: None or int
|
2020-06-16 15:12:18 -04:00
|
|
|
"""
|
|
|
|
return self._basement_heated
|
|
|
|
|
|
|
|
@basement_heated.setter
|
|
|
|
def basement_heated(self, value):
|
|
|
|
"""
|
|
|
|
Set if the city object basement is heated
|
2021-09-14 13:46:48 -04:00
|
|
|
:param value: int
|
2020-06-16 15:12:18 -04:00
|
|
|
"""
|
2021-09-14 13:46:48 -04:00
|
|
|
if value is not None:
|
|
|
|
self._basement_heated = int(value)
|
2020-06-16 15:12:18 -04:00
|
|
|
|
|
|
|
@property
|
2022-03-08 19:19:52 -05:00
|
|
|
def thermal_zones(self) -> Union[None, List[ThermalZone]]:
|
2020-06-16 15:12:18 -04:00
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Get building thermal zones
|
2020-06-16 15:12:18 -04:00
|
|
|
:return: [ThermalZone]
|
|
|
|
"""
|
|
|
|
return self._thermal_zones
|
|
|
|
|
2022-03-08 19:19:52 -05:00
|
|
|
@thermal_zones.setter
|
|
|
|
def thermal_zones(self, value):
|
|
|
|
"""
|
|
|
|
Set city object thermal zones
|
|
|
|
:param value: [ThermalZone]
|
|
|
|
"""
|
|
|
|
self._thermal_zones = value
|
|
|
|
|
2020-06-16 15:12:18 -04:00
|
|
|
@property
|
|
|
|
def heated_volume(self):
|
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Raises not implemented error
|
2020-06-16 15:12:18 -04:00
|
|
|
"""
|
2020-11-26 09:26:55 -05:00
|
|
|
# ToDo: this need to be calculated based on the basement and attic heated values
|
2021-03-15 11:47:30 -04:00
|
|
|
raise NotImplementedError
|
2020-06-16 15:12:18 -04:00
|
|
|
|
|
|
|
@property
|
|
|
|
def year_of_construction(self):
|
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Get building year of construction
|
2020-06-16 15:12:18 -04:00
|
|
|
:return: int
|
|
|
|
"""
|
|
|
|
return self._year_of_construction
|
|
|
|
|
2021-11-05 10:16:35 -04:00
|
|
|
@year_of_construction.setter
|
|
|
|
def year_of_construction(self, value):
|
|
|
|
"""
|
|
|
|
Set building year of construction
|
|
|
|
:param value: int
|
|
|
|
"""
|
|
|
|
if value is not None:
|
2022-03-08 19:19:52 -05:00
|
|
|
self._year_of_construction = int(value)
|
2021-11-05 10:16:35 -04:00
|
|
|
|
2020-06-16 15:12:18 -04:00
|
|
|
@property
|
2021-09-14 13:46:48 -04:00
|
|
|
def function(self) -> Union[None, str]:
|
2020-06-16 15:12:18 -04:00
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Get building function
|
2021-09-14 13:46:48 -04:00
|
|
|
:return: None or str
|
2020-06-16 15:12:18 -04:00
|
|
|
"""
|
|
|
|
return self._function
|
|
|
|
|
2021-05-27 17:20:06 -04:00
|
|
|
@function.setter
|
|
|
|
def function(self, value):
|
|
|
|
"""
|
|
|
|
Set building function
|
2021-08-30 14:39:24 -04:00
|
|
|
:param value: str
|
2021-05-27 17:20:06 -04:00
|
|
|
"""
|
2021-09-14 13:46:48 -04:00
|
|
|
if value is not None:
|
|
|
|
self._function = str(value)
|
2021-05-27 17:20:06 -04:00
|
|
|
|
2020-06-16 15:12:18 -04:00
|
|
|
@property
|
2021-09-14 13:46:48 -04:00
|
|
|
def average_storey_height(self) -> Union[None, float]:
|
2020-06-16 15:12:18 -04:00
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Get building average storey height in meters
|
2021-09-14 13:46:48 -04:00
|
|
|
:return: None or float
|
2020-06-16 15:12:18 -04:00
|
|
|
"""
|
|
|
|
return self._average_storey_height
|
|
|
|
|
|
|
|
@average_storey_height.setter
|
|
|
|
def average_storey_height(self, value):
|
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Set building average storey height in meters
|
2020-06-16 15:12:18 -04:00
|
|
|
:param value: float
|
|
|
|
"""
|
2021-09-14 13:46:48 -04:00
|
|
|
if value is not None:
|
|
|
|
self._average_storey_height = float(value)
|
2020-06-16 15:12:18 -04:00
|
|
|
|
|
|
|
@property
|
2021-09-14 13:46:48 -04:00
|
|
|
def storeys_above_ground(self) -> Union[None, int]:
|
2020-06-16 15:12:18 -04:00
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Get building storeys number above ground
|
2021-09-14 13:46:48 -04:00
|
|
|
:return: None or int
|
2020-06-16 15:12:18 -04:00
|
|
|
"""
|
|
|
|
return self._storeys_above_ground
|
|
|
|
|
|
|
|
@storeys_above_ground.setter
|
|
|
|
def storeys_above_ground(self, value):
|
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Set building storeys number above ground
|
2020-06-16 15:12:18 -04:00
|
|
|
:param value: int
|
|
|
|
"""
|
2021-09-14 13:46:48 -04:00
|
|
|
if value is not None:
|
|
|
|
self._storeys_above_ground = int(value)
|
2020-06-16 15:12:18 -04:00
|
|
|
|
2020-10-16 06:54:49 -04:00
|
|
|
@property
|
2020-10-30 08:32:38 -04:00
|
|
|
def heating(self) -> dict:
|
2020-10-16 06:54:49 -04:00
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Get heating demand in Wh
|
2020-10-30 08:32:38 -04:00
|
|
|
:return: dict{DataFrame(float)}
|
2020-10-28 13:14:05 -04:00
|
|
|
"""
|
|
|
|
return self._heating
|
|
|
|
|
2020-10-30 08:32:38 -04:00
|
|
|
@heating.setter
|
|
|
|
def heating(self, value):
|
2020-10-16 06:54:49 -04:00
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Set heating demand in Wh
|
2020-10-30 08:32:38 -04:00
|
|
|
:param value: dict{DataFrame(float)}
|
2020-10-16 08:05:17 -04:00
|
|
|
"""
|
2020-10-30 08:32:38 -04:00
|
|
|
self._heating = value
|
2020-10-16 08:05:17 -04:00
|
|
|
|
|
|
|
@property
|
2020-10-30 08:32:38 -04:00
|
|
|
def cooling(self) -> dict:
|
2020-10-16 08:05:17 -04:00
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Get cooling demand in Wh
|
2020-10-30 08:32:38 -04:00
|
|
|
:return: dict{DataFrame(float)}
|
2020-10-28 13:14:05 -04:00
|
|
|
"""
|
2020-10-30 08:32:38 -04:00
|
|
|
return self._cooling
|
2020-10-28 13:14:05 -04:00
|
|
|
|
2020-10-30 08:32:38 -04:00
|
|
|
@cooling.setter
|
|
|
|
def cooling(self, value):
|
2020-10-28 13:14:05 -04:00
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Set cooling demand in Wh
|
2020-10-30 08:32:38 -04:00
|
|
|
:param value: dict{DataFrame(float)}
|
2020-10-16 08:05:17 -04:00
|
|
|
"""
|
2020-10-30 08:32:38 -04:00
|
|
|
self._cooling = value
|
2020-10-16 08:05:17 -04:00
|
|
|
|
2021-03-01 16:42:03 -05:00
|
|
|
@property
|
2021-03-31 14:17:53 -04:00
|
|
|
def eave_height(self):
|
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Get building eave height in meters
|
2021-03-31 14:17:53 -04:00
|
|
|
:return: float
|
|
|
|
"""
|
|
|
|
if self._eave_height is None:
|
|
|
|
self._eave_height = 0
|
|
|
|
for wall in self.walls:
|
2021-06-09 14:23:45 -04:00
|
|
|
self._eave_height = max(self._eave_height, wall.upper_corner[2])
|
2021-03-31 14:17:53 -04:00
|
|
|
return self._eave_height
|
|
|
|
|
|
|
|
@property
|
2021-09-01 09:39:27 -04:00
|
|
|
def storeys(self) -> List[Storey]:
|
2021-03-31 14:17:53 -04:00
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Get building storeys
|
2021-06-23 09:53:33 -04:00
|
|
|
:return: [Storey]
|
2021-03-31 14:17:53 -04:00
|
|
|
"""
|
2021-08-27 17:20:24 -04:00
|
|
|
return self._storeys
|
2021-06-23 09:53:33 -04:00
|
|
|
|
2021-08-27 17:20:24 -04:00
|
|
|
@storeys.setter
|
|
|
|
def storeys(self, value):
|
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Set building storeys
|
2021-08-27 17:20:24 -04:00
|
|
|
:param value: [Storey]
|
|
|
|
"""
|
|
|
|
self._storeys = value
|
2021-03-11 14:31:28 -05:00
|
|
|
|
2021-03-25 09:11:30 -04:00
|
|
|
@property
|
|
|
|
def roof_type(self):
|
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Get roof type for the building flat or pitch
|
|
|
|
:return: str
|
2021-03-25 09:11:30 -04:00
|
|
|
"""
|
|
|
|
if self._roof_type is None:
|
|
|
|
self._roof_type = 'flat'
|
|
|
|
for roof in self.roofs:
|
|
|
|
grads = np.rad2deg(roof.inclination)
|
|
|
|
if 355 > grads > 5:
|
|
|
|
self._roof_type = 'pitch'
|
|
|
|
break
|
|
|
|
return self._roof_type
|
|
|
|
|
2022-03-08 19:19:52 -05:00
|
|
|
@roof_type.setter
|
|
|
|
def roof_type(self, value):
|
|
|
|
"""
|
|
|
|
Set roof type for the building flat or pitch
|
|
|
|
:return: str
|
|
|
|
"""
|
|
|
|
self._roof_type = value
|
|
|
|
|
2021-03-11 14:31:28 -05:00
|
|
|
@property
|
|
|
|
def floor_area(self):
|
|
|
|
"""
|
2021-08-30 14:39:24 -04:00
|
|
|
Get building floor area in square meters
|
2021-03-11 14:31:28 -05:00
|
|
|
:return: float
|
|
|
|
"""
|
|
|
|
if self._floor_area is None:
|
|
|
|
self._floor_area = 0
|
|
|
|
for surface in self.surfaces:
|
|
|
|
if surface.type == 'Ground':
|
|
|
|
self._floor_area += surface.perimeter_polygon.area
|
|
|
|
return self._floor_area
|
2021-05-25 13:34:57 -04:00
|
|
|
|
2021-10-18 16:07:18 -04:00
|
|
|
@property
|
|
|
|
def households(self) -> List[Household]:
|
|
|
|
"""
|
|
|
|
Get the list of households inside the building
|
|
|
|
:return: List[Household]
|
|
|
|
"""
|
|
|
|
return self._households
|
2022-03-08 19:19:52 -05:00
|
|
|
|
|
|
|
@property
|
|
|
|
def is_conditioned(self):
|
|
|
|
"""
|
|
|
|
Get building heated flag
|
|
|
|
:return: Boolean
|
|
|
|
"""
|
|
|
|
if self.thermal_zones is None:
|
|
|
|
return False
|
|
|
|
for thermal_zone in self.thermal_zones:
|
|
|
|
if thermal_zone.hvac_system is not None:
|
|
|
|
return True
|
|
|
|
return False
|