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
|
2022-04-08 09:35:33 -04:00
|
|
|
Copyright © 2022 Concordia CERC group
|
|
|
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
|
|
|
Code 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
|
|
|
|
2023-05-17 17:10:30 -04:00
|
|
|
import logging
|
2023-05-26 18:21:35 -04:00
|
|
|
from typing import List, Union, TypeVar
|
2023-05-17 17:10:30 -04:00
|
|
|
|
2021-08-26 11:00:59 -04:00
|
|
|
import numpy as np
|
2023-01-24 10:51:50 -05:00
|
|
|
import hub.helpers.constants as cte
|
2023-05-17 17:10:30 -04:00
|
|
|
from hub.city_model_structure.attributes.polyhedron import Polyhedron
|
2023-01-24 10:51:50 -05:00
|
|
|
from hub.city_model_structure.building_demand.household import Household
|
|
|
|
from hub.city_model_structure.building_demand.internal_zone import InternalZone
|
2023-06-08 14:18:29 -04:00
|
|
|
from hub.city_model_structure.building_demand.thermal_zone import ThermalZone
|
2023-05-17 17:10:30 -04:00
|
|
|
from hub.city_model_structure.building_demand.surface import Surface
|
|
|
|
from hub.city_model_structure.city_object import CityObject
|
2023-05-15 11:03:54 -04:00
|
|
|
from hub.city_model_structure.energy_systems.energy_system import EnergySystem
|
2023-05-17 17:10:30 -04:00
|
|
|
from hub.helpers.peak_loads import PeakLoads
|
2021-03-31 14:17:53 -04:00
|
|
|
|
2023-05-26 18:21:35 -04:00
|
|
|
City = TypeVar('City')
|
|
|
|
|
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
|
|
|
"""
|
2023-05-26 18:21:35 -04:00
|
|
|
def __init__(self, name, surfaces, year_of_construction, function, terrains=None, city=None):
|
2022-11-28 13:44:02 -05:00
|
|
|
super().__init__(name, surfaces)
|
2023-05-26 18:21:35 -04:00
|
|
|
self._city = city
|
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
|
2022-03-08 20:08:03 -05:00
|
|
|
self._internal_zones = None
|
2023-07-31 17:01:35 -04:00
|
|
|
self._thermal_zones_from_internal_zones = None
|
2022-03-17 18:49:44 -04:00
|
|
|
self._shell = None
|
2023-06-06 17:44:07 -04:00
|
|
|
self._aliases = []
|
2020-06-16 15:12:18 -04:00
|
|
|
self._type = 'building'
|
2023-05-30 17:13:49 -04:00
|
|
|
self._cold_water_temperature = {}
|
2023-06-05 11:53:47 -04:00
|
|
|
self._heating_demand = {}
|
|
|
|
self._cooling_demand = {}
|
2023-05-30 17:13:49 -04:00
|
|
|
self._lighting_electrical_demand = {}
|
|
|
|
self._appliances_electrical_demand = {}
|
|
|
|
self._domestic_hot_water_heat_demand = {}
|
|
|
|
self._heating_consumption = {}
|
|
|
|
self._cooling_consumption = {}
|
|
|
|
self._domestic_hot_water_consumption = {}
|
|
|
|
self._distribution_systems_electrical_consumption = {}
|
|
|
|
self._onsite_electrical_production = {}
|
2021-03-31 14:17:53 -04:00
|
|
|
self._eave_height = None
|
2023-04-28 12:24:08 -04:00
|
|
|
self._energy_systems = None
|
2023-04-28 16:31:53 -04:00
|
|
|
self._systems_archetype_name = None
|
2021-06-09 14:23:45 -04:00
|
|
|
self._grounds = []
|
|
|
|
self._roofs = []
|
|
|
|
self._walls = []
|
|
|
|
self._internal_walls = []
|
2023-02-09 05:29:53 -05:00
|
|
|
self._ground_walls = []
|
|
|
|
self._attic_floors = []
|
|
|
|
self._interior_slabs = []
|
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])
|
2023-09-28 23:55:36 -04:00
|
|
|
self._max_x = max(self._max_x, surface.upper_corner[0])
|
|
|
|
self._max_y = max(self._max_y, surface.upper_corner[1])
|
|
|
|
self._max_z = max(self._max_z, surface.upper_corner[2])
|
2021-04-14 10:41:51 -04:00
|
|
|
surface.id = surface_id
|
2022-11-09 14:22:26 -05:00
|
|
|
if surface.type == cte.GROUND:
|
2021-03-25 09:11:30 -04:00
|
|
|
self._grounds.append(surface)
|
2022-11-09 14:22:26 -05:00
|
|
|
elif surface.type == cte.WALL:
|
2021-03-25 09:11:30 -04:00
|
|
|
self._walls.append(surface)
|
2022-11-09 14:22:26 -05:00
|
|
|
elif surface.type == cte.ROOF:
|
2021-03-25 09:11:30 -04:00
|
|
|
self._roofs.append(surface)
|
2023-02-09 05:29:53 -05:00
|
|
|
elif surface.type == cte.INTERIOR_WALL:
|
2021-04-06 13:48:18 -04:00
|
|
|
self._internal_walls.append(surface)
|
2023-02-09 05:29:53 -05:00
|
|
|
elif surface.type == cte.GROUND_WALL:
|
|
|
|
self._ground_walls.append(surface)
|
|
|
|
elif surface.type == cte.ATTIC_FLOOR:
|
|
|
|
self._attic_floors.append(surface)
|
|
|
|
elif surface.type == cte.INTERIOR_SLAB:
|
|
|
|
self._interior_slabs.append(surface)
|
|
|
|
else:
|
2023-10-05 05:30:20 -04:00
|
|
|
logging.error('Building %s [%s] has an unexpected surface type %s.', self.name, self.aliases, surface.type)
|
2024-02-07 18:54:09 -05:00
|
|
|
self._heating_consumption_disaggregated = {}
|
2021-03-25 09:11:30 -04:00
|
|
|
|
2022-02-16 15:08:05 -05:00
|
|
|
@property
|
2022-03-08 20:08:03 -05:00
|
|
|
def shell(self) -> Polyhedron:
|
|
|
|
"""
|
2022-11-09 14:22:26 -05:00
|
|
|
Get building's external polyhedron
|
2022-03-08 20:08:03 -05:00
|
|
|
:return: [Polyhedron]
|
|
|
|
"""
|
2022-11-09 14:22:26 -05:00
|
|
|
polygons = []
|
|
|
|
for surface in self.surfaces:
|
|
|
|
if surface.type is not cte.INTERIOR_WALL:
|
|
|
|
polygons.append(surface.solid_polygon)
|
|
|
|
if surface.holes_polygons is not None:
|
|
|
|
for hole in surface.holes_polygons:
|
|
|
|
polygons.append(hole)
|
2022-03-08 20:08:03 -05:00
|
|
|
if self._shell is None:
|
2022-11-09 14:22:26 -05:00
|
|
|
self._shell = Polyhedron(polygons)
|
2022-03-08 20:08:03 -05:00
|
|
|
return self._shell
|
|
|
|
|
|
|
|
@property
|
|
|
|
def internal_zones(self) -> List[InternalZone]:
|
|
|
|
"""
|
|
|
|
Get building internal zones
|
|
|
|
For Lod up to 3, there is only one internal zone which corresponds to the building shell.
|
|
|
|
In LoD 4 there can be more than one. In this case the definition of surfaces and floor area must be redefined.
|
|
|
|
:return: [InternalZone]
|
|
|
|
"""
|
|
|
|
if self._internal_zones is None:
|
2023-08-08 11:20:44 -04:00
|
|
|
self._internal_zones = [InternalZone(self.surfaces, self.floor_area, self.volume)]
|
2022-03-08 20:08:03 -05:00
|
|
|
return self._internal_zones
|
2022-02-16 15:08:05 -05:00
|
|
|
|
2023-06-08 14:18:29 -04:00
|
|
|
@property
|
2023-07-31 17:01:35 -04:00
|
|
|
def thermal_zones_from_internal_zones(self) -> Union[None, List[ThermalZone]]:
|
2023-06-08 14:18:29 -04:00
|
|
|
"""
|
|
|
|
Get building thermal zones
|
|
|
|
:return: [ThermalZone]
|
|
|
|
"""
|
2023-07-31 17:01:35 -04:00
|
|
|
if self._thermal_zones_from_internal_zones is None:
|
|
|
|
self._thermal_zones_from_internal_zones = []
|
2023-06-08 14:18:29 -04:00
|
|
|
for internal_zone in self.internal_zones:
|
2023-07-31 17:01:35 -04:00
|
|
|
if internal_zone.thermal_zones_from_internal_zones is None:
|
|
|
|
self._thermal_zones_from_internal_zones = None
|
|
|
|
return self._thermal_zones_from_internal_zones
|
|
|
|
self._thermal_zones_from_internal_zones.append(internal_zone.thermal_zones_from_internal_zones[0])
|
|
|
|
return self._thermal_zones_from_internal_zones
|
2023-06-08 14:18:29 -04:00
|
|
|
|
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
|
|
|
|
2022-11-09 14:22:26 -05:00
|
|
|
@property
|
|
|
|
def internal_walls(self) -> List[Surface]:
|
|
|
|
"""
|
|
|
|
Get building internal wall surfaces
|
|
|
|
:return: [Surface]
|
|
|
|
"""
|
|
|
|
return self._internal_walls
|
|
|
|
|
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
|
2022-11-25 14:45:03 -05:00
|
|
|
0: no attic in the building
|
|
|
|
1: attic exists but is not heated
|
|
|
|
2: attic exists and 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
|
2022-11-25 14:45:03 -05:00
|
|
|
0: no attic in the building
|
|
|
|
1: attic exists but is not heated
|
|
|
|
2: attic exists and 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
|
2022-11-25 14:45:03 -05:00
|
|
|
0: no basement in the building
|
|
|
|
1: basement exists but is not heated
|
|
|
|
2: basement exists and 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
|
2022-11-25 14:45:03 -05:00
|
|
|
0: no basement in the building
|
|
|
|
1: basement exists but is not heated
|
|
|
|
2: basement exists and 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
|
|
|
|
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
|
|
|
"""
|
2023-08-03 13:49:57 -04:00
|
|
|
if len(self.internal_zones) > 1:
|
|
|
|
self._average_storey_height = 0
|
|
|
|
for internal_zone in self.internal_zones:
|
|
|
|
self._average_storey_height += internal_zone.mean_height / len(self.internal_zones)
|
|
|
|
else:
|
2023-08-03 17:00:59 -04:00
|
|
|
if self.internal_zones[0].thermal_archetype is None:
|
|
|
|
self._average_storey_height = None
|
|
|
|
else:
|
|
|
|
self._average_storey_height = self.internal_zones[0].thermal_archetype.average_storey_height
|
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
|
|
|
"""
|
2023-05-29 14:13:57 -04:00
|
|
|
if self._storeys_above_ground is None:
|
|
|
|
if self.eave_height is not None and self.average_storey_height is not None:
|
|
|
|
self._storeys_above_ground = int(self.eave_height / self.average_storey_height)
|
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
|
|
|
|
2023-03-20 14:15:57 -04:00
|
|
|
@property
|
|
|
|
def cold_water_temperature(self) -> {float}:
|
|
|
|
"""
|
|
|
|
Get cold water temperature in degrees Celsius
|
2023-08-07 12:32:33 -04:00
|
|
|
:return: dict{[float]}
|
2023-03-20 14:15:57 -04:00
|
|
|
"""
|
|
|
|
return self._cold_water_temperature
|
|
|
|
|
|
|
|
@cold_water_temperature.setter
|
|
|
|
def cold_water_temperature(self, value):
|
|
|
|
"""
|
|
|
|
Set cold water temperature in degrees Celsius
|
2023-08-07 12:32:33 -04:00
|
|
|
:param value: dict{[float]}
|
2023-03-20 14:15:57 -04:00
|
|
|
"""
|
|
|
|
self._cold_water_temperature = value
|
|
|
|
|
2020-10-16 06:54:49 -04:00
|
|
|
@property
|
2023-06-05 11:53:47 -04:00
|
|
|
def heating_demand(self) -> dict:
|
2020-10-16 06:54:49 -04:00
|
|
|
"""
|
2023-08-08 10:19:35 -04:00
|
|
|
Get heating demand in J
|
2023-08-07 12:32:33 -04:00
|
|
|
:return: dict{[float]}
|
2020-10-28 13:14:05 -04:00
|
|
|
"""
|
2023-06-05 11:53:47 -04:00
|
|
|
return self._heating_demand
|
2020-10-28 13:14:05 -04:00
|
|
|
|
2023-06-05 11:53:47 -04:00
|
|
|
@heating_demand.setter
|
|
|
|
def heating_demand(self, value):
|
2020-10-16 06:54:49 -04:00
|
|
|
"""
|
2023-08-08 10:19:35 -04:00
|
|
|
Set heating demand in J
|
2023-08-07 12:32:33 -04:00
|
|
|
:param value: dict{[float]}
|
2020-10-16 08:05:17 -04:00
|
|
|
"""
|
2023-06-07 12:55:03 -04:00
|
|
|
self._heating_demand = value
|
2020-10-16 08:05:17 -04:00
|
|
|
|
|
|
|
@property
|
2023-06-05 11:53:47 -04:00
|
|
|
def cooling_demand(self) -> dict:
|
2020-10-16 08:05:17 -04:00
|
|
|
"""
|
2023-08-08 10:19:35 -04:00
|
|
|
Get cooling demand in J
|
2023-08-07 12:32:33 -04:00
|
|
|
:return: dict{[float]}
|
2020-10-28 13:14:05 -04:00
|
|
|
"""
|
2023-06-05 11:53:47 -04:00
|
|
|
return self._cooling_demand
|
2020-10-28 13:14:05 -04:00
|
|
|
|
2023-06-05 11:53:47 -04:00
|
|
|
@cooling_demand.setter
|
|
|
|
def cooling_demand(self, value):
|
2020-10-28 13:14:05 -04:00
|
|
|
"""
|
2023-08-08 10:19:35 -04:00
|
|
|
Set cooling demand in J
|
2023-08-07 12:32:33 -04:00
|
|
|
:param value: dict{[float]}
|
2020-10-16 08:05:17 -04:00
|
|
|
"""
|
2023-06-07 12:55:03 -04:00
|
|
|
self._cooling_demand = value
|
2020-10-16 08:05:17 -04:00
|
|
|
|
2022-12-06 14:18:36 -05:00
|
|
|
@property
|
|
|
|
def lighting_electrical_demand(self) -> dict:
|
|
|
|
"""
|
2023-08-08 10:19:35 -04:00
|
|
|
Get lighting electrical demand in J
|
2023-08-07 12:32:33 -04:00
|
|
|
:return: dict{[float]}
|
2022-12-06 14:18:36 -05:00
|
|
|
"""
|
|
|
|
return self._lighting_electrical_demand
|
|
|
|
|
|
|
|
@lighting_electrical_demand.setter
|
|
|
|
def lighting_electrical_demand(self, value):
|
|
|
|
"""
|
2023-08-08 10:19:35 -04:00
|
|
|
Set lighting electrical demand in J
|
2023-08-07 12:32:33 -04:00
|
|
|
:param value: dict{[float]}
|
2022-12-06 14:18:36 -05:00
|
|
|
"""
|
|
|
|
self._lighting_electrical_demand = value
|
|
|
|
|
|
|
|
@property
|
|
|
|
def appliances_electrical_demand(self) -> dict:
|
|
|
|
"""
|
2023-08-08 10:19:35 -04:00
|
|
|
Get appliances electrical demand in J
|
2023-08-07 12:32:33 -04:00
|
|
|
:return: dict{[float]}
|
2022-12-06 14:18:36 -05:00
|
|
|
"""
|
|
|
|
return self._appliances_electrical_demand
|
|
|
|
|
|
|
|
@appliances_electrical_demand.setter
|
|
|
|
def appliances_electrical_demand(self, value):
|
|
|
|
"""
|
2023-08-08 10:19:35 -04:00
|
|
|
Set appliances electrical demand in J
|
2023-08-07 12:32:33 -04:00
|
|
|
:param value: dict{[float]}
|
2022-12-06 14:18:36 -05:00
|
|
|
"""
|
|
|
|
self._appliances_electrical_demand = value
|
|
|
|
|
|
|
|
@property
|
|
|
|
def domestic_hot_water_heat_demand(self) -> dict:
|
|
|
|
"""
|
2023-08-08 10:19:35 -04:00
|
|
|
Get domestic hot water heat demand in J
|
2023-08-07 12:32:33 -04:00
|
|
|
:return: dict{[float]}
|
2022-12-06 14:18:36 -05:00
|
|
|
"""
|
|
|
|
return self._domestic_hot_water_heat_demand
|
|
|
|
|
|
|
|
@domestic_hot_water_heat_demand.setter
|
|
|
|
def domestic_hot_water_heat_demand(self, value):
|
|
|
|
"""
|
2023-08-08 10:19:35 -04:00
|
|
|
Set domestic hot water heat demand in J
|
2023-08-07 12:32:33 -04:00
|
|
|
:param value: dict{[float]}
|
2022-12-06 14:18:36 -05:00
|
|
|
"""
|
|
|
|
self._domestic_hot_water_heat_demand = value
|
|
|
|
|
2023-08-01 16:41:37 -04:00
|
|
|
@property
|
|
|
|
def lighting_peak_load(self) -> Union[None, dict]:
|
|
|
|
"""
|
|
|
|
Get lighting peak load in W
|
|
|
|
:return: dict{[float]}
|
|
|
|
"""
|
|
|
|
results = {}
|
|
|
|
peak_lighting = 0
|
2023-08-03 13:49:57 -04:00
|
|
|
peak = 0
|
2023-08-02 14:46:17 -04:00
|
|
|
for thermal_zone in self.thermal_zones_from_internal_zones:
|
2023-08-01 16:41:37 -04:00
|
|
|
lighting = thermal_zone.lighting
|
|
|
|
for schedule in lighting.schedules:
|
|
|
|
peak = max(schedule.values) * lighting.density * thermal_zone.total_floor_area
|
|
|
|
if peak > peak_lighting:
|
|
|
|
peak_lighting = peak
|
|
|
|
results[cte.MONTH] = [peak for _ in range(0, 12)]
|
|
|
|
results[cte.YEAR] = [peak]
|
|
|
|
return results
|
|
|
|
|
|
|
|
@property
|
|
|
|
def appliances_peak_load(self) -> Union[None, dict]:
|
|
|
|
"""
|
|
|
|
Get appliances peak load in W
|
|
|
|
:return: dict{[float]}
|
|
|
|
"""
|
|
|
|
results = {}
|
|
|
|
peak_appliances = 0
|
2023-08-03 13:49:57 -04:00
|
|
|
peak = 0
|
2023-08-02 14:46:17 -04:00
|
|
|
for thermal_zone in self.thermal_zones_from_internal_zones:
|
2023-08-01 16:41:37 -04:00
|
|
|
appliances = thermal_zone.appliances
|
|
|
|
for schedule in appliances.schedules:
|
|
|
|
peak = max(schedule.values) * appliances.density * thermal_zone.total_floor_area
|
|
|
|
if peak > peak_appliances:
|
|
|
|
peak_appliances = peak
|
|
|
|
results[cte.MONTH] = [peak for _ in range(0, 12)]
|
|
|
|
results[cte.YEAR] = [peak]
|
|
|
|
return results
|
|
|
|
|
2023-04-18 12:21:00 -04:00
|
|
|
@property
|
2023-05-12 09:27:29 -04:00
|
|
|
def heating_peak_load(self) -> Union[None, dict]:
|
2023-04-18 12:21:00 -04:00
|
|
|
"""
|
|
|
|
Get heating peak load in W
|
2023-08-07 12:32:33 -04:00
|
|
|
:return: dict{[float]}
|
2023-04-18 12:21:00 -04:00
|
|
|
"""
|
2023-04-27 13:31:00 -04:00
|
|
|
results = {}
|
2023-06-05 11:53:47 -04:00
|
|
|
if cte.HOUR in self.heating_demand:
|
2023-08-09 16:12:10 -04:00
|
|
|
monthly_values = PeakLoads().peak_loads_from_hourly(self.heating_demand[cte.HOUR])
|
2023-05-01 10:37:51 -04:00
|
|
|
else:
|
2023-05-12 09:27:29 -04:00
|
|
|
monthly_values = PeakLoads(self).heating_peak_loads_from_methodology
|
|
|
|
if monthly_values is None:
|
|
|
|
return None
|
2024-04-15 01:13:53 -04:00
|
|
|
results[cte.MONTH] = monthly_values
|
2023-06-02 15:05:29 -04:00
|
|
|
results[cte.YEAR] = [max(monthly_values)]
|
2023-04-27 13:31:00 -04:00
|
|
|
return results
|
2023-04-18 12:21:00 -04:00
|
|
|
|
|
|
|
@property
|
2023-05-12 09:27:29 -04:00
|
|
|
def cooling_peak_load(self) -> Union[None, dict]:
|
2023-04-18 12:21:00 -04:00
|
|
|
"""
|
|
|
|
Get cooling peak load in W
|
2023-08-07 12:32:33 -04:00
|
|
|
:return: dict{[float]}
|
2023-04-18 12:21:00 -04:00
|
|
|
"""
|
2023-04-27 13:31:00 -04:00
|
|
|
results = {}
|
2023-06-05 11:53:47 -04:00
|
|
|
if cte.HOUR in self.cooling_demand:
|
2023-08-07 12:32:33 -04:00
|
|
|
monthly_values = PeakLoads().peak_loads_from_hourly(self.cooling_demand[cte.HOUR])
|
2023-05-01 10:37:51 -04:00
|
|
|
else:
|
2023-05-12 09:27:29 -04:00
|
|
|
monthly_values = PeakLoads(self).cooling_peak_loads_from_methodology
|
|
|
|
if monthly_values is None:
|
|
|
|
return None
|
2023-08-08 11:20:44 -04:00
|
|
|
results[cte.MONTH] = [x * cte.WATTS_HOUR_TO_JULES for x in monthly_values]
|
2023-06-02 15:05:29 -04:00
|
|
|
results[cte.YEAR] = [max(monthly_values)]
|
2023-05-01 10:37:51 -04:00
|
|
|
return results
|
2023-04-18 12:21:00 -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:
|
2023-06-02 15:05:29 -04:00
|
|
|
self._eave_height = max(self._eave_height, wall.upper_corner[2]) - self.simplified_polyhedron.min_z
|
2021-03-31 14:17:53 -04:00
|
|
|
return self._eave_height
|
|
|
|
|
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
|
|
|
|
"""
|
2022-03-17 18:49:44 -04:00
|
|
|
if self.internal_zones is None:
|
2022-03-08 19:19:52 -05:00
|
|
|
return False
|
2022-03-17 18:49:44 -04:00
|
|
|
for internal_zone in self.internal_zones:
|
2022-12-06 15:28:59 -05:00
|
|
|
if internal_zone.usages is not None:
|
2023-01-26 08:53:00 -05:00
|
|
|
for usage in internal_zone.usages:
|
|
|
|
if usage.thermal_control is not None:
|
2022-03-17 18:49:44 -04:00
|
|
|
return True
|
2022-03-08 19:19:52 -05:00
|
|
|
return False
|
2022-04-11 14:53:22 -04:00
|
|
|
|
|
|
|
@property
|
2023-05-26 18:21:35 -04:00
|
|
|
def aliases(self):
|
2022-05-02 17:39:37 -04:00
|
|
|
"""
|
2023-01-31 13:11:39 -05:00
|
|
|
Get the alias name for the building
|
2022-05-02 17:39:37 -04:00
|
|
|
:return: str
|
|
|
|
"""
|
2023-05-26 18:21:35 -04:00
|
|
|
return self._aliases
|
|
|
|
|
|
|
|
def add_alias(self, value):
|
|
|
|
"""
|
|
|
|
Add a new alias for the building
|
|
|
|
"""
|
2023-06-06 17:44:07 -04:00
|
|
|
self._aliases.append(value)
|
2023-05-26 18:21:35 -04:00
|
|
|
if self.city is not None:
|
|
|
|
self.city.add_building_alias(self, value)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def city(self) -> City:
|
|
|
|
"""
|
|
|
|
Get the city containing the building
|
|
|
|
:return: City
|
|
|
|
"""
|
|
|
|
return self._city
|
2022-04-11 14:53:22 -04:00
|
|
|
|
2023-05-26 18:21:35 -04:00
|
|
|
@city.setter
|
|
|
|
def city(self, value):
|
2022-05-02 17:39:37 -04:00
|
|
|
"""
|
2023-05-26 18:21:35 -04:00
|
|
|
Set the city containing the building
|
2022-05-02 17:39:37 -04:00
|
|
|
"""
|
2023-05-26 18:21:35 -04:00
|
|
|
self._city = value
|
2023-02-07 06:16:08 -05:00
|
|
|
|
|
|
|
@property
|
|
|
|
def usages_percentage(self):
|
|
|
|
"""
|
|
|
|
Get the usages and percentages for the building
|
|
|
|
"""
|
|
|
|
_usage = ''
|
|
|
|
for internal_zone in self.internal_zones:
|
2023-05-16 18:06:22 -04:00
|
|
|
if internal_zone.usages is None:
|
|
|
|
continue
|
2023-02-07 06:16:08 -05:00
|
|
|
for usage in internal_zone.usages:
|
|
|
|
_usage = f'{_usage}{usage.name}_{usage.percentage} '
|
|
|
|
return _usage.rstrip()
|
2023-04-28 12:24:08 -04:00
|
|
|
|
|
|
|
@property
|
2023-05-15 11:03:54 -04:00
|
|
|
def energy_systems(self) -> Union[None, List[EnergySystem]]:
|
2023-04-28 12:24:08 -04:00
|
|
|
"""
|
|
|
|
Get list of energy systems installed to cover the building demands
|
2023-05-15 11:03:54 -04:00
|
|
|
:return: [EnergySystem]
|
2023-04-28 12:24:08 -04:00
|
|
|
"""
|
|
|
|
return self._energy_systems
|
|
|
|
|
|
|
|
@energy_systems.setter
|
|
|
|
def energy_systems(self, value):
|
|
|
|
"""
|
|
|
|
Set list of energy systems installed to cover the building demands
|
2023-05-15 11:03:54 -04:00
|
|
|
:param value: [EnergySystem]
|
2023-04-28 12:24:08 -04:00
|
|
|
"""
|
|
|
|
self._energy_systems = value
|
2023-04-28 16:31:53 -04:00
|
|
|
|
|
|
|
@property
|
2023-05-02 12:35:42 -04:00
|
|
|
def energy_systems_archetype_name(self):
|
2023-04-28 16:31:53 -04:00
|
|
|
"""
|
2023-05-02 12:35:42 -04:00
|
|
|
Get energy systems archetype name
|
2023-04-28 16:31:53 -04:00
|
|
|
:return: str
|
|
|
|
"""
|
|
|
|
return self._systems_archetype_name
|
|
|
|
|
2023-05-02 12:35:42 -04:00
|
|
|
@energy_systems_archetype_name.setter
|
|
|
|
def energy_systems_archetype_name(self, value):
|
2023-04-28 16:31:53 -04:00
|
|
|
"""
|
2023-05-02 12:35:42 -04:00
|
|
|
Set energy systems archetype name
|
2023-04-28 16:31:53 -04:00
|
|
|
:param value: str
|
|
|
|
"""
|
|
|
|
self._systems_archetype_name = value
|
2023-05-04 10:39:23 -04:00
|
|
|
|
|
|
|
@property
|
|
|
|
def heating_consumption(self):
|
|
|
|
"""
|
2023-08-08 10:19:35 -04:00
|
|
|
Get energy consumption for heating according to the heating system installed in J
|
2023-05-04 10:39:23 -04:00
|
|
|
return: dict
|
|
|
|
"""
|
2023-05-15 11:03:54 -04:00
|
|
|
if len(self._heating_consumption) == 0:
|
2023-06-05 11:53:47 -04:00
|
|
|
for heating_demand_key in self.heating_demand:
|
2023-08-07 12:32:33 -04:00
|
|
|
demand = self.heating_demand[heating_demand_key]
|
2023-05-15 11:03:54 -04:00
|
|
|
consumption_type = cte.HEATING
|
|
|
|
final_energy_consumed = self._calculate_consumption(consumption_type, demand)
|
2023-05-29 15:26:21 -04:00
|
|
|
if final_energy_consumed is None:
|
|
|
|
continue
|
2023-05-15 11:03:54 -04:00
|
|
|
self._heating_consumption[heating_demand_key] = final_energy_consumed
|
2023-05-04 10:39:23 -04:00
|
|
|
return self._heating_consumption
|
|
|
|
|
|
|
|
@property
|
|
|
|
def cooling_consumption(self):
|
|
|
|
"""
|
2023-08-08 10:19:35 -04:00
|
|
|
Get energy consumption for cooling according to the cooling system installed in J
|
2023-05-04 10:39:23 -04:00
|
|
|
return: dict
|
|
|
|
"""
|
2023-05-15 11:03:54 -04:00
|
|
|
if len(self._cooling_consumption) == 0:
|
2023-06-05 11:53:47 -04:00
|
|
|
for cooling_demand_key in self.cooling_demand:
|
2023-08-07 12:32:33 -04:00
|
|
|
demand = self.cooling_demand[cooling_demand_key]
|
2023-05-15 11:03:54 -04:00
|
|
|
consumption_type = cte.COOLING
|
|
|
|
final_energy_consumed = self._calculate_consumption(consumption_type, demand)
|
2023-05-29 15:26:21 -04:00
|
|
|
if final_energy_consumed is None:
|
|
|
|
continue
|
2023-05-15 11:03:54 -04:00
|
|
|
self._cooling_consumption[cooling_demand_key] = final_energy_consumed
|
2023-05-04 10:39:23 -04:00
|
|
|
return self._cooling_consumption
|
|
|
|
|
|
|
|
@property
|
|
|
|
def domestic_hot_water_consumption(self):
|
|
|
|
"""
|
2023-08-08 10:19:35 -04:00
|
|
|
Get energy consumption for domestic according to the domestic hot water system installed in J
|
2023-05-04 10:39:23 -04:00
|
|
|
return: dict
|
|
|
|
"""
|
2023-05-15 11:03:54 -04:00
|
|
|
if len(self._domestic_hot_water_consumption) == 0:
|
|
|
|
for domestic_hot_water_demand_key in self.domestic_hot_water_heat_demand:
|
2023-08-07 12:32:33 -04:00
|
|
|
demand = self.domestic_hot_water_heat_demand[domestic_hot_water_demand_key]
|
2023-05-15 11:03:54 -04:00
|
|
|
consumption_type = cte.DOMESTIC_HOT_WATER
|
|
|
|
final_energy_consumed = self._calculate_consumption(consumption_type, demand)
|
2023-05-29 15:26:21 -04:00
|
|
|
if final_energy_consumed is None:
|
|
|
|
continue
|
2023-05-15 11:03:54 -04:00
|
|
|
self._domestic_hot_water_consumption[domestic_hot_water_demand_key] = final_energy_consumed
|
2023-05-04 10:39:23 -04:00
|
|
|
return self._domestic_hot_water_consumption
|
|
|
|
|
2023-05-19 15:04:02 -04:00
|
|
|
def _calculate_working_hours(self):
|
|
|
|
_working_hours = {}
|
|
|
|
for internal_zone in self.internal_zones:
|
2023-07-31 17:01:35 -04:00
|
|
|
for thermal_zone in internal_zone.thermal_zones_from_internal_zones:
|
2023-05-19 15:04:02 -04:00
|
|
|
_working_hours_per_thermal_zone = {}
|
|
|
|
for schedule in thermal_zone.thermal_control.hvac_availability_schedules:
|
|
|
|
_working_hours_per_schedule = [0] * len(schedule.values)
|
|
|
|
for i, value in enumerate(schedule.values):
|
|
|
|
if value > 0:
|
|
|
|
_working_hours_per_schedule[i] = 1
|
2023-06-07 14:40:44 -04:00
|
|
|
for day_type in schedule.day_types:
|
|
|
|
_working_hours_per_thermal_zone[day_type] = _working_hours_per_schedule
|
2023-05-19 15:04:02 -04:00
|
|
|
if len(_working_hours) == 0:
|
|
|
|
_working_hours = _working_hours_per_thermal_zone
|
|
|
|
else:
|
2023-05-30 17:13:49 -04:00
|
|
|
for key, item in _working_hours.items():
|
2023-05-19 15:04:02 -04:00
|
|
|
saved_values = _working_hours_per_thermal_zone[key]
|
2023-05-30 17:13:49 -04:00
|
|
|
for i, value in enumerate(item):
|
2023-06-07 14:40:44 -04:00
|
|
|
_working_hours[key][i] = max(_working_hours[key][i], saved_values[i])
|
|
|
|
|
2023-09-25 05:19:02 -04:00
|
|
|
working_hours = {}
|
|
|
|
values_months = []
|
|
|
|
for month in cte.WEEK_DAYS_A_MONTH.keys():
|
|
|
|
_total_hours_month = 0
|
|
|
|
for key in _working_hours:
|
|
|
|
hours = sum(_working_hours[key])
|
|
|
|
_total_hours_month += hours * cte.WEEK_DAYS_A_MONTH[month][key]
|
|
|
|
values_months.append(_total_hours_month)
|
|
|
|
working_hours[cte.MONTH] = values_months
|
|
|
|
working_hours[cte.YEAR] = sum(working_hours[cte.MONTH])
|
|
|
|
return working_hours
|
2023-05-19 15:04:02 -04:00
|
|
|
|
|
|
|
@property
|
|
|
|
def distribution_systems_electrical_consumption(self):
|
|
|
|
"""
|
2023-08-08 10:19:35 -04:00
|
|
|
Get total electricity consumption for distribution and emission systems in J
|
2023-05-19 15:04:02 -04:00
|
|
|
return: dict
|
|
|
|
"""
|
2024-02-07 18:54:09 -05:00
|
|
|
_distribution_systems_electrical_consumption = {}
|
2023-05-30 17:13:49 -04:00
|
|
|
if len(self._distribution_systems_electrical_consumption) != 0:
|
|
|
|
return self._distribution_systems_electrical_consumption
|
2023-06-02 15:05:29 -04:00
|
|
|
_peak_load = self.heating_peak_load[cte.YEAR][0]
|
2023-05-30 17:13:49 -04:00
|
|
|
_peak_load_type = cte.HEATING
|
2023-06-02 15:05:29 -04:00
|
|
|
if _peak_load < self.cooling_peak_load[cte.YEAR][0]:
|
|
|
|
_peak_load = self.cooling_peak_load[cte.YEAR][0]
|
2023-05-30 17:13:49 -04:00
|
|
|
_peak_load_type = cte.COOLING
|
|
|
|
|
2023-06-01 13:55:27 -04:00
|
|
|
_working_hours = self._calculate_working_hours()
|
2023-05-30 17:13:49 -04:00
|
|
|
_consumption_fix_flow = 0
|
2023-06-01 13:55:27 -04:00
|
|
|
if self.energy_systems is None:
|
|
|
|
return self._distribution_systems_electrical_consumption
|
2023-05-30 17:13:49 -04:00
|
|
|
for energy_system in self.energy_systems:
|
2024-02-07 18:54:09 -05:00
|
|
|
distribution_systems = energy_system.distribution_systems
|
|
|
|
for distribution_system in distribution_systems:
|
|
|
|
emission_systems = distribution_system.emission_systems
|
|
|
|
parasitic_energy_consumption = 0
|
|
|
|
if emission_systems is not None:
|
|
|
|
for emission_system in emission_systems:
|
|
|
|
parasitic_energy_consumption += emission_system.parasitic_energy_consumption
|
|
|
|
consumption_variable_flow = distribution_system.distribution_consumption_variable_flow
|
|
|
|
for demand_type in energy_system.demand_types:
|
|
|
|
if demand_type.lower() == cte.HEATING.lower():
|
|
|
|
if _peak_load_type == cte.HEATING.lower():
|
|
|
|
_consumption_fix_flow = distribution_system.distribution_consumption_fix_flow
|
|
|
|
for heating_demand_key in self.heating_demand:
|
|
|
|
_consumption = [0]*len(self.heating_demand[heating_demand_key])
|
|
|
|
_demand = self.heating_demand[heating_demand_key]
|
|
|
|
for i, _ in enumerate(_consumption):
|
|
|
|
_consumption[i] += (parasitic_energy_consumption + consumption_variable_flow) * _demand[i]
|
|
|
|
self._distribution_systems_electrical_consumption[heating_demand_key] = _consumption
|
|
|
|
if demand_type.lower() == cte.COOLING.lower():
|
|
|
|
if _peak_load_type == cte.COOLING.lower():
|
|
|
|
_consumption_fix_flow = distribution_system.distribution_consumption_fix_flow
|
|
|
|
for demand_key in self.cooling_demand:
|
|
|
|
_consumption = self._distribution_systems_electrical_consumption[demand_key]
|
|
|
|
_demand = self.cooling_demand[demand_key]
|
|
|
|
for i, _ in enumerate(_consumption):
|
|
|
|
_consumption[i] += (parasitic_energy_consumption + consumption_variable_flow) * _demand[i]
|
|
|
|
self._distribution_systems_electrical_consumption[demand_key] = _consumption
|
2023-05-30 17:13:49 -04:00
|
|
|
|
2023-06-01 13:55:27 -04:00
|
|
|
for key, item in self._distribution_systems_electrical_consumption.items():
|
|
|
|
for i in range(0, len(item)):
|
2023-10-13 03:11:03 -04:00
|
|
|
_working_hours_value = _working_hours[key]
|
|
|
|
if len(item) == 12:
|
|
|
|
_working_hours_value = _working_hours[key][i]
|
2023-10-13 02:57:42 -04:00
|
|
|
self._distribution_systems_electrical_consumption[key][i] += (
|
2023-10-13 03:11:03 -04:00
|
|
|
_peak_load * _consumption_fix_flow * _working_hours_value * cte.WATTS_HOUR_TO_JULES
|
2023-10-13 02:57:42 -04:00
|
|
|
)
|
|
|
|
|
2023-05-19 15:04:02 -04:00
|
|
|
return self._distribution_systems_electrical_consumption
|
|
|
|
|
2023-05-04 10:39:23 -04:00
|
|
|
def _calculate_consumption(self, consumption_type, demand):
|
|
|
|
# todo: modify when COP depends on the hour
|
|
|
|
coefficient_of_performance = 0
|
2023-05-29 15:26:21 -04:00
|
|
|
if self.energy_systems is None:
|
|
|
|
return None
|
2023-05-04 10:39:23 -04:00
|
|
|
for energy_system in self.energy_systems:
|
2023-10-05 13:18:57 -04:00
|
|
|
generation_systems = energy_system.generation_systems
|
2023-05-04 10:39:23 -04:00
|
|
|
for demand_type in energy_system.demand_types:
|
2023-05-12 09:27:29 -04:00
|
|
|
if demand_type.lower() == consumption_type.lower():
|
2023-05-30 17:13:49 -04:00
|
|
|
if consumption_type in (cte.HEATING, cte.DOMESTIC_HOT_WATER):
|
2023-10-04 17:07:19 -04:00
|
|
|
for generation_system in generation_systems:
|
2024-02-07 18:54:09 -05:00
|
|
|
if generation_system.heat_efficiency is not None:
|
|
|
|
coefficient_of_performance = float(generation_system.heat_efficiency)
|
2023-05-04 10:39:23 -04:00
|
|
|
elif consumption_type == cte.COOLING:
|
2023-10-04 17:07:19 -04:00
|
|
|
for generation_system in generation_systems:
|
2024-02-07 18:54:09 -05:00
|
|
|
if generation_system.cooling_efficiency is not None:
|
|
|
|
coefficient_of_performance = float(generation_system.cooling_efficiency)
|
2023-05-04 10:39:23 -04:00
|
|
|
elif consumption_type == cte.ELECTRICITY:
|
2023-10-04 17:07:19 -04:00
|
|
|
for generation_system in generation_systems:
|
2024-02-07 18:54:09 -05:00
|
|
|
if generation_system.electricity_efficiency is not None:
|
|
|
|
coefficient_of_performance = float(generation_system.electricity_efficiency)
|
2023-05-04 10:39:23 -04:00
|
|
|
if coefficient_of_performance == 0:
|
2023-05-15 11:03:54 -04:00
|
|
|
values = [0]*len(demand)
|
|
|
|
final_energy_consumed = values
|
2023-05-04 10:39:23 -04:00
|
|
|
else:
|
2023-05-15 11:03:54 -04:00
|
|
|
final_energy_consumed = []
|
2023-05-04 10:39:23 -04:00
|
|
|
for demand_value in demand:
|
|
|
|
final_energy_consumed.append(demand_value / coefficient_of_performance)
|
|
|
|
return final_energy_consumed
|
|
|
|
|
|
|
|
@property
|
|
|
|
def onsite_electrical_production(self):
|
|
|
|
"""
|
2023-08-07 16:56:01 -04:00
|
|
|
Get total electricity produced onsite in J
|
2023-05-04 10:39:23 -04:00
|
|
|
return: dict
|
|
|
|
"""
|
2023-05-29 11:56:56 -04:00
|
|
|
orientation_losses_factor = {cte.MONTH: {'north': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
|
|
'east': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
|
|
'south': [2.137931, 1.645503, 1.320946, 1.107817, 0.993213, 0.945175,
|
|
|
|
0.967949, 1.065534, 1.24183, 1.486486, 1.918033, 2.210526],
|
|
|
|
'west': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]},
|
|
|
|
cte.YEAR: {'north': [0],
|
|
|
|
'east': [0],
|
|
|
|
'south': [1.212544],
|
|
|
|
'west': [0]}
|
|
|
|
}
|
2023-08-08 11:20:44 -04:00
|
|
|
|
|
|
|
# Add other systems whenever new ones appear
|
2023-05-29 15:26:21 -04:00
|
|
|
if self.energy_systems is None:
|
|
|
|
return self._onsite_electrical_production
|
2023-05-04 10:39:23 -04:00
|
|
|
for energy_system in self.energy_systems:
|
2024-02-07 18:54:09 -05:00
|
|
|
for generation_system in energy_system.generation_systems:
|
|
|
|
if generation_system.system_type == cte.PHOTOVOLTAIC:
|
|
|
|
if generation_system.electricity_efficiency is not None:
|
|
|
|
_efficiency = float(generation_system.electricity_efficiency)
|
|
|
|
else:
|
|
|
|
_efficiency = 0
|
|
|
|
self._onsite_electrical_production = {}
|
|
|
|
for _key in self.roofs[0].global_irradiance.keys():
|
|
|
|
_results = [0 for _ in range(0, len(self.roofs[0].global_irradiance[_key]))]
|
|
|
|
for surface in self.roofs:
|
|
|
|
if _key in orientation_losses_factor:
|
|
|
|
_results = [x + y * _efficiency * surface.perimeter_area
|
|
|
|
* surface.solar_collectors_area_reduction_factor * z
|
|
|
|
for x, y, z in zip(_results, surface.global_irradiance[_key],
|
|
|
|
orientation_losses_factor[_key]['south'])]
|
|
|
|
self._onsite_electrical_production[_key] = _results
|
2023-05-04 10:39:23 -04:00
|
|
|
return self._onsite_electrical_production
|
2024-02-07 18:54:09 -05:00
|
|
|
|
|
|
|
@property
|
|
|
|
def heating_consumption_disaggregated(self) -> dict:
|
|
|
|
"""
|
|
|
|
Get energy consumed for heating from different fuels in J
|
|
|
|
return: dict
|
|
|
|
"""
|
|
|
|
return self._heating_consumption_disaggregated
|
|
|
|
|
|
|
|
@heating_consumption_disaggregated.setter
|
|
|
|
def heating_consumption_disaggregated(self, value):
|
|
|
|
"""
|
|
|
|
Get energy consumed for heating from different fuels in J
|
|
|
|
return: dict
|
|
|
|
"""
|
|
|
|
self._heating_consumption_disaggregated = value
|
|
|
|
|
2024-02-15 23:50:45 -05:00
|
|
|
|
2023-10-19 01:39:35 -04:00
|
|
|
@property
|
|
|
|
def lower_corner(self):
|
|
|
|
"""
|
|
|
|
Get building lower corner.
|
|
|
|
"""
|
|
|
|
return [self._min_x, self._min_y, self._min_z]
|
|
|
|
|
|
|
|
@property
|
|
|
|
def upper_corner(self):
|
|
|
|
"""
|
|
|
|
Get building upper corner.
|
|
|
|
"""
|
|
|
|
return [self._max_x, self._max_y, self._max_z]
|