city_retrofit/city_model_structure/building.py

382 lines
9.6 KiB
Python
Raw Normal View History

"""
Building module
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
"""
2021-09-14 13:46:48 -04:00
from typing import List, Union
import numpy as np
from city_model_structure.building_demand.surface import Surface
from city_model_structure.building_demand.thermal_zone import ThermalZone
from city_model_structure.building_demand.thermal_boundary import ThermalBoundary
from city_model_structure.building_demand.usage_zone import UsageZone
from city_model_structure.building_demand.storey import Storey
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
from city_model_structure.attributes.polyhedron import Polyhedron
2021-03-31 14:17:53 -04:00
class Building(CityObject):
"""
Building(CityObject) class
"""
def __init__(self, name, lod, surfaces, year_of_construction, function, city_lower_corner, terrains=None):
super().__init__(name, lod, surfaces, city_lower_corner)
2021-10-18 16:07:18 -04:00
self._households = None
self._basement_heated = None
self._attic_heated = None
self._terrains = terrains
self._year_of_construction = year_of_construction
self._function = function
self._average_storey_height = None
self._storeys_above_ground = None
self._floor_area = None
self._roof_type = None
self._storeys = None
self._geometrical_zones = None
self._thermal_zones = []
self._thermal_boundaries = None
self._usage_zones = []
self._type = 'building'
self._heating = dict()
self._cooling = dict()
2021-03-31 14:17:53 -04:00
self._eave_height = None
self._grounds = []
self._roofs = []
self._walls = []
self._internal_walls = []
for surface_id, surface in enumerate(self.surfaces):
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])
surface.id = surface_id
# todo: consider all type of surfaces, not only these four
if surface.type == 'Ground':
self._grounds.append(surface)
elif surface.type == 'Wall':
self._walls.append(surface)
elif surface.type == 'Roof':
self._roofs.append(surface)
else:
self._internal_walls.append(surface)
@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
@property
2021-09-01 09:39:27 -04:00
def grounds(self) -> List[Surface]:
"""
Get building ground surfaces
2021-09-01 09:39:27 -04:00
:return: [Surface]
"""
return self._grounds
@property
def is_heated(self):
"""
Get building heated flag
:return: Boolean
"""
for usage_zone in self.usage_zones:
if usage_zone.is_heated:
return usage_zone.is_heated
return False
@property
def is_cooled(self):
"""
Get building cooled flag
:return: Boolean
"""
for usage_zone in self.usage_zones:
if usage_zone.is_cooled:
return usage_zone.is_cooled
return False
@property
2021-09-01 09:39:27 -04:00
def roofs(self) -> List[Surface]:
"""
Get building roof surfaces
:return: [Surface]
"""
return self._roofs
@property
2021-09-01 09:39:27 -04:00
def walls(self) -> List[Surface]:
"""
Get building wall surfaces
:return: [Surface]
"""
return self._walls
@property
def usage_zones(self) -> List[UsageZone]:
"""
Get city object usage zones
:return: [UsageZone]
"""
if len(self._usage_zones) == 0:
for thermal_zone in self.thermal_zones:
self._usage_zones.extend(thermal_zone.usage_zones)
return self._usage_zones
@property
def terrains(self) -> List[Surface]:
"""
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]:
"""
Get if the city object attic is heated
2021-09-14 13:46:48 -04:00
:return: None or int
"""
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
"""
2021-09-14 13:46:48 -04:00
if value is not None:
self._attic_heated = int(value)
@property
2021-09-14 13:46:48 -04:00
def basement_heated(self) -> Union[None, int]:
"""
Get if the city object basement is heated
2021-09-14 13:46:48 -04:00
:return: None or int
"""
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
"""
2021-09-14 13:46:48 -04:00
if value is not None:
self._basement_heated = int(value)
@property
def name(self):
"""
Get building name
:return: str
"""
return self._name
@property
def thermal_zones(self) -> List[ThermalZone]:
"""
Get building thermal zones
:return: [ThermalZone]
"""
if len(self._thermal_zones) == 0:
if self.storeys is None:
return []
for storey in self.storeys:
self._thermal_zones.append(storey.thermal_zone)
return self._thermal_zones
@property
def heated_volume(self):
"""
Raises not implemented error
"""
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
@property
def year_of_construction(self):
"""
Get building year of construction
:return: int
"""
return self._year_of_construction
@year_of_construction.setter
def year_of_construction(self, value):
"""
Set building year of construction
:param value: int
"""
if value is not None:
self._year_of_construction = value
@property
2021-09-14 13:46:48 -04:00
def function(self) -> Union[None, str]:
"""
Get building function
2021-09-14 13:46:48 -04:00
:return: None or str
"""
return self._function
@function.setter
def function(self, value):
"""
Set building function
:param value: str
"""
2021-09-14 13:46:48 -04:00
if value is not None:
self._function = str(value)
@property
2021-09-14 13:46:48 -04:00
def average_storey_height(self) -> Union[None, float]:
"""
Get building average storey height in meters
2021-09-14 13:46:48 -04:00
:return: None or float
"""
return self._average_storey_height
@average_storey_height.setter
def average_storey_height(self, value):
"""
Set building average storey height in meters
:param value: float
"""
2021-09-14 13:46:48 -04:00
if value is not None:
self._average_storey_height = float(value)
@property
2021-09-14 13:46:48 -04:00
def storeys_above_ground(self) -> Union[None, int]:
"""
Get building storeys number above ground
2021-09-14 13:46:48 -04:00
:return: None or int
"""
return self._storeys_above_ground
@storeys_above_ground.setter
def storeys_above_ground(self, value):
"""
Set building storeys number above ground
:param value: int
"""
2021-09-14 13:46:48 -04:00
if value is not None:
self._storeys_above_ground = int(value)
@property
def heating(self) -> dict:
"""
Get heating demand in Wh
:return: dict{DataFrame(float)}
"""
return self._heating
@heating.setter
def heating(self, value):
"""
Set heating demand in Wh
:param value: dict{DataFrame(float)}
"""
self._heating = value
@property
def cooling(self) -> dict:
"""
Get cooling demand in Wh
:return: dict{DataFrame(float)}
"""
return self._cooling
@cooling.setter
def cooling(self, value):
"""
Set cooling demand in Wh
:param value: dict{DataFrame(float)}
"""
self._cooling = value
@property
2021-03-31 14:17:53 -04:00
def eave_height(self):
"""
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:
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
"""
Get building storeys
:return: [Storey]
2021-03-31 14:17:53 -04:00
"""
return self._storeys
@storeys.setter
def storeys(self, value):
"""
Set building storeys
:param value: [Storey]
"""
self._storeys = value
@property
def roof_type(self):
"""
Get roof type for the building flat or pitch
:return: str
"""
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
@property
def floor_area(self):
"""
Get building floor area in square meters
: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
@property
def thermal_boundaries(self) -> List[ThermalBoundary]:
"""
Get all thermal boundaries associated to the building's thermal zones
:return: [ThermalBoundary]
"""
if self._thermal_boundaries is None:
self._thermal_boundaries = []
for thermal_zone in self.thermal_zones:
_thermal_boundary_duplicated = False
for thermal_boundary in thermal_zone.thermal_boundaries:
if len(thermal_boundary.thermal_zones) > 1:
if thermal_zone != thermal_boundary.thermal_zones[1]:
self._thermal_boundaries.append(thermal_boundary)
else:
self._thermal_boundaries.append(thermal_boundary)
return self._thermal_boundaries
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