energy systems factory test added and solved some bugs. Not working yet

This commit is contained in:
Pilar Monsalvete 2023-05-04 10:39:23 -04:00
parent 5a99f16bc9
commit dbcab5bcb9
9 changed files with 303 additions and 15 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View 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()