Merge branch 'geojson' into idf_lights
This commit is contained in:
commit
27549e784d
|
@ -20,7 +20,7 @@ from hub.catalog_factories.data_models.cost.cost_helper import CostHelper
|
||||||
|
|
||||||
class MontrealCustomCatalog(Catalog):
|
class MontrealCustomCatalog(Catalog):
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
path = 'C:/Users/JGAVALDA/PycharmProjects/hub/hub/data/costs/montreal_costs.xml'
|
path = (path / 'montreal_costs.xml')
|
||||||
with open(path) as xml:
|
with open(path) as xml:
|
||||||
self._archetypes = xmltodict.parse(xml.read(), force_list='archetype')
|
self._archetypes = xmltodict.parse(xml.read(), force_list='archetype')
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ class CostCatalogFactory:
|
||||||
"""
|
"""
|
||||||
def __init__(self, file_type, base_path=None):
|
def __init__(self, file_type, base_path=None):
|
||||||
if base_path is None:
|
if base_path is None:
|
||||||
base_path = 'C:/Users/JGAVALDA/PycharmProjects/hub/hub/data/costs'
|
base_path = Path(Path(__file__).parent.parent / 'data/costs')
|
||||||
self._catalog_type = '_' + file_type.lower()
|
self._catalog_type = '_' + file_type.lower()
|
||||||
self._path = base_path
|
self._path = base_path
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ class Archetype:
|
||||||
Get name
|
Get name
|
||||||
:return: string
|
:return: string
|
||||||
"""
|
"""
|
||||||
return f'{self._country}_{self._municipality}_{self._function}_{self._lod}'
|
return f'{self._country}_{self._municipality}_{self._function}_lod{self._lod}'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lod(self):
|
def lod(self):
|
||||||
|
|
|
@ -38,3 +38,13 @@ class CapitalCost:
|
||||||
:return: float
|
:return: float
|
||||||
"""
|
"""
|
||||||
return self._overhead_and_profit
|
return self._overhead_and_profit
|
||||||
|
|
||||||
|
def chapter(self, name) -> Chapter:
|
||||||
|
"""
|
||||||
|
Get specific chapter by name
|
||||||
|
:return: Chapter
|
||||||
|
"""
|
||||||
|
for chapter in self.general_chapters:
|
||||||
|
if chapter.chapter_type == name:
|
||||||
|
return chapter
|
||||||
|
raise KeyError(f'Chapter name {name} not found')
|
||||||
|
|
|
@ -30,3 +30,13 @@ class Chapter:
|
||||||
:return: [str]
|
:return: [str]
|
||||||
"""
|
"""
|
||||||
return self._items
|
return self._items
|
||||||
|
|
||||||
|
def item(self, name) -> ItemDescription:
|
||||||
|
"""
|
||||||
|
Get specific item by name
|
||||||
|
:return: ItemDescription
|
||||||
|
"""
|
||||||
|
for item in self.items:
|
||||||
|
if item.type == name:
|
||||||
|
return item
|
||||||
|
raise KeyError(f'Item name {name} not found')
|
||||||
|
|
|
@ -9,8 +9,11 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
|
||||||
import sys
|
import sys
|
||||||
from typing import List, Union
|
from typing import List, Union
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
from hub.hub_logger import logger
|
from hub.hub_logger import logger
|
||||||
import hub.helpers.constants as cte
|
import hub.helpers.constants as cte
|
||||||
|
import hub.helpers.peak_loads as pl
|
||||||
from hub.city_model_structure.building_demand.surface import Surface
|
from hub.city_model_structure.building_demand.surface import Surface
|
||||||
from hub.city_model_structure.city_object import CityObject
|
from hub.city_model_structure.city_object import CityObject
|
||||||
from hub.city_model_structure.building_demand.household import Household
|
from hub.city_model_structure.building_demand.household import Household
|
||||||
|
@ -370,15 +373,14 @@ class Building(CityObject):
|
||||||
Get heating peak load in W
|
Get heating peak load in W
|
||||||
:return: dict{DataFrame(float)}
|
:return: dict{DataFrame(float)}
|
||||||
"""
|
"""
|
||||||
return self._heating_peak_load
|
results = {}
|
||||||
|
if cte.HOUR in self.heating:
|
||||||
@heating_peak_load.setter
|
monthly_values = pl.peak_loads_from_hourly(self.heating[cte.HOUR][next(iter(self.heating[cte.HOUR]))].values)
|
||||||
def heating_peak_load(self, value):
|
else:
|
||||||
"""
|
monthly_values = pl.heating_peak_loads_from_methodology(self)
|
||||||
Set heating peak load in W
|
results[cte.MONTH] = pd.DataFrame(monthly_values, columns=['heating peak loads'])
|
||||||
:param value: dict{DataFrame(float)}
|
results[cte.YEAR] = pd.DataFrame([max(monthly_values)], columns=['heating peak loads'])
|
||||||
"""
|
return results
|
||||||
self._heating_peak_load = value
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cooling_peak_load(self) -> dict:
|
def cooling_peak_load(self) -> dict:
|
||||||
|
@ -386,15 +388,14 @@ class Building(CityObject):
|
||||||
Get cooling peak load in W
|
Get cooling peak load in W
|
||||||
:return: dict{DataFrame(float)}
|
:return: dict{DataFrame(float)}
|
||||||
"""
|
"""
|
||||||
return self._cooling_peak_load
|
results = {}
|
||||||
|
if cte.HOUR in self.cooling:
|
||||||
@cooling_peak_load.setter
|
monthly_values = pl.peak_loads_from_hourly(self.cooling[cte.HOUR][next(iter(self.cooling[cte.HOUR]))])
|
||||||
def cooling_peak_load(self, value):
|
else:
|
||||||
"""
|
monthly_values = pl.cooling_peak_loads_from_methodology(self)
|
||||||
Set peak load in W
|
results[cte.MONTH] = pd.DataFrame(monthly_values, columns=['cooling peak loads'])
|
||||||
:param value: dict{DataFrame(float)}
|
results[cte.YEAR] = pd.DataFrame([max(monthly_values)], columns=['cooling peak loads'])
|
||||||
"""
|
return results
|
||||||
self._cooling_peak_load = value
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def eave_height(self):
|
def eave_height(self):
|
||||||
|
|
|
@ -78,7 +78,7 @@ class InternalZone:
|
||||||
def usages(self) -> [Usage]:
|
def usages(self) -> [Usage]:
|
||||||
"""
|
"""
|
||||||
Get internal zone usage zones
|
Get internal zone usage zones
|
||||||
:return: [UsageZone]
|
:return: [Usage]
|
||||||
"""
|
"""
|
||||||
return self._usages
|
return self._usages
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ class InternalZone:
|
||||||
def usages(self, value):
|
def usages(self, value):
|
||||||
"""
|
"""
|
||||||
Set internal zone usage zones
|
Set internal zone usage zones
|
||||||
:param value: [UsageZone]
|
:param value: [Usage]
|
||||||
"""
|
"""
|
||||||
self._usages = value
|
self._usages = value
|
||||||
|
|
||||||
|
|
|
@ -122,6 +122,8 @@ class City:
|
||||||
Get the name for the climatic information reference city
|
Get the name for the climatic information reference city
|
||||||
:return: None or str
|
:return: None or str
|
||||||
"""
|
"""
|
||||||
|
if self._climate_reference_city is None:
|
||||||
|
self._climate_reference_city = self._get_location().city
|
||||||
return self._climate_reference_city
|
return self._climate_reference_city
|
||||||
|
|
||||||
@climate_reference_city.setter
|
@climate_reference_city.setter
|
||||||
|
@ -130,7 +132,6 @@ class City:
|
||||||
Set the name for the climatic information reference city
|
Set the name for the climatic information reference city
|
||||||
:param value: str
|
:param value: str
|
||||||
"""
|
"""
|
||||||
if value is not None:
|
|
||||||
self._climate_reference_city = str(value)
|
self._climate_reference_city = str(value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -51,6 +51,7 @@ class InselMonthlyEnergyBalance(Insel):
|
||||||
)
|
)
|
||||||
self._export()
|
self._export()
|
||||||
|
|
||||||
|
|
||||||
def _export(self):
|
def _export(self):
|
||||||
for i_file, content in enumerate(self._contents):
|
for i_file, content in enumerate(self._contents):
|
||||||
file_name = self._insel_files_paths[i_file]
|
file_name = self._insel_files_paths[i_file]
|
||||||
|
@ -94,12 +95,19 @@ class InselMonthlyEnergyBalance(Insel):
|
||||||
inputs.append(f"{str(100 + i)}.1 % Radiation surface {str(i)}")
|
inputs.append(f"{str(100 + i)}.1 % Radiation surface {str(i)}")
|
||||||
|
|
||||||
number_of_storeys = int(building.eave_height / building.average_storey_height)
|
number_of_storeys = int(building.eave_height / building.average_storey_height)
|
||||||
|
attic_heated = building.attic_heated
|
||||||
|
basement_heated = building.basement_heated
|
||||||
|
if building.attic_heated is None:
|
||||||
|
attic_heated = 0
|
||||||
|
if building.basement_heated is None:
|
||||||
|
basement_heated = 0
|
||||||
|
|
||||||
# BUILDING PARAMETERS
|
# BUILDING PARAMETERS
|
||||||
parameters = [f'{building.volume} % BP(1) Heated Volume (m3)',
|
parameters = [f'{building.volume} % BP(1) Heated Volume (m3)',
|
||||||
f'{building.average_storey_height} % BP(2) Average storey height (m)',
|
f'{building.average_storey_height} % BP(2) Average storey height (m)',
|
||||||
f'{number_of_storeys} % BP(3) Number of storeys above ground',
|
f'{number_of_storeys} % BP(3) Number of storeys above ground',
|
||||||
f'{building.attic_heated} % BP(4) Attic heating type (0=no room, 1=unheated, 2=heated)',
|
f'{attic_heated} % BP(4) Attic heating type (0=no room, 1=unheated, 2=heated)',
|
||||||
f'{building.basement_heated} % BP(5) Cellar heating type (0=no room, 1=unheated, 2=heated, '
|
f'{basement_heated} % BP(5) Cellar heating type (0=no room, 1=unheated, 2=heated, '
|
||||||
f'99=invalid)']
|
f'99=invalid)']
|
||||||
|
|
||||||
# todo: this method and the insel model have to be reviewed for more than one internal zone
|
# todo: this method and the insel model have to be reviewed for more than one internal zone
|
||||||
|
|
|
@ -55,9 +55,7 @@ class EnergyBuildingsExportsFactory:
|
||||||
"""
|
"""
|
||||||
idf_data_path = (Path(__file__).parent / './building_energy/idf_files/').resolve()
|
idf_data_path = (Path(__file__).parent / './building_energy/idf_files/').resolve()
|
||||||
# todo: create a get epw file function based on the city
|
# todo: create a get epw file function based on the city
|
||||||
#print('path', idf_data_path)
|
|
||||||
weather_path = (Path(__file__).parent / '../data/weather/epw/CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').resolve()
|
weather_path = (Path(__file__).parent / '../data/weather/epw/CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').resolve()
|
||||||
#print(weather_path)
|
|
||||||
return Idf(self._city, self._path, (idf_data_path / 'Minimal.idf'), (idf_data_path / 'Energy+.idd'), weather_path,
|
return Idf(self._city, self._path, (idf_data_path / 'Minimal.idf'), (idf_data_path / 'Energy+.idd'), weather_path,
|
||||||
target_buildings=self._target_buildings, adjacent_buildings=self._adjacent_buildings)
|
target_buildings=self._target_buildings, adjacent_buildings=self._adjacent_buildings)
|
||||||
|
|
||||||
|
@ -74,7 +72,6 @@ class EnergyBuildingsExportsFactory:
|
||||||
Export the city given to the class using the given export type handler
|
Export the city given to the class using the given export type handler
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
print(self)
|
|
||||||
return getattr(self, self._export_type, lambda: None)
|
return getattr(self, self._export_type, lambda: None)
|
||||||
|
|
||||||
def export_debug(self):
|
def export_debug(self):
|
||||||
|
|
|
@ -65,14 +65,6 @@ class ExportsFactory:
|
||||||
"""
|
"""
|
||||||
return Obj(self._city, self._path)
|
return Obj(self._city, self._path)
|
||||||
|
|
||||||
@property
|
|
||||||
def _grounded_obj(self):
|
|
||||||
"""
|
|
||||||
Export the city geometry to obj with grounded coordinates
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
return Obj(self._city, self._path)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _sra(self):
|
def _sra(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -4,6 +4,8 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
Copyright © 2022 Concordia CERC group
|
Copyright © 2022 Concordia CERC group
|
||||||
Project Coder Guillermo.GutierrezMorote@concordia.ca
|
Project Coder Guillermo.GutierrezMorote@concordia.ca
|
||||||
"""
|
"""
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import xmltodict
|
import xmltodict
|
||||||
|
|
||||||
from hub.imports.weather_factory import WeatherFactory
|
from hub.imports.weather_factory import WeatherFactory
|
||||||
|
@ -32,9 +34,12 @@ class SimplifiedRadiosityAlgorithm:
|
||||||
self._end_month = end_month
|
self._end_month = end_month
|
||||||
self._end_day = end_day
|
self._end_day = end_day
|
||||||
self._city = city
|
self._city = city
|
||||||
|
self._city.climate_file = str((Path(file_name).parent / f'{city.name}.cli').resolve())
|
||||||
|
self._city.climate_reference_city = city.location
|
||||||
self._target_buildings = target_buildings
|
self._target_buildings = target_buildings
|
||||||
self._weather_format = weather_format
|
self._weather_format = weather_format
|
||||||
self._weather_file = weather_file
|
self._weather_file = weather_file
|
||||||
|
|
||||||
self._export()
|
self._export()
|
||||||
|
|
||||||
def _correct_point(self, point):
|
def _correct_point(self, point):
|
||||||
|
@ -45,8 +50,8 @@ class SimplifiedRadiosityAlgorithm:
|
||||||
return [x, y, z]
|
return [x, y, z]
|
||||||
|
|
||||||
def _export(self):
|
def _export(self):
|
||||||
self._export_sra_xml()
|
|
||||||
self._export_sra_cli()
|
self._export_sra_cli()
|
||||||
|
self._export_sra_xml()
|
||||||
|
|
||||||
def _export_sra_cli(self):
|
def _export_sra_cli(self):
|
||||||
file = self._city.climate_file
|
file = self._city.climate_file
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
"""
|
||||||
|
Dictionaries module for hub function to Montreal custom costs function
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
import hub.helpers.constants as cte
|
||||||
|
|
||||||
|
|
||||||
|
class HubFunctionToMontrealCustomCostsFunction:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._dictionary = {
|
||||||
|
cte.RESIDENTIAL: 'residential',
|
||||||
|
cte.SINGLE_FAMILY_HOUSE: 'residential',
|
||||||
|
cte.MULTI_FAMILY_HOUSE: 'residential',
|
||||||
|
cte.ROW_HOUSE: 'residential',
|
||||||
|
cte.MID_RISE_APARTMENT: 'residential',
|
||||||
|
cte.HIGH_RISE_APARTMENT: 'residential',
|
||||||
|
cte.OFFICE_AND_ADMINISTRATION: 'non-residential',
|
||||||
|
cte.SMALL_OFFICE: 'non-residential',
|
||||||
|
cte.MEDIUM_OFFICE: 'non-residential',
|
||||||
|
cte.LARGE_OFFICE: 'non-residential',
|
||||||
|
cte.COURTHOUSE: 'non-residential',
|
||||||
|
cte.FIRE_STATION: 'non-residential',
|
||||||
|
cte.PENITENTIARY: 'non-residential',
|
||||||
|
cte.POLICE_STATION: 'non-residential',
|
||||||
|
cte.POST_OFFICE: 'non-residential',
|
||||||
|
cte.LIBRARY: 'non-residential',
|
||||||
|
cte.EDUCATION: 'non-residential',
|
||||||
|
cte.PRIMARY_SCHOOL: 'non-residential',
|
||||||
|
cte.PRIMARY_SCHOOL_WITH_SHOWER: 'non-residential',
|
||||||
|
cte.SECONDARY_SCHOOL: 'non-residential',
|
||||||
|
cte.UNIVERSITY: 'non-residential',
|
||||||
|
cte.LABORATORY_AND_RESEARCH_CENTER: 'non-residential',
|
||||||
|
cte.STAND_ALONE_RETAIL: 'non-residential',
|
||||||
|
cte.HOSPITAL: 'non-residential',
|
||||||
|
cte.OUT_PATIENT_HEALTH_CARE: 'non-residential',
|
||||||
|
cte.HEALTH_CARE: 'non-residential',
|
||||||
|
cte.RETIREMENT_HOME_OR_ORPHANAGE: 'non-residential',
|
||||||
|
cte.COMMERCIAL: 'non-residential',
|
||||||
|
cte.STRIP_MALL: 'non-residential',
|
||||||
|
cte.SUPERMARKET: 'non-residential',
|
||||||
|
cte.RETAIL_SHOP_WITHOUT_REFRIGERATED_FOOD: 'non-residential',
|
||||||
|
cte.RETAIL_SHOP_WITH_REFRIGERATED_FOOD: 'non-residential',
|
||||||
|
cte.RESTAURANT: 'full service restaurant',
|
||||||
|
cte.QUICK_SERVICE_RESTAURANT: 'non-residential',
|
||||||
|
cte.FULL_SERVICE_RESTAURANT: 'non-residential',
|
||||||
|
cte.HOTEL: 'non-residential',
|
||||||
|
cte.HOTEL_MEDIUM_CLASS: 'non-residential',
|
||||||
|
cte.SMALL_HOTEL: 'non-residential',
|
||||||
|
cte.LARGE_HOTEL: 'non-residential',
|
||||||
|
cte.DORMITORY: 'non-residential',
|
||||||
|
cte.EVENT_LOCATION: 'non-residential',
|
||||||
|
cte.CONVENTION_CENTER: 'non-residential',
|
||||||
|
cte.HALL: 'non-residential',
|
||||||
|
cte.GREEN_HOUSE: 'non-residential',
|
||||||
|
cte.INDUSTRY: 'non-residential',
|
||||||
|
cte.WORKSHOP: 'non-residential',
|
||||||
|
cte.WAREHOUSE: 'non-residential',
|
||||||
|
cte.WAREHOUSE_REFRIGERATED: 'non-residential',
|
||||||
|
cte.SPORTS_LOCATION: 'non-residential',
|
||||||
|
cte.SPORTS_ARENA: 'non-residential',
|
||||||
|
cte.GYMNASIUM: 'non-residential',
|
||||||
|
cte.MOTION_PICTURE_THEATRE: 'non-residential',
|
||||||
|
cte.MUSEUM: 'non-residential',
|
||||||
|
cte.PERFORMING_ARTS_THEATRE: 'non-residential',
|
||||||
|
cte.TRANSPORTATION: 'non-residential',
|
||||||
|
cte.AUTOMOTIVE_FACILITY: 'non-residential',
|
||||||
|
cte.PARKING_GARAGE: 'non-residential',
|
||||||
|
cte.RELIGIOUS: 'non-residential',
|
||||||
|
cte.NON_HEATED: 'non-residential'
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dictionary(self) -> dict:
|
||||||
|
return self._dictionary
|
|
@ -67,7 +67,7 @@ class HubFunctionToNrelConstructionFunction:
|
||||||
cte.MUSEUM: 'n/a',
|
cte.MUSEUM: 'n/a',
|
||||||
cte.PERFORMING_ARTS_THEATRE: 'n/a',
|
cte.PERFORMING_ARTS_THEATRE: 'n/a',
|
||||||
cte.TRANSPORTATION: 'n/a',
|
cte.TRANSPORTATION: 'n/a',
|
||||||
cte.AUTOMOTIVE_FACILITY: 'n/aquebec_to_hub',
|
cte.AUTOMOTIVE_FACILITY: 'n/a',
|
||||||
cte.PARKING_GARAGE: 'n/a',
|
cte.PARKING_GARAGE: 'n/a',
|
||||||
cte.RELIGIOUS: 'n/a',
|
cte.RELIGIOUS: 'n/a',
|
||||||
cte.NON_HEATED: 'n/a'
|
cte.NON_HEATED: 'n/a'
|
||||||
|
|
|
@ -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_comnet_usage import HubUsageToComnetUsage
|
||||||
from hub.helpers.data.hub_usage_to_hft_usage import HubUsageToHftUsage
|
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.hub_usage_to_nrcan_usage import HubUsageToNrcanUsage
|
||||||
|
from hub.helpers.data.hub_function_to_montreal_custom_costs_function import HubFunctionToMontrealCustomCostsFunction
|
||||||
|
|
||||||
|
|
||||||
class Dictionaries:
|
class Dictionaries:
|
||||||
|
@ -91,3 +92,11 @@ class Dictionaries:
|
||||||
"""
|
"""
|
||||||
return AlkisFunctionToHubFunction().dictionary
|
return AlkisFunctionToHubFunction().dictionary
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hub_function_to_montreal_custom_costs_function(self) -> dict:
|
||||||
|
"""
|
||||||
|
Get hub function to Montreal custom costs function, transformation dictionary
|
||||||
|
:return: dict
|
||||||
|
"""
|
||||||
|
return HubFunctionToMontrealCustomCostsFunction().dictionary
|
||||||
|
|
||||||
|
|
|
@ -100,9 +100,9 @@ class GeometryHelper:
|
||||||
j = 0
|
j = 0
|
||||||
next_coordinate = ground.perimeter_polygon.coordinates[j]
|
next_coordinate = ground.perimeter_polygon.coordinates[j]
|
||||||
distance = GeometryHelper.distance_between_points(coordinate, next_coordinate)
|
distance = GeometryHelper.distance_between_points(coordinate, next_coordinate)
|
||||||
if distance == 0:
|
|
||||||
continue
|
|
||||||
steps = int(distance * factor * 2)
|
steps = int(distance * factor * 2)
|
||||||
|
if steps == 0:
|
||||||
|
continue
|
||||||
delta_x = (next_coordinate[0] - coordinate[0]) / steps
|
delta_x = (next_coordinate[0] - coordinate[0]) / steps
|
||||||
delta_y = (next_coordinate[1] - coordinate[1]) / steps
|
delta_y = (next_coordinate[1] - coordinate[1]) / steps
|
||||||
|
|
||||||
|
|
71
hub/helpers/peak_loads.py
Normal file
71
hub/helpers/peak_loads.py
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import math
|
||||||
|
|
||||||
|
import hub.helpers.constants as cte
|
||||||
|
from hub.helpers.peak_calculation.loads_calculation import LoadsCalculation
|
||||||
|
|
||||||
|
_MONTH_STARTING_HOUR = [0, 744, 1416, 2160, 2880, 3624, 4344, 5088, 5832, 6552, 7296, 8016, math.inf]
|
||||||
|
|
||||||
|
def peak_loads_from_hourly(hourly_values):
|
||||||
|
month = 1
|
||||||
|
peaks = [0 for _ in range(12)]
|
||||||
|
for i, value in enumerate(hourly_values):
|
||||||
|
if _MONTH_STARTING_HOUR[month] <= i:
|
||||||
|
month += 1
|
||||||
|
if value > peaks[month-1]:
|
||||||
|
peaks[month-1] = value
|
||||||
|
return peaks
|
||||||
|
|
||||||
|
def heating_peak_loads_from_methodology(building):
|
||||||
|
monthly_heating_loads = []
|
||||||
|
ambient_temperature = building.external_temperature[cte.HOUR]['epw']
|
||||||
|
for month in range(0, 12):
|
||||||
|
ground_temperature = building.ground_temperature[cte.MONTH]['2'][month]
|
||||||
|
heating_ambient_temperature = 100
|
||||||
|
start_hour = _MONTH_STARTING_HOUR[month]
|
||||||
|
end_hour = 8760
|
||||||
|
if month < 11:
|
||||||
|
end_hour = _MONTH_STARTING_HOUR[month + 1]
|
||||||
|
for hour in range(start_hour, end_hour):
|
||||||
|
temperature = ambient_temperature[hour]
|
||||||
|
if temperature < heating_ambient_temperature:
|
||||||
|
heating_ambient_temperature = temperature
|
||||||
|
loads = LoadsCalculation(building)
|
||||||
|
heating_load_transmitted = loads.get_heating_transmitted_load(heating_ambient_temperature, ground_temperature)
|
||||||
|
heating_load_ventilation_sensible = loads.get_heating_ventilation_load_sensible(heating_ambient_temperature)
|
||||||
|
heating_load_ventilation_latent = 0
|
||||||
|
heating_load = heating_load_transmitted + heating_load_ventilation_sensible + heating_load_ventilation_latent
|
||||||
|
if heating_load < 0:
|
||||||
|
heating_load = 0
|
||||||
|
monthly_heating_loads.append(heating_load)
|
||||||
|
return monthly_heating_loads
|
||||||
|
|
||||||
|
def cooling_peak_loads_from_methodology(building):
|
||||||
|
monthly_cooling_loads = []
|
||||||
|
ambient_temperature = building.external_temperature[cte.HOUR]['epw']
|
||||||
|
for month in range(0, 12):
|
||||||
|
ground_temperature = building.ground_temperature[cte.MONTH]['2'][month]
|
||||||
|
cooling_ambient_temperature = -100
|
||||||
|
cooling_calculation_hour = -1
|
||||||
|
start_hour = _MONTH_STARTING_HOUR[month]
|
||||||
|
end_hour = 8760
|
||||||
|
if month < 11:
|
||||||
|
end_hour = _MONTH_STARTING_HOUR[month + 1]
|
||||||
|
for hour in range(start_hour, end_hour):
|
||||||
|
temperature = ambient_temperature[hour]
|
||||||
|
if temperature > cooling_ambient_temperature:
|
||||||
|
cooling_ambient_temperature = temperature
|
||||||
|
cooling_calculation_hour = hour
|
||||||
|
loads = LoadsCalculation(building)
|
||||||
|
cooling_load_transmitted = loads.get_cooling_transmitted_load(cooling_ambient_temperature, ground_temperature)
|
||||||
|
cooling_load_renovation_sensible = loads.get_cooling_ventilation_load_sensible(cooling_ambient_temperature)
|
||||||
|
cooling_load_internal_gains_sensible = loads.get_internal_load_sensible()
|
||||||
|
cooling_load_radiation = loads.get_radiation_load('sra', cooling_calculation_hour)
|
||||||
|
cooling_load_sensible = cooling_load_transmitted + cooling_load_renovation_sensible - cooling_load_radiation \
|
||||||
|
- cooling_load_internal_gains_sensible
|
||||||
|
|
||||||
|
cooling_load_latent = 0
|
||||||
|
cooling_load = cooling_load_sensible + cooling_load_latent
|
||||||
|
if cooling_load > 0:
|
||||||
|
cooling_load = 0
|
||||||
|
monthly_cooling_loads.append(abs(cooling_load))
|
||||||
|
return monthly_cooling_loads
|
|
@ -61,44 +61,31 @@ class Geojson:
|
||||||
self._min_y = y
|
self._min_y = y
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _create_buildings_lod0(name, year_of_construction, function, surfaces_coordinates):
|
def _create_building_lod0(name, year_of_construction, function, surface_coordinates):
|
||||||
surfaces = []
|
|
||||||
buildings = []
|
|
||||||
for zone, surface_coordinates in enumerate(surfaces_coordinates):
|
|
||||||
points = igh.points_from_string(igh.remove_last_point_from_string(surface_coordinates))
|
points = igh.points_from_string(igh.remove_last_point_from_string(surface_coordinates))
|
||||||
# geojson provides the roofs, need to be transform into grounds
|
|
||||||
points = igh.invert_points(points)
|
points = igh.invert_points(points)
|
||||||
polygon = Polygon(points)
|
polygon = Polygon(points)
|
||||||
polygon.area = igh.ground_area(points)
|
polygon.area = igh.ground_area(points)
|
||||||
surface = Surface(polygon, polygon)
|
surface = Surface(polygon, polygon, name=f'{name}_ground')
|
||||||
if len(buildings) == 1:
|
return Building(f'{name}', [surface], year_of_construction, function)
|
||||||
buildings[0].surfaces.append(surface)
|
|
||||||
else:
|
|
||||||
surfaces.append(surface)
|
|
||||||
buildings.append(Building(f'{name}', surfaces, year_of_construction, function))
|
|
||||||
return buildings
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _create_buildings_lod1(name, year_of_construction, function, height, surface_coordinates):
|
def _create_building_lod1(name, year_of_construction, function, height, surface_coordinates):
|
||||||
lod0_buildings = Geojson._create_buildings_lod0(name, year_of_construction, function, surface_coordinates)
|
building = Geojson._create_building_lod0(name, year_of_construction, function, surface_coordinates)
|
||||||
surfaces = []
|
surfaces = []
|
||||||
buildings = []
|
|
||||||
|
|
||||||
for zone, lod0_building in enumerate(lod0_buildings):
|
|
||||||
# print(zone, lod0_building.name)
|
|
||||||
volume = 0
|
volume = 0
|
||||||
for surface in lod0_building.grounds:
|
for ground in building.grounds:
|
||||||
volume = volume + surface.solid_polygon.area * height
|
volume += ground.solid_polygon.area * height
|
||||||
surfaces.append(surface)
|
surfaces.append(ground)
|
||||||
roof_coordinates = []
|
roof_coordinates = []
|
||||||
# adding a roof means invert the polygon coordinates and change the Z value
|
# adding a roof means invert the polygon coordinates and change the Z value
|
||||||
for coordinate in surface.solid_polygon.coordinates:
|
for coordinate in ground.solid_polygon.coordinates:
|
||||||
roof_coordinate = np.array([coordinate[0], coordinate[1], height])
|
roof_coordinate = np.array([coordinate[0], coordinate[1], height])
|
||||||
# insert the roof rotated already
|
# insert the roof rotated already
|
||||||
roof_coordinates.insert(0, roof_coordinate)
|
roof_coordinates.insert(0, roof_coordinate)
|
||||||
polygon = Polygon(roof_coordinates)
|
roof_polygon = Polygon(roof_coordinates)
|
||||||
polygon.area = surface.solid_polygon.area
|
roof_polygon.area = ground.solid_polygon.area
|
||||||
roof = Surface(polygon, polygon)
|
roof = Surface(roof_polygon, roof_polygon)
|
||||||
surfaces.append(roof)
|
surfaces.append(roof)
|
||||||
# adding a wall means add the point coordinates and the next point coordinates with Z's height and 0
|
# adding a wall means add the point coordinates and the next point coordinates with Z's height and 0
|
||||||
coordinates_length = len(roof.solid_polygon.coordinates)
|
coordinates_length = len(roof.solid_polygon.coordinates)
|
||||||
|
@ -118,9 +105,7 @@ class Geojson:
|
||||||
surfaces.append(wall)
|
surfaces.append(wall)
|
||||||
building = Building(f'{name}', surfaces, year_of_construction, function)
|
building = Building(f'{name}', surfaces, year_of_construction, function)
|
||||||
building.volume = volume
|
building.volume = volume
|
||||||
buildings.append(building)
|
return building
|
||||||
|
|
||||||
return buildings
|
|
||||||
|
|
||||||
def _get_polygons(self, polygons, coordinates):
|
def _get_polygons(self, polygons, coordinates):
|
||||||
if type(coordinates[0][self.X]) != float:
|
if type(coordinates[0][self.X]) != float:
|
||||||
|
@ -173,8 +158,6 @@ class Geojson:
|
||||||
GeometryHelper.distance_between_points(neighbour_line[0], neighbour_line[1]) -
|
GeometryHelper.distance_between_points(neighbour_line[0], neighbour_line[1]) -
|
||||||
GeometryHelper.distance_between_points(line[1], neighbour_line[0]) -
|
GeometryHelper.distance_between_points(line[1], neighbour_line[0]) -
|
||||||
GeometryHelper.distance_between_points(line[0], neighbour_line[1])) / 2
|
GeometryHelper.distance_between_points(line[0], neighbour_line[1])) / 2
|
||||||
print(line_shared)
|
|
||||||
print()
|
|
||||||
percentage_ground = line_shared / GeometryHelper.distance_between_points(line[0], line[1])
|
percentage_ground = line_shared / GeometryHelper.distance_between_points(line[0], line[1])
|
||||||
percentage_height = neighbour_height / building.max_height
|
percentage_height = neighbour_height / building.max_height
|
||||||
if percentage_height > 1:
|
if percentage_height > 1:
|
||||||
|
@ -188,14 +171,13 @@ class Geojson:
|
||||||
Get city out of a Geojson file
|
Get city out of a Geojson file
|
||||||
"""
|
"""
|
||||||
if self._city is None:
|
if self._city is None:
|
||||||
missing_functions = []
|
|
||||||
buildings = []
|
buildings = []
|
||||||
building_id = 0
|
lod = 0
|
||||||
lod = 1
|
|
||||||
for feature in self._geojson['features']:
|
for feature in self._geojson['features']:
|
||||||
extrusion_height = 0
|
extrusion_height = 0
|
||||||
if self._extrusion_height_field is not None:
|
if self._extrusion_height_field is not None:
|
||||||
extrusion_height = float(feature['properties'][self._extrusion_height_field])
|
extrusion_height = float(feature['properties'][self._extrusion_height_field])
|
||||||
|
lod = 0.5
|
||||||
year_of_construction = None
|
year_of_construction = None
|
||||||
if self._year_of_construction_field is not None:
|
if self._year_of_construction_field is not None:
|
||||||
year_of_construction = int(feature['properties'][self._year_of_construction_field])
|
year_of_construction = int(feature['properties'][self._year_of_construction_field])
|
||||||
|
@ -206,57 +188,111 @@ class Geojson:
|
||||||
# use the transformation dictionary to retrieve the proper function
|
# use the transformation dictionary to retrieve the proper function
|
||||||
if function in self._function_to_hub:
|
if function in self._function_to_hub:
|
||||||
function = self._function_to_hub[function]
|
function = self._function_to_hub[function]
|
||||||
else:
|
|
||||||
if function not in missing_functions:
|
|
||||||
missing_functions.append(function)
|
|
||||||
function = function
|
|
||||||
geometry = feature['geometry']
|
geometry = feature['geometry']
|
||||||
if 'id' in feature:
|
if 'id' in feature:
|
||||||
building_name = feature['id']
|
building_name = feature['id']
|
||||||
else:
|
|
||||||
building_name = f'building_{building_id}'
|
|
||||||
building_id += 1
|
|
||||||
if self._name_field is not None:
|
if self._name_field is not None:
|
||||||
building_name = feature['properties'][self._name_field]
|
building_name = feature['properties'][self._name_field]
|
||||||
polygons = []
|
|
||||||
for part, coordinates in enumerate(geometry['coordinates']):
|
|
||||||
polygons = self._get_polygons(polygons, coordinates)
|
|
||||||
for polygon in polygons:
|
|
||||||
if extrusion_height == 0:
|
|
||||||
buildings = buildings + Geojson._create_buildings_lod0(f'{building_name}',
|
|
||||||
year_of_construction,
|
|
||||||
function,
|
|
||||||
[polygon])
|
|
||||||
lod = 0
|
|
||||||
else:
|
|
||||||
if self._max_z < extrusion_height:
|
|
||||||
self._max_z = extrusion_height
|
|
||||||
if part == 0:
|
|
||||||
buildings = buildings + Geojson._create_buildings_lod1(f'{building_name}',
|
|
||||||
year_of_construction,
|
|
||||||
function,
|
|
||||||
extrusion_height,
|
|
||||||
[polygon])
|
|
||||||
else:
|
|
||||||
new_part = Geojson._create_buildings_lod1(f'{building_name}',
|
|
||||||
year_of_construction,
|
|
||||||
function,
|
|
||||||
extrusion_height,
|
|
||||||
[polygon])
|
|
||||||
surfaces = buildings[len(buildings) - 1].surfaces + new_part[0].surfaces
|
|
||||||
volume = buildings[len(buildings) - 1].volume + new_part[0].volume
|
|
||||||
buildings[len(buildings) - 1] = Building(f'{building_name}', surfaces, year_of_construction, function)
|
|
||||||
buildings[len(buildings) - 1].volume = volume
|
|
||||||
|
|
||||||
|
if str(geometry['type']).lower() == 'polygon':
|
||||||
|
buildings.append(self._parse_polygon(geometry['coordinates'],
|
||||||
|
building_name,
|
||||||
|
function,
|
||||||
|
year_of_construction,
|
||||||
|
extrusion_height))
|
||||||
|
|
||||||
|
elif str(geometry['type']).lower() == 'multipolygon':
|
||||||
|
buildings.append(self._parse_multi_polygon(geometry['coordinates'],
|
||||||
|
building_name,
|
||||||
|
function,
|
||||||
|
year_of_construction,
|
||||||
|
extrusion_height))
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(f'Geojson geometry type [{geometry["type"]}] unknown')
|
||||||
self._city = City([self._min_x, self._min_y, 0.0], [self._max_x, self._max_y, self._max_z], 'epsg:26911')
|
self._city = City([self._min_x, self._min_y, 0.0], [self._max_x, self._max_y, self._max_z], 'epsg:26911')
|
||||||
for building in buildings:
|
for building in buildings:
|
||||||
# Do not include "small building-like structures" to buildings
|
# Do not include "small building-like structures" to buildings
|
||||||
if building.floor_area >= 25:
|
if building.floor_area >= 25:
|
||||||
self._city.add_city_object(building)
|
self._city.add_city_object(building)
|
||||||
self._city.level_of_detail.geometry = lod
|
self._city.level_of_detail.geometry = lod
|
||||||
if lod == 1:
|
if lod > 0:
|
||||||
lines_information = GeometryHelper.city_mapping(self._city, plot=False)
|
lines_information = GeometryHelper.city_mapping(self._city, plot=False)
|
||||||
self._store_shared_percentage_to_walls(self._city, lines_information)
|
self._store_shared_percentage_to_walls(self._city, lines_information)
|
||||||
if len(missing_functions) > 0:
|
|
||||||
print(f'There are unknown functions {missing_functions}')
|
|
||||||
return self._city
|
return self._city
|
||||||
|
|
||||||
|
def _polygon_coordinates_to_3d(self, polygon_coordinates):
|
||||||
|
transformed_coordinates = ''
|
||||||
|
for coordinate in polygon_coordinates:
|
||||||
|
transformed = self._transformer.transform(coordinate[self.Y], coordinate[self.X])
|
||||||
|
self._save_bounds(transformed[self.X], transformed[self.Y])
|
||||||
|
transformed_coordinates = f'{transformed_coordinates} {transformed[self.X]} {transformed[self.Y]} 0.0'
|
||||||
|
return transformed_coordinates.lstrip(' ')
|
||||||
|
|
||||||
|
def _parse_polygon(self, coordinates, building_name, function, year_of_construction, extrusion_height):
|
||||||
|
print('poly')
|
||||||
|
for polygon_coordinates in coordinates:
|
||||||
|
coordinates_3d = self._polygon_coordinates_to_3d(polygon_coordinates)
|
||||||
|
if extrusion_height == 0:
|
||||||
|
building = Geojson._create_building_lod0(f'{building_name}',
|
||||||
|
year_of_construction,
|
||||||
|
function,
|
||||||
|
coordinates_3d)
|
||||||
|
else:
|
||||||
|
if self._max_z < extrusion_height:
|
||||||
|
self._max_z = extrusion_height
|
||||||
|
building = Geojson._create_building_lod1(f'{building_name}',
|
||||||
|
year_of_construction,
|
||||||
|
function,
|
||||||
|
extrusion_height,
|
||||||
|
coordinates_3d)
|
||||||
|
return building
|
||||||
|
|
||||||
|
def _parse_multi_polygon(self, coordinates, building_name, function, year_of_construction, extrusion_height):
|
||||||
|
print('multi')
|
||||||
|
surfaces = []
|
||||||
|
for polygon_coordinate in coordinates:
|
||||||
|
building = self._parse_polygon(polygon_coordinate, building_name, function, year_of_construction, 0)
|
||||||
|
for surface in building.surfaces:
|
||||||
|
if surface.type == cte.GROUND:
|
||||||
|
surfaces.append(surface)
|
||||||
|
else:
|
||||||
|
# overwrite last surface by adding the "hole" in the polygon
|
||||||
|
polygon = Polygon(surfaces[-1].solid_polygon.coordinates + surface.solid_polygon.coordinates)
|
||||||
|
surfaces[-1] = Surface(polygon, polygon)
|
||||||
|
if extrusion_height == 0:
|
||||||
|
return Building(building_name, surfaces, year_of_construction, function)
|
||||||
|
else:
|
||||||
|
volume = 0
|
||||||
|
for ground in building.grounds:
|
||||||
|
volume += ground.solid_polygon.area * extrusion_height
|
||||||
|
surfaces.append(ground)
|
||||||
|
roof_coordinates = []
|
||||||
|
# adding a roof means invert the polygon coordinates and change the Z value
|
||||||
|
for coordinate in ground.solid_polygon.coordinates:
|
||||||
|
roof_coordinate = np.array([coordinate[0], coordinate[1], extrusion_height])
|
||||||
|
# insert the roof rotated already
|
||||||
|
roof_coordinates.insert(0, roof_coordinate)
|
||||||
|
roof_polygon = Polygon(roof_coordinates)
|
||||||
|
roof_polygon.area = ground.solid_polygon.area
|
||||||
|
roof = Surface(roof_polygon, roof_polygon)
|
||||||
|
surfaces.append(roof)
|
||||||
|
# adding a wall means add the point coordinates and the next point coordinates with Z's height and 0
|
||||||
|
coordinates_length = len(roof.solid_polygon.coordinates)
|
||||||
|
for i, coordinate in enumerate(roof.solid_polygon.coordinates):
|
||||||
|
j = i + 1
|
||||||
|
if j == coordinates_length:
|
||||||
|
j = 0
|
||||||
|
next_coordinate = roof.solid_polygon.coordinates[j]
|
||||||
|
wall_coordinates = [
|
||||||
|
np.array([coordinate[0], coordinate[1], 0.0]),
|
||||||
|
np.array([next_coordinate[0], next_coordinate[1], 0.0]),
|
||||||
|
np.array([next_coordinate[0], next_coordinate[1], next_coordinate[2]]),
|
||||||
|
np.array([coordinate[0], coordinate[1], coordinate[2]])
|
||||||
|
]
|
||||||
|
polygon = Polygon(wall_coordinates)
|
||||||
|
wall = Surface(polygon, polygon)
|
||||||
|
surfaces.append(wall)
|
||||||
|
building = Building(f'{building_name}', surfaces, year_of_construction, function)
|
||||||
|
building.volume = volume
|
||||||
|
return building
|
||||||
|
|
|
@ -12,7 +12,7 @@ import hub.helpers.constants as cte
|
||||||
|
|
||||||
class InselMonthlyEnergyBalance:
|
class InselMonthlyEnergyBalance:
|
||||||
"""
|
"""
|
||||||
Import SRA results
|
Import insel monthly energy balance results
|
||||||
"""
|
"""
|
||||||
def __init__(self, city, base_path):
|
def __init__(self, city, base_path):
|
||||||
|
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
import hub.helpers.constants as cte
|
|
||||||
from hub.imports.results.peak_calculation.loads_calculation import LoadsCalculation
|
|
||||||
|
|
||||||
|
|
||||||
class PeakLoad:
|
|
||||||
|
|
||||||
_MONTH_STARTING_HOUR = [0, 744, 1416, 2160, 2880, 3624, 4344, 5088, 5832, 6552, 7296, 8016]
|
|
||||||
|
|
||||||
def __init__(self, city):
|
|
||||||
self._city = city
|
|
||||||
self._weather_format = 'epw'
|
|
||||||
|
|
||||||
def enrich(self):
|
|
||||||
for building in self._city.buildings:
|
|
||||||
monthly_heating_loads = []
|
|
||||||
monthly_cooling_loads = []
|
|
||||||
ambient_temperature = building.external_temperature[cte.HOUR][self._weather_format]
|
|
||||||
for month in range(0, 12):
|
|
||||||
ground_temperature = building.ground_temperature[cte.MONTH]['2'][month]
|
|
||||||
heating_ambient_temperature = 100
|
|
||||||
cooling_ambient_temperature = -100
|
|
||||||
heating_calculation_hour = -1
|
|
||||||
cooling_calculation_hour = -1
|
|
||||||
start_hour = self._MONTH_STARTING_HOUR[month]
|
|
||||||
end_hour = 8760
|
|
||||||
if month < 11:
|
|
||||||
end_hour = self._MONTH_STARTING_HOUR[month + 1]
|
|
||||||
for hour in range(start_hour, end_hour):
|
|
||||||
temperature = ambient_temperature[hour]
|
|
||||||
if temperature < heating_ambient_temperature:
|
|
||||||
heating_ambient_temperature = temperature
|
|
||||||
heating_calculation_hour = hour
|
|
||||||
if temperature > cooling_ambient_temperature:
|
|
||||||
cooling_ambient_temperature = temperature
|
|
||||||
cooling_calculation_hour = hour
|
|
||||||
|
|
||||||
loads = LoadsCalculation(building)
|
|
||||||
heating_load_transmitted = loads.get_heating_transmitted_load(heating_ambient_temperature, ground_temperature)
|
|
||||||
heating_load_ventilation_sensible = loads.get_heating_ventilation_load_sensible(heating_ambient_temperature)
|
|
||||||
heating_load_ventilation_latent = 0
|
|
||||||
heating_load = heating_load_transmitted + heating_load_ventilation_sensible + heating_load_ventilation_latent
|
|
||||||
|
|
||||||
cooling_load_transmitted = loads.get_cooling_transmitted_load(cooling_ambient_temperature, ground_temperature)
|
|
||||||
cooling_load_renovation_sensible = loads.get_cooling_ventilation_load_sensible(cooling_ambient_temperature)
|
|
||||||
cooling_load_internal_gains_sensible = loads.get_internal_load_sensible()
|
|
||||||
cooling_load_radiation = loads.get_radiation_load(self._irradiance_format, cooling_calculation_hour)
|
|
||||||
cooling_load_sensible = cooling_load_transmitted + cooling_load_renovation_sensible - cooling_load_radiation \
|
|
||||||
- cooling_load_internal_gains_sensible
|
|
||||||
|
|
||||||
cooling_load_latent = 0
|
|
||||||
cooling_load = cooling_load_sensible + cooling_load_latent
|
|
||||||
if heating_load < 0:
|
|
||||||
heating_load = 0
|
|
||||||
if cooling_load > 0:
|
|
||||||
cooling_load = 0
|
|
||||||
monthly_heating_loads.append(heating_load)
|
|
||||||
monthly_cooling_loads.append(cooling_load)
|
|
||||||
|
|
||||||
self._results[building.name] = {'monthly heating peak load': monthly_heating_loads,
|
|
||||||
'monthly cooling peak load': monthly_cooling_loads}
|
|
||||||
self._print_results()
|
|
|
@ -9,7 +9,6 @@ from pathlib import Path
|
||||||
|
|
||||||
from hub.helpers.utils import validate_import_export_type
|
from hub.helpers.utils import validate_import_export_type
|
||||||
from hub.hub_logger import logger
|
from hub.hub_logger import logger
|
||||||
from hub.imports.results.peak_load import PeakLoad
|
|
||||||
from hub.imports.results.simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm
|
from hub.imports.results.simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm
|
||||||
from hub.imports.results.insel_monthly_energry_balance import InselMonthlyEnergyBalance
|
from hub.imports.results.insel_monthly_energry_balance import InselMonthlyEnergyBalance
|
||||||
from hub.imports.results.insel_heatpump_energy_demand import InselHeatPumpEnergyDemand
|
from hub.imports.results.insel_heatpump_energy_demand import InselHeatPumpEnergyDemand
|
||||||
|
@ -54,18 +53,12 @@ class ResultFactory:
|
||||||
"""
|
"""
|
||||||
InselHeatPumpEnergyDemand(self._city, self._base_path, self._hp_model).enrich()
|
InselHeatPumpEnergyDemand(self._city, self._base_path, self._hp_model).enrich()
|
||||||
|
|
||||||
def _insel_meb(self):
|
def _insel_monthly_energy_balance(self):
|
||||||
"""
|
"""
|
||||||
Enrich the city with insel monthly energy balance results
|
Enrich the city with insel monthly energy balance results
|
||||||
"""
|
"""
|
||||||
InselMonthlyEnergyBalance(self._city, self._base_path).enrich()
|
InselMonthlyEnergyBalance(self._city, self._base_path).enrich()
|
||||||
|
|
||||||
def _peak_load(self):
|
|
||||||
"""
|
|
||||||
Enrich the city with peak load results
|
|
||||||
"""
|
|
||||||
PeakLoad(self._city).enrich()
|
|
||||||
|
|
||||||
def enrich(self):
|
def enrich(self):
|
||||||
"""
|
"""
|
||||||
Enrich the city given to the class using the usage factory given handler
|
Enrich the city given to the class using the usage factory given handler
|
||||||
|
|
|
@ -71,7 +71,6 @@ class EpwWeatherParameters:
|
||||||
except SystemExit:
|
except SystemExit:
|
||||||
sys.stderr.write(f'Error: wrong formatting of weather file {self._path}\n')
|
sys.stderr.write(f'Error: wrong formatting of weather file {self._path}\n')
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
for building in self._city.buildings:
|
for building in self._city.buildings:
|
||||||
building.ground_temperature[cte.MONTH] = ground_temperature
|
building.ground_temperature[cte.MONTH] = ground_temperature
|
||||||
ground_temperature = {}
|
ground_temperature = {}
|
||||||
|
|
|
@ -52,3 +52,7 @@ class WeatherFactory:
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
getattr(self, self._handler, lambda: None)()
|
getattr(self, self._handler, lambda: None)()
|
||||||
|
|
||||||
|
def enrich_debug(self):
|
||||||
|
_path = Path(self._base_path / 'epw').resolve()
|
||||||
|
return EpwWeatherParameters(self._city, _path, self._file_name)
|
||||||
|
|
|
@ -137,18 +137,16 @@ class TestGeometryFactory(TestCase):
|
||||||
"""
|
"""
|
||||||
Test geojson import
|
Test geojson import
|
||||||
"""
|
"""
|
||||||
file = '2000_buildings.geojson'
|
file = 'hole_building.geojson'
|
||||||
city = GeometryFactory('geojson',
|
city = GeometryFactory('geojson',
|
||||||
path=(self._example_path / file).resolve(),
|
path=(self._example_path / file).resolve(),
|
||||||
height_field='building_height',
|
height_field='citygml_me',
|
||||||
year_of_construction_field='ANNEE_CONS',
|
year_of_construction_field='ANNEE_CONS',
|
||||||
name_field='ID_UEV',
|
name_field='ID_UEV',
|
||||||
function_field='CODE_UTILI',
|
function_field='CODE_UTILI',
|
||||||
function_to_hub=MontrealFunctionToHubFunction().dictionary).city
|
function_to_hub=MontrealFunctionToHubFunction().dictionary).city
|
||||||
# include 25 square meter condition for a building reduces buildings number from 2289 to 2057
|
hub.exports.exports_factory.ExportsFactory('obj', city, self._output_path).export_debug()
|
||||||
for building in city.buildings:
|
self.assertEqual(1964, len(city.buildings), 'wrong number of buildings')
|
||||||
print(building.name)
|
|
||||||
self.assertEqual(2057, len(city.buildings), 'wrong number of buildings')
|
|
||||||
|
|
||||||
def test_map_neighbours(self):
|
def test_map_neighbours(self):
|
||||||
"""
|
"""
|
||||||
|
@ -164,7 +162,7 @@ class TestGeometryFactory(TestCase):
|
||||||
year_of_construction_field='ANNEE_CONS',
|
year_of_construction_field='ANNEE_CONS',
|
||||||
function_field='LIBELLE_UT')
|
function_field='LIBELLE_UT')
|
||||||
|
|
||||||
# info_lod0 = GeometryHelper.city_mapping(city, plot=False)
|
info_lod0 = GeometryHelper.city_mapping(city, plot=False)
|
||||||
hub.exports.exports_factory.ExportsFactory('obj', city, self._output_path).export()
|
hub.exports.exports_factory.ExportsFactory('obj', city, self._output_path).export()
|
||||||
self.assertEqual(info_lod0, info_lod1)
|
self.assertEqual(info_lod0, info_lod1)
|
||||||
for building in city.buildings:
|
for building in city.buildings:
|
||||||
|
@ -177,36 +175,6 @@ class TestGeometryFactory(TestCase):
|
||||||
self.assertEqual('1_part_0_zone_0', city.city_object('3_part_0_zone_0').neighbours[0].name)
|
self.assertEqual('1_part_0_zone_0', city.city_object('3_part_0_zone_0').neighbours[0].name)
|
||||||
self.assertEqual('2_part_0_zone_0', city.city_object('3_part_0_zone_0').neighbours[1].name)
|
self.assertEqual('2_part_0_zone_0', city.city_object('3_part_0_zone_0').neighbours[1].name)
|
||||||
|
|
||||||
def test_neighbours(self):
|
|
||||||
"""
|
|
||||||
Test neighbours map creation
|
|
||||||
"""
|
|
||||||
file_path = (self._example_path / 'concordia_clean.geojson').resolve()
|
|
||||||
city = GeometryFactory('geojson',
|
|
||||||
path=file_path,
|
|
||||||
height_field='citygml_me',
|
|
||||||
year_of_construction_field='ANNEE_CONS',
|
|
||||||
name_field='OBJECTID_12',
|
|
||||||
function_field='CODE_UTILI',
|
|
||||||
function_to_hub=Dictionaries().montreal_function_to_hub_function).city
|
|
||||||
# print(city.lower_corner, city.upper_corner)
|
|
||||||
for building in city.buildings:
|
|
||||||
#for ground in building.grounds:
|
|
||||||
# print(ground.perimeter_polygon.coordinates)
|
|
||||||
# print(ground.perimeter_polygon.coordinates[0][0] - city.lower_corner[0], ground.perimeter_polygon.coordinates[0][1] - city.lower_corner[1])
|
|
||||||
# print(ground.perimeter_polygon.coordinates[1][0] - city.lower_corner[0], ground.perimeter_polygon.coordinates[1][1] - city.lower_corner[1])
|
|
||||||
break
|
|
||||||
ConstructionFactory('nrcan', city).enrich()
|
|
||||||
UsageFactory('nrcan', city).enrich()
|
|
||||||
info_lod1 = GeometryHelper.city_mapping(city, plot=True)
|
|
||||||
for building in city.buildings:
|
|
||||||
print(building.name)
|
|
||||||
ns = ''
|
|
||||||
for n in building.neighbours:
|
|
||||||
ns = f'{ns} {n.name}'
|
|
||||||
for surface in n.surfaces:
|
|
||||||
print('shared', surface.percentage_shared)
|
|
||||||
print('\t', ns)
|
|
||||||
# EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
115
hub/unittests/test_results_import.py
Normal file
115
hub/unittests/test_results_import.py
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
"""
|
||||||
|
TestExports test and validate the city export formats
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
import hub.helpers.constants as cte
|
||||||
|
from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
|
||||||
|
from hub.exports.exports_factory import ExportsFactory
|
||||||
|
from hub.helpers.dictionaries import Dictionaries
|
||||||
|
from hub.imports.construction_factory import ConstructionFactory
|
||||||
|
from hub.imports.geometry_factory import GeometryFactory
|
||||||
|
from hub.imports.results_factory import ResultFactory
|
||||||
|
from hub.imports.usage_factory import UsageFactory
|
||||||
|
|
||||||
|
|
||||||
|
class TestResultsImport(TestCase):
|
||||||
|
"""
|
||||||
|
TestImports class contains the unittest for import functionality
|
||||||
|
"""
|
||||||
|
def setUp(self) -> None:
|
||||||
|
"""
|
||||||
|
Test setup
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
self._example_path = (Path(__file__).parent / 'tests_data').resolve()
|
||||||
|
self._gml_path = (self._example_path / 'FZK_Haus_LoD_2.gml').resolve()
|
||||||
|
self._output_path = (Path(__file__).parent / 'tests_outputs').resolve()
|
||||||
|
self._city = GeometryFactory('citygml',
|
||||||
|
self._gml_path,
|
||||||
|
function_to_hub=Dictionaries().alkis_function_to_hub_function).city
|
||||||
|
ConstructionFactory('nrcan', self._city).enrich()
|
||||||
|
UsageFactory('nrcan', self._city).enrich()
|
||||||
|
|
||||||
|
def test_sra_import(self):
|
||||||
|
weather_file = (self._example_path / 'CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').resolve()
|
||||||
|
ExportsFactory('sra', self._city, self._output_path, weather_file=weather_file, weather_format='epw').export()
|
||||||
|
sra_path = (self._output_path / f'{self._city.name}_sra.xml').resolve()
|
||||||
|
subprocess.run(['sra', str(sra_path)])
|
||||||
|
ResultFactory('sra', self._city, self._output_path).enrich()
|
||||||
|
# Check that all the buildings have radiance in the surfaces
|
||||||
|
for building in self._city.buildings:
|
||||||
|
for surface in building.surfaces:
|
||||||
|
self.assertIsNotNone(surface.global_irradiance)
|
||||||
|
|
||||||
|
def test_meb_import(self):
|
||||||
|
weather_file = (self._example_path / 'CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').resolve()
|
||||||
|
ExportsFactory('sra', self._city, self._output_path, weather_file=weather_file, weather_format='epw').export()
|
||||||
|
sra_path = (self._output_path / f'{self._city.name}_sra.xml').resolve()
|
||||||
|
subprocess.run(['sra', str(sra_path)])
|
||||||
|
ResultFactory('sra', self._city, self._output_path).enrich()
|
||||||
|
EnergyBuildingsExportsFactory('insel_monthly_energy_balance', self._city, self._output_path).export()
|
||||||
|
for building in self._city.buildings:
|
||||||
|
insel_path = (self._output_path / f'{building.name}.insel')
|
||||||
|
subprocess.run(['insel', str(insel_path)])
|
||||||
|
ResultFactory('insel_monthly_energy_balance', self._city, self._output_path).enrich()
|
||||||
|
# Check that all the buildings have heating and cooling values
|
||||||
|
for building in self._city.buildings:
|
||||||
|
self.assertIsNotNone(building.heating[cte.MONTH][cte.INSEL_MEB])
|
||||||
|
self.assertIsNotNone(building.cooling[cte.MONTH][cte.INSEL_MEB])
|
||||||
|
self.assertIsNotNone(building.heating[cte.YEAR][cte.INSEL_MEB])
|
||||||
|
self.assertIsNotNone(building.cooling[cte.YEAR][cte.INSEL_MEB])
|
||||||
|
|
||||||
|
def test_peak_loads(self):
|
||||||
|
# todo: this is not technically a import
|
||||||
|
weather_file = (self._example_path / 'CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').resolve()
|
||||||
|
ExportsFactory('sra', self._city, self._output_path, weather_file=weather_file, weather_format='epw').export()
|
||||||
|
sra_path = (self._output_path / f'{self._city.name}_sra.xml').resolve()
|
||||||
|
subprocess.run(['sra', str(sra_path)])
|
||||||
|
ResultFactory('sra', self._city, self._output_path).enrich()
|
||||||
|
for building in self._city.buildings:
|
||||||
|
self.assertIsNotNone(building.heating_peak_load)
|
||||||
|
self.assertIsNotNone(building.cooling_peak_load)
|
||||||
|
|
||||||
|
values = [0 for _ in range(8760)]
|
||||||
|
values[0] = 1000
|
||||||
|
expected_yearly = pd.DataFrame([1000], columns=['expected'])
|
||||||
|
expected_monthly_list = [0 for _ in range(12)]
|
||||||
|
expected_monthly_list[0] = 1000
|
||||||
|
expected_monthly = pd.DataFrame(expected_monthly_list, columns=['expected'])
|
||||||
|
for building in self._city.buildings:
|
||||||
|
building.heating[cte.HOUR] = pd.DataFrame(values, columns=['dummy'])
|
||||||
|
building.cooling[cte.HOUR] = pd.DataFrame(values, columns=['dummy'])
|
||||||
|
self.assertIsNotNone(building.heating_peak_load)
|
||||||
|
self.assertIsNotNone(building.cooling_peak_load)
|
||||||
|
pd.testing.assert_series_equal(
|
||||||
|
building.heating_peak_load[cte.YEAR]['heating peak loads'],
|
||||||
|
expected_yearly['expected'],
|
||||||
|
check_names=False
|
||||||
|
)
|
||||||
|
pd.testing.assert_series_equal(
|
||||||
|
building.cooling_peak_load[cte.YEAR]['cooling peak loads'],
|
||||||
|
expected_yearly['expected'],
|
||||||
|
check_names=False
|
||||||
|
)
|
||||||
|
pd.testing.assert_series_equal(
|
||||||
|
building.heating_peak_load[cte.MONTH]['heating peak loads'],
|
||||||
|
expected_monthly['expected'],
|
||||||
|
check_names=False
|
||||||
|
)
|
||||||
|
pd.testing.assert_series_equal(
|
||||||
|
building.cooling_peak_load[cte.MONTH]['cooling peak loads'],
|
||||||
|
expected_monthly['expected'],
|
||||||
|
check_names=False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user