energy systems factory test added and solved some bugs. Not working yet
This commit is contained in:
parent
5a99f16bc9
commit
dbcab5bcb9
|
@ -45,6 +45,12 @@ class Building(CityObject):
|
|||
self._lighting_electrical_demand = dict()
|
||||
self._appliances_electrical_demand = dict()
|
||||
self._domestic_hot_water_heat_demand = dict()
|
||||
self._heating_consumption = dict()
|
||||
self._cooling_consumption = dict()
|
||||
self._lighting_electrical_consumption = dict()
|
||||
self._appliances_electrical_consumption = dict()
|
||||
self._domestic_hot_water_consumption = dict()
|
||||
self._onsite_electrical_production = dict()
|
||||
self._eave_height = None
|
||||
self._energy_systems = None
|
||||
self._systems_archetype_name = None
|
||||
|
@ -485,3 +491,114 @@ class Building(CityObject):
|
|||
:param value: str
|
||||
"""
|
||||
self._systems_archetype_name = value
|
||||
|
||||
@property
|
||||
def heating_consumption(self):
|
||||
"""
|
||||
Get energy consumption for heating according to the heating system installed
|
||||
return: dict
|
||||
"""
|
||||
for heating_demand_key in self.heating:
|
||||
demand = self.heating[heating_demand_key][0]
|
||||
consumption_type = cte.HEATING
|
||||
final_energy_consumed = self._calculate_consumption(consumption_type, demand)
|
||||
self._heating_consumption[heating_demand_key] = final_energy_consumed
|
||||
return self._heating_consumption
|
||||
|
||||
@property
|
||||
def cooling_consumption(self):
|
||||
"""
|
||||
Get energy consumption for cooling according to the cooling system installed
|
||||
return: dict
|
||||
"""
|
||||
for cooling_demand_key in self.cooling:
|
||||
demand = self.cooling[cooling_demand_key][0]
|
||||
consumption_type = cte.COOLING
|
||||
final_energy_consumed = self._calculate_consumption(consumption_type, demand)
|
||||
self._cooling_consumption[cooling_demand_key] = final_energy_consumed
|
||||
return self._cooling_consumption
|
||||
|
||||
@property
|
||||
def domestic_hot_water_consumption(self):
|
||||
"""
|
||||
Get energy consumption for domestic according to the domestic hot water system installed
|
||||
return: dict
|
||||
"""
|
||||
for domestic_hot_water_demand_key in self.domestic_hot_water_heat_demand:
|
||||
demand = self.domestic_hot_water_heat_demand[domestic_hot_water_demand_key][0]
|
||||
consumption_type = cte.DOMESTIC_HOT_WATER
|
||||
final_energy_consumed = self._calculate_consumption(consumption_type, demand)
|
||||
self._domestic_hot_water_consumption[domestic_hot_water_demand_key] = final_energy_consumed
|
||||
return self._domestic_hot_water_consumption
|
||||
|
||||
@property
|
||||
def lighting_electrical_consumption(self):
|
||||
"""
|
||||
Get energy consumption for lighting according to the electricity system installed
|
||||
return: dict
|
||||
"""
|
||||
for lighting_demand_key in self.lighting_electrical_demand:
|
||||
demand = self.lighting_electrical_demand[lighting_demand_key][0]
|
||||
consumption_type = cte.ELECTRICITY
|
||||
final_energy_consumed = self._calculate_consumption(consumption_type, demand)
|
||||
self._lighting_electrical_consumption[lighting_demand_key] = final_energy_consumed
|
||||
return self._lighting_electrical_consumption
|
||||
|
||||
@property
|
||||
def appliances_electrical_consumption(self):
|
||||
"""
|
||||
Get energy consumption for appliances according to the electricity system installed
|
||||
return: dict
|
||||
"""
|
||||
for appliances_demand_key in self.appliances_electrical_demand:
|
||||
demand = self.appliances_electrical_demand[appliances_demand_key][0]
|
||||
consumption_type = cte.ELECTRICITY
|
||||
final_energy_consumed = self._calculate_consumption(consumption_type, demand)
|
||||
self._appliances_electrical_consumption[appliances_demand_key] = final_energy_consumed
|
||||
return self._appliances_electrical_consumption
|
||||
|
||||
def _calculate_consumption(self, consumption_type, demand):
|
||||
# todo: modify when COP depends on the hour
|
||||
coefficient_of_performance = 0
|
||||
for energy_system in self.energy_systems:
|
||||
for demand_type in energy_system.demand_types:
|
||||
if demand_type == consumption_type:
|
||||
if consumption_type == cte.HEATING or consumption_type == cte.DOMESTIC_HOT_WATER:
|
||||
coefficient_of_performance = energy_system.generation_system.heat_efficiency
|
||||
elif consumption_type == cte.COOLING:
|
||||
coefficient_of_performance = energy_system.generation_system.cooling_efficiency
|
||||
elif consumption_type == cte.ELECTRICITY:
|
||||
coefficient_of_performance = energy_system.generation_system.electricity_efficiency
|
||||
final_energy_consumed = []
|
||||
if coefficient_of_performance == 0:
|
||||
final_energy_consumed.append(0)
|
||||
else:
|
||||
for demand_value in demand:
|
||||
final_energy_consumed.append(demand_value / coefficient_of_performance)
|
||||
return final_energy_consumed
|
||||
|
||||
@property
|
||||
def onsite_electrical_production(self):
|
||||
"""
|
||||
Get total electricity produced onsite
|
||||
return: dict
|
||||
"""
|
||||
# Add other systems whenever new ones appear
|
||||
for energy_system in self.energy_systems:
|
||||
if energy_system.generation_system.type == cte.PHOTOVOLTAIC:
|
||||
_efficiency = energy_system.generation_system.electricity_efficiency
|
||||
_temporal_cases = self.roofs[0].global_irradiance.keys()
|
||||
self._onsite_electrical_production = {}
|
||||
for _case in _temporal_cases:
|
||||
_results = []
|
||||
for surface in self.surfaces:
|
||||
for i, value in enumerate(surface.global_irradiance[_case]):
|
||||
if len(_results) == 0:
|
||||
_collector_production = value * _efficiency \
|
||||
* surface.perimeter_area * surface.solar_collectors_area_reduction_factor
|
||||
else:
|
||||
_collector_production = _results[i] * value * _efficiency \
|
||||
* surface.perimeter_area * surface.solar_collectors_area_reduction_factor
|
||||
_results.append(_collector_production)
|
||||
self._onsite_electrical_production[_case] = _results
|
||||
return self._onsite_electrical_production
|
||||
|
|
|
@ -7,6 +7,8 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
|
|||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import uuid
|
||||
import numpy as np
|
||||
from typing import List, Union
|
||||
|
@ -42,7 +44,7 @@ class Surface:
|
|||
self._associated_thermal_boundaries = []
|
||||
self._vegetation = None
|
||||
self._percentage_shared = None
|
||||
self._pv_coverage = None
|
||||
self._solar_collectors_area_reduction_factor = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
@ -135,7 +137,7 @@ class Surface:
|
|||
@property
|
||||
def azimuth(self):
|
||||
"""
|
||||
Get surface azimuth in radians
|
||||
Get surface azimuth in radians (north = 0)
|
||||
:return: float
|
||||
"""
|
||||
if self._azimuth is None:
|
||||
|
@ -146,7 +148,7 @@ class Surface:
|
|||
@property
|
||||
def inclination(self):
|
||||
"""
|
||||
Get surface inclination in radians
|
||||
Get surface inclination in radians (zenith = 0, horizon = pi/2)
|
||||
:return: float
|
||||
"""
|
||||
if self._inclination is None:
|
||||
|
@ -162,10 +164,12 @@ class Surface:
|
|||
:return: str
|
||||
"""
|
||||
if self._type is None:
|
||||
grad = np.rad2deg(self.inclination)
|
||||
if grad >= 170:
|
||||
inclination_cos = math.cos(self.inclination)
|
||||
# 170 degrees
|
||||
if inclination_cos <= -0.98:
|
||||
self._type = 'Ground'
|
||||
elif 80 <= grad <= 100:
|
||||
# between 80 and 100 degrees
|
||||
elif abs(inclination_cos) <= 0.17:
|
||||
self._type = 'Wall'
|
||||
else:
|
||||
self._type = 'Roof'
|
||||
|
@ -347,3 +351,36 @@ class Surface:
|
|||
:param value: float
|
||||
"""
|
||||
self._percentage_shared = value
|
||||
|
||||
@property
|
||||
def solar_collectors_area_reduction_factor(self):
|
||||
"""
|
||||
Get factor area collector per surface area if set or calculate using Romero Rodríguez, L. et al (2017) model if not
|
||||
:return: float
|
||||
"""
|
||||
if self._solar_collectors_area_reduction_factor is None:
|
||||
_solar_collectors_area_reduction_factor = 0
|
||||
if self.type == cte.ROOF:
|
||||
_protected_building_restriction = 1
|
||||
# 10 degrees range
|
||||
if abs(math.sin(self.inclination)) < 0.17:
|
||||
# horizontal
|
||||
_construction_restriction = 0.8
|
||||
_separation_of_panels = 0.46
|
||||
_shadow_between_panels = 0.7
|
||||
else:
|
||||
# pitched
|
||||
_construction_restriction = 0.9
|
||||
_separation_of_panels = 0.9
|
||||
_shadow_between_panels = 1
|
||||
_solar_collectors_area_reduction_factor = _protected_building_restriction * _construction_restriction \
|
||||
* _separation_of_panels * _shadow_between_panels
|
||||
return self._solar_collectors_area_reduction_factor
|
||||
|
||||
@solar_collectors_area_reduction_factor.setter
|
||||
def solar_collectors_area_reduction_factor(self, value):
|
||||
"""
|
||||
Set factor area collector per surface area
|
||||
:param value: float
|
||||
"""
|
||||
self._solar_collectors_area_reduction_factor = value
|
||||
|
|
|
@ -25,7 +25,7 @@ class GenericEnergySystem:
|
|||
@property
|
||||
def demand_types(self):
|
||||
"""
|
||||
Get demand able to cover from [heating, cooling, domestic_hot_water, electricity]
|
||||
Get demand able to cover from [Heating, Cooling, Domestic Hot Water, Electricity]
|
||||
:return: [string]
|
||||
"""
|
||||
return self._demand_types
|
||||
|
@ -33,7 +33,7 @@ class GenericEnergySystem:
|
|||
@demand_types.setter
|
||||
def demand_types(self, value):
|
||||
"""
|
||||
Set demand able to cover from [heating, cooling, domestic_hot_water, electricity]
|
||||
Set demand able to cover from [Heating, Cooling, Domestic Hot Water, Electricity]
|
||||
:param value: [string]
|
||||
"""
|
||||
self._demand_types = value
|
||||
|
|
|
@ -14,6 +14,7 @@ class GenericGenerationSystem:
|
|||
GenericGenerationSystem class
|
||||
"""
|
||||
def __init__(self):
|
||||
self._type = None
|
||||
self._fuel_type = None
|
||||
self._heat_power = None
|
||||
self._cooling_power = None
|
||||
|
@ -27,10 +28,26 @@ class GenericGenerationSystem:
|
|||
self._storage_capacity = None
|
||||
self._auxiliary_equipment = None
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
"""
|
||||
Get system type
|
||||
:return: string
|
||||
"""
|
||||
return self._type
|
||||
|
||||
@type.setter
|
||||
def type(self, value):
|
||||
"""
|
||||
Set system type
|
||||
:param value: string
|
||||
"""
|
||||
self._type = value
|
||||
|
||||
@property
|
||||
def fuel_type(self):
|
||||
"""
|
||||
Get fuel_type from [renewable, gas, diesel, electricity, wood, coal]
|
||||
Get fuel_type from [Renewable, Gas, Diesel, Electricity, Wood, Coal]
|
||||
:return: string
|
||||
"""
|
||||
return self._fuel_type
|
||||
|
@ -38,7 +55,7 @@ class GenericGenerationSystem:
|
|||
@fuel_type.setter
|
||||
def fuel_type(self, value):
|
||||
"""
|
||||
Set fuel_type from [renewable, gas, diesel, electricity, wood, coal]
|
||||
Set fuel_type from [Renewable, Gas, Diesel, Electricity, Wood, Coal]
|
||||
:param value: string
|
||||
"""
|
||||
self._fuel_type = value
|
||||
|
@ -46,7 +63,7 @@ class GenericGenerationSystem:
|
|||
@property
|
||||
def source_types(self):
|
||||
"""
|
||||
Get source_type from [air, water, geothermal, district_heating, grid, on_site_electricity]
|
||||
Get source_type from [Air, Water, Geothermal, District Heating, Grid, Onsite Electricity]
|
||||
:return: [string]
|
||||
"""
|
||||
return self._source_types
|
||||
|
@ -54,7 +71,7 @@ class GenericGenerationSystem:
|
|||
@source_types.setter
|
||||
def source_types(self, value):
|
||||
"""
|
||||
Set source_type from [air, water, geothermal, district_heating, grid, on_site_electricity]
|
||||
Set source_type from [Air, Water, Geothermal, District Heating, Grid, Onsite Electricity]
|
||||
:param value: [string]
|
||||
"""
|
||||
self._source_types = value
|
||||
|
|
|
@ -164,6 +164,23 @@ EQUIPMENT = 'Equipment'
|
|||
ACTIVITY = 'Activity'
|
||||
PEOPLE_ACTIVITY_LEVEL = 'People Activity Level'
|
||||
DOMESTIC_HOT_WATER = 'Domestic Hot Water'
|
||||
HEATING = 'Heating'
|
||||
COOLING = 'Cooling'
|
||||
ELECTRICITY = 'Electricity'
|
||||
RENEWABLE = 'Renewable'
|
||||
WOOD = 'Wood'
|
||||
GAS = 'Gas'
|
||||
DIESEL = 'Diesel'
|
||||
COAL = 'Coal'
|
||||
AIR = 'Air'
|
||||
WATER = 'Water'
|
||||
GEOTHERMAL = 'Geothermal'
|
||||
DISTRICT_HEATING_NETWORK = 'District Heating'
|
||||
GRID = 'Grid'
|
||||
ONSITE_ELECTRICITY = 'Onsite Electricity'
|
||||
PHOTOVOLTAIC = 'Photovoltaic'
|
||||
BOILER = 'Boiler'
|
||||
HEAT_PUMP = 'Heat Pump'
|
||||
|
||||
# Geometry
|
||||
EPSILON = 0.0000001
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
"""
|
||||
Dictionaries module for Montreal system to hub energy generation system
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2023 Concordia CERC group
|
||||
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||
"""
|
||||
|
||||
import hub.helpers.constants as cte
|
||||
|
||||
|
||||
class MontrealSystemToHubEnergyGenerationSystem:
|
||||
|
||||
def __init__(self):
|
||||
self._dictionary = {'Fuel-fired water boiler with baseboards': cte.BOILER,
|
||||
'Electrical resistance water boiler': cte.BOILER,
|
||||
'Fuel-fired furnace and fuel boiler for acs': cte.BOILER,
|
||||
'Baseboards: hydronic with fuel boiler': cte.BOILER,
|
||||
'Electrical baseboards and electrical boiler for acs': cte.BOILER,
|
||||
'Air cooled DX with external condenser': cte.HEAT_PUMP,
|
||||
'Water cooled, water chiller': cte.HEAT_PUMP,
|
||||
'PV system': cte.PHOTOVOLTAIC
|
||||
}
|
||||
|
||||
@property
|
||||
def dictionary(self) -> dict:
|
||||
return self._dictionary
|
|
@ -14,6 +14,7 @@ from hub.helpers.data.hub_function_to_nrcan_construction_function import HubFunc
|
|||
from hub.helpers.data.hub_usage_to_comnet_usage import HubUsageToComnetUsage
|
||||
from hub.helpers.data.hub_usage_to_hft_usage import HubUsageToHftUsage
|
||||
from hub.helpers.data.hub_usage_to_nrcan_usage import HubUsageToNrcanUsage
|
||||
from hub.helpers.data.montreal_system_to_hub_energy_generation_system import MontrealSystemToHubEnergyGenerationSystem
|
||||
|
||||
|
||||
class Dictionaries:
|
||||
|
@ -91,3 +92,9 @@ class Dictionaries:
|
|||
"""
|
||||
return AlkisFunctionToHubFunction().dictionary
|
||||
|
||||
@property
|
||||
def montreal_system_to_hub_energy_generation_system(self):
|
||||
"""
|
||||
Get montreal custom system names to hub energy system names, transformation dictionary
|
||||
"""
|
||||
return MontrealSystemToHubEnergyGenerationSystem().dictionary
|
||||
|
|
|
@ -12,6 +12,7 @@ from hub.catalog_factories.energy_systems_catalog_factory import EnergySystemsCa
|
|||
from hub.city_model_structure.energy_systems.generic_energy_system import GenericEnergySystem
|
||||
from hub.city_model_structure.energy_systems.generic_generation_system import GenericGenerationSystem
|
||||
from hub.city_model_structure.energy_systems.generic_distribution_system import GenericDistributionSystem
|
||||
from hub.helpers.dictionaries import Dictionaries
|
||||
|
||||
logger = get_logger()
|
||||
|
||||
|
@ -32,12 +33,17 @@ class MontrealCustomEnergySystemParameters:
|
|||
city = self._city
|
||||
montreal_custom_catalog = EnergySystemsCatalogFactory('montreal_custom').catalog
|
||||
for building in city.buildings:
|
||||
archetype_name = building.energy_systems_archetype_name
|
||||
archetype_name = f'{building.energy_systems_archetype_name}_lod1.0'
|
||||
# archetype_name = building.energy_systems_archetype_name
|
||||
try:
|
||||
print(archetype_name)
|
||||
archetype = self._search_archetypes(montreal_custom_catalog, archetype_name)
|
||||
except KeyError:
|
||||
logger.error(f'Building {building.name} has unknown usage archetype for usage: {archetype_name}')
|
||||
sys.stderr.write(f'Building {building.name} has unknown usage archetype for usage: {archetype_name}')
|
||||
print('ERROR')
|
||||
logger.error(f'Building {building.name} has unknown energy system archetype for system name: {archetype_name}')
|
||||
sys.stderr.write(f'Building {building.name} has unknown energy system archetype '
|
||||
f'for system name: {archetype_name}')
|
||||
print('END ERROR')
|
||||
continue
|
||||
building_systems = []
|
||||
for equipment in archetype.equipments:
|
||||
|
@ -45,6 +51,9 @@ class MontrealCustomEnergySystemParameters:
|
|||
energy_system.demand_types = equipment.demand_types
|
||||
_generation_system = GenericGenerationSystem()
|
||||
archetype_generation_equipment = equipment.generation_system
|
||||
_type = str(equipment.name).split('_')[0]
|
||||
_generation_system.type = Dictionaries().montreal_system_to_hub_energy_generation_system[
|
||||
_type]
|
||||
# dhw peak does not add anything to the total heat peak
|
||||
_generation_system.heat_power = building.heating_peak_load
|
||||
_generation_system.cooling_power = building.cooling_peak_load
|
||||
|
|
58
hub/unittests/test_systems_factory.py
Normal file
58
hub/unittests/test_systems_factory.py
Normal file
|
@ -0,0 +1,58 @@
|
|||
"""
|
||||
TestSystemsFactory
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2023 Concordia CERC group
|
||||
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from unittest import TestCase
|
||||
|
||||
from hub.imports.geometry_factory import GeometryFactory
|
||||
from hub.imports.construction_factory import ConstructionFactory
|
||||
from hub.imports.usage_factory import UsageFactory
|
||||
from hub.imports.energy_systems_factory import EnergySystemsFactory
|
||||
|
||||
|
||||
class TestSystemsFactory(TestCase):
|
||||
"""
|
||||
TestSystemsFactory TestCase
|
||||
"""
|
||||
def setUp(self) -> None:
|
||||
"""
|
||||
Configure test environment
|
||||
:return:
|
||||
"""
|
||||
self._city = None
|
||||
self._example_path = (Path(__file__).parent / 'tests_data').resolve()
|
||||
|
||||
def _get_citygml(self, file):
|
||||
file_path = (self._example_path / file).resolve()
|
||||
self._city = GeometryFactory('citygml', path=file_path).city
|
||||
self.assertIsNotNone(self._city, 'city is none')
|
||||
self.assertIsNotNone(self._city.level_of_detail.geometry, 'wrong construction level of detail')
|
||||
return self._city
|
||||
|
||||
def test_montreal_custom_system_factory(self):
|
||||
"""
|
||||
Enrich the city with the construction information and verify it
|
||||
"""
|
||||
file = 'one_building_in_kelowna.gml'
|
||||
city = self._get_citygml(file)
|
||||
for building in city.buildings:
|
||||
building.energy_systems_archetype_name = 'system 1 gas'
|
||||
|
||||
EnergySystemsFactory('montreal_custom', city).enrich()
|
||||
for building in city.buildings:
|
||||
print(building.energy_systems)
|
||||
|
||||
def test_montreal_custom_system_results(self):
|
||||
"""
|
||||
Enrich the city with the construction information and verify it
|
||||
"""
|
||||
file = 'one_building_in_kelowna.gml'
|
||||
city = self._get_citygml(file)
|
||||
for building in city.buildings:
|
||||
building.year_of_construction = 1980
|
||||
ConstructionFactory('nrcan', city).enrich()
|
||||
UsageFactory('nrcan', city).enrich()
|
Loading…
Reference in New Issue
Block a user