From 22cbede8d50acda958cb1971150bcdbc7595edee Mon Sep 17 00:00:00 2001 From: guille Date: Wed, 8 Feb 2023 13:40:06 -0500 Subject: [PATCH] Added the full functionality to create meb --- hub/city_model_structure/level_of_detail.py | 32 +++++++++++ hub/helpers/constants.py | 4 ++ .../results/insel_monthly_energry_balance.py | 53 +++++++++++++++++++ .../results/simplified_radiosity_algorithm.py | 23 ++++---- hub/imports/results_factory.py | 7 +++ hub/imports/weather/epw_weather_parameters.py | 8 +++ hub/imports/weather/helpers/weather.py | 38 +++++++++++++ hub/persistence/models/city_object.py | 2 +- hub/persistence/models/simulation_results.py | 2 +- hub/persistence/repositories/city.py | 2 +- 10 files changed, 159 insertions(+), 12 deletions(-) create mode 100644 hub/imports/results/insel_monthly_energry_balance.py diff --git a/hub/city_model_structure/level_of_detail.py b/hub/city_model_structure/level_of_detail.py index d673971c..2c8665b9 100644 --- a/hub/city_model_structure/level_of_detail.py +++ b/hub/city_model_structure/level_of_detail.py @@ -14,6 +14,8 @@ class LevelOfDetail: self._geometry = None self._construction = None self._usage = None + self._weather = None + self._surface_radiation = None @property def geometry(self): @@ -59,3 +61,33 @@ class LevelOfDetail: Set the city minimal usage level of detail, 1 or 2 """ self._usage = value + + @property + def weather(self): + """ + Get the city minimal weather level of detail, 0 (yearly), 1 (monthly), 2 (hourly) + :return: int + """ + return self._weather + + @weather.setter + def weather(self, value): + """ + Set the city minimal weather level of detail, 0 (yearly), 1 (monthly), 2 (hourly) + """ + self._usage = value + + @property + def surface_radiation(self): + """ + Get the city minimal surface radiation level of detail, 0 (yearly), 1 (monthly), 2 (hourly) + :return: int + """ + return self._surface_radiation + + @surface_radiation.setter + def surface_radiation(self, value): + """ + Set the city minimal surface radiation level of detail, 0 (yearly), 1 (monthly), 2 (hourly) + """ + self._surface_radiation = value diff --git a/hub/helpers/constants.py b/hub/helpers/constants.py index 479c9dd5..b75d9d2d 100644 --- a/hub/helpers/constants.py +++ b/hub/helpers/constants.py @@ -152,3 +152,7 @@ FULL_HVAC = 'Heating and cooling and ventilation' # Floats MAX_FLOAT = float('inf') MIN_FLOAT = float('-inf') + +# Tools +SRA = 'sra' +INSEL_MEB = 'insel meb' diff --git a/hub/imports/results/insel_monthly_energry_balance.py b/hub/imports/results/insel_monthly_energry_balance.py new file mode 100644 index 00000000..385a8aa8 --- /dev/null +++ b/hub/imports/results/insel_monthly_energry_balance.py @@ -0,0 +1,53 @@ +""" +Insel monthly energy balance +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Concordia CERC group +Project Coder Guillermo.GutierrezMorote@concordia.ca +""" + +from pathlib import Path +import pandas as pd +import csv +import hub.helpers.constants as cte + +class InselMonthlyEnergyBalance: + """ + Import SRA results + """ + def __init__(self, city, base_path): + + self._city = city + self._base_path = base_path + + @staticmethod + def _demand(insel_output_file_path): + heating = [] + cooling = [] + with open(Path(insel_output_file_path).resolve()) as csv_file: + csv_reader = csv.reader(csv_file) + for line in csv_reader: + demand = str(line).replace("['", '').replace("']", '').split() + for i in range(0, 2): + if demand[i] != 'NaN': + aux = float(demand[i]) * 1000 # kWh to Wh + demand[i] = str(aux) + else: + demand[i] = '0' + heating.append(demand[0]) + cooling.append(demand[1]) + monthly_heating = pd.DataFrame(heating, columns=[cte.INSEL_MEB]) + monthly_cooling = pd.DataFrame(cooling, columns=[cte.INSEL_MEB]) + return monthly_heating, monthly_cooling + + def enrich(self): + for building in self._city.buildings: + file_name = building.name + '.out' + insel_output_file_path = Path(self._base_path / file_name).resolve() + if insel_output_file_path.is_file(): + building.heating[cte.MONTH], building.cooling[cte.MONTH] = self._demand(insel_output_file_path) + building.heating[cte.YEAR] = pd.DataFrame( + [building.heating[cte.MONTH][cte.INSEL_MEB].sum()], columns=[cte.INSEL_MEB] + ) + building.cooling[cte.YEAR] = pd.DataFrame( + [building.cooling[cte.MONTH][cte.INSEL_MEB].sum()], columns=[cte.INSEL_MEB] + ) diff --git a/hub/imports/results/simplified_radiosity_algorithm.py b/hub/imports/results/simplified_radiosity_algorithm.py index 251dceaf..334a26b1 100644 --- a/hub/imports/results/simplified_radiosity_algorithm.py +++ b/hub/imports/results/simplified_radiosity_algorithm.py @@ -32,7 +32,7 @@ class SimplifiedRadiosityAlgorithm: array = np.concatenate((array, np.full(total_hours, i + 1))) return pd.DataFrame(array, columns=[cte.MONTH]) - def _get_mean_values(self, values): + def _get_monthly_mean_values(self, values): out = None if values is not None: if cte.MONTH not in values.columns: @@ -41,6 +41,9 @@ class SimplifiedRadiosityAlgorithm: del out[cte.MONTH] return out + def _get_yearly_mean_values(self, values): + return values.mean() + def _read_results(self): try: return pd.read_csv(self._input_file_path, sep='\s+', header=0) @@ -65,11 +68,11 @@ class SimplifiedRadiosityAlgorithm: def enrich(self): """ - saves in building surfaces the correspondent irradiance at different time-scales depending on the mode - if building is None, it saves all buildings' surfaces in file, if building is specified, it saves only that - specific building values - :return: none - """ + saves in building surfaces the correspondent irradiance at different time-scales depending on the mode + if building is None, it saves all buildings' surfaces in file, if building is specified, it saves only that + specific building values + :return: none + """ for radiation in self._radiation: city_object_name = radiation.columns.values.tolist()[1].split(':')[1] building = self._city.city_object(city_object_name) @@ -79,9 +82,8 @@ class SimplifiedRadiosityAlgorithm: header_id = column surface_id = header_id.split(':')[2] surface = building.surface_by_id(surface_id) - new_value = pd.DataFrame(radiation[[header_id]].to_numpy(), columns=['sra']) - - month_new_value = self._get_mean_values(new_value) + new_value = pd.DataFrame(radiation[[header_id]].to_numpy(), columns=[cte.SRA]) + month_new_value = self._get_monthly_mean_values(new_value) if cte.MONTH not in surface.global_irradiance: surface.global_irradiance[cte.MONTH] = month_new_value else: @@ -90,3 +92,6 @@ class SimplifiedRadiosityAlgorithm: surface.global_irradiance[cte.HOUR] = new_value else: pd.concat([surface.global_irradiance[cte.HOUR], new_value], axis=1) + if cte.YEAR not in surface.global_irradiance: + surface.global_irradiance[cte.YEAR] = self._get_yearly_mean_values(new_value) + self._city.level_of_detail.surface_radiation = 2 diff --git a/hub/imports/results_factory.py b/hub/imports/results_factory.py index 0d4db8d3..afa90fb6 100644 --- a/hub/imports/results_factory.py +++ b/hub/imports/results_factory.py @@ -10,6 +10,7 @@ from pathlib import Path from hub.helpers.utils import validate_import_export_type from hub.hub_logger import logger from hub.imports.results.simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm +from hub.imports.results.insel_monthly_energry_balance import InselMonthlyEnergyBalance class ResultFactory: @@ -34,6 +35,12 @@ class ResultFactory: """ SimplifiedRadiosityAlgorithm(self._city, self._base_path).enrich() + def _insel_meb(self): + """ + Enrich the city with insel monthly energy balance results + """ + InselMonthlyEnergyBalance(self._city, self._base_path).enrich() + def enrich(self): """ Enrich the city given to the class using the usage factory given handler diff --git a/hub/imports/weather/epw_weather_parameters.py b/hub/imports/weather/epw_weather_parameters.py index 658e1871..47ddb5fe 100644 --- a/hub/imports/weather/epw_weather_parameters.py +++ b/hub/imports/weather/epw_weather_parameters.py @@ -9,6 +9,7 @@ import sys from pathlib import Path import pandas as pd import hub.helpers.constants as cte +from hub.imports.weather.helpers.weather import Weather as wh class EpwWeatherParameters: @@ -110,3 +111,10 @@ class EpwWeatherParameters: building.beam[cte.HOUR] = new_value else: pd.concat([building.beam[cte.HOUR], new_value], axis=1) + # create the monthly and yearly values out of the hourly + for building in self._city.buildings: + if cte.MONTH not in building.external_temperature: + building.external_temperature[cte.MONTH] = wh().get_monthly_mean_values(building.external_temperature[cte.HOUR][['epw']]) + if cte.YEAR not in building.external_temperature: + building.external_temperature[cte.YEAR] = wh(). get_yearly_mean_values(building.external_temperature[cte.HOUR][['epw']]) + self._city.level_of_detail.weather = 2 diff --git a/hub/imports/weather/helpers/weather.py b/hub/imports/weather/helpers/weather.py index 94769078..ae4c9ad2 100644 --- a/hub/imports/weather/helpers/weather.py +++ b/hub/imports/weather/helpers/weather.py @@ -6,6 +6,9 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ import math import hub.helpers.constants as cte +import pandas as pd +import calendar as cal +import numpy as np class Weather: @@ -28,3 +31,38 @@ class Weather: + 0.32 * (temperature + cte.KELVIN) - cte.KELVIN values.append(value) return values + + def get_monthly_mean_values(self, values): + out = None + if values is not None: + if 'month' not in values.columns: + values = pd.concat([self.month_hour, pd.DataFrame(values)], axis=1) + out = values.groupby('month', as_index=False).mean() + del out['month'] + return out + + def get_yearly_mean_values(self, values): + return values.mean() + + def get_total_month(self, values): + out = None + if values is not None: + if 'month' not in values.columns: + values = pd.concat([self.month_hour, pd.DataFrame(values)], axis=1) + out = pd.DataFrame(values).groupby('month', as_index=False).sum() + del out['month'] + return out + + @property + def month_hour(self): + """ + returns a DataFrame that has x values of the month number (January = 1, February = 2...), + being x the number of hours of the corresponding month + :return: DataFrame(int) + """ + array = [] + for i in range(0, 12): + days_of_month = cal.monthrange(2015, i+1)[1] + total_hours = days_of_month * 24 + array = np.concatenate((array, np.full(total_hours, i + 1))) + return pd.DataFrame(array, columns=['month']) diff --git a/hub/persistence/models/city_object.py b/hub/persistence/models/city_object.py index 0d2a47e7..7229a2ac 100644 --- a/hub/persistence/models/city_object.py +++ b/hub/persistence/models/city_object.py @@ -16,7 +16,7 @@ class CityObject(Models): A model representation of an application """ __tablename__ = 'city_object' - id = Column(Integer, Sequence('application_id_seq'), primary_key=True) + id = Column(Integer, Sequence('city_object_id_seq'), primary_key=True) city_id = Column(Integer, ForeignKey('city.id'), nullable=False) name = Column(String, nullable=False) alias = Column(String, nullable=True) diff --git a/hub/persistence/models/simulation_results.py b/hub/persistence/models/simulation_results.py index 256cbea5..defc3e7e 100644 --- a/hub/persistence/models/simulation_results.py +++ b/hub/persistence/models/simulation_results.py @@ -17,7 +17,7 @@ class SimulationResults(Models): A model representation of an application """ __tablename__ = 'simulation_results' - id = Column(Integer, Sequence('application_id_seq'), primary_key=True) + id = Column(Integer, Sequence('simulation_results_id_seq'), primary_key=True) city_id = Column(Integer, ForeignKey('city.id'), nullable=True) city_object_id = Column(Integer, ForeignKey('city_object.id'), nullable=True) name = Column(String, nullable=False) diff --git a/hub/persistence/repositories/city.py b/hub/persistence/repositories/city.py index 812f83e8..47f36e94 100644 --- a/hub/persistence/repositories/city.py +++ b/hub/persistence/repositories/city.py @@ -47,7 +47,7 @@ class City(Repository): pickle.dumps(city), city.name, city.level_of_detail.geometry, - 'None' if city.climate_file is None else city.climate_file, + 'None' if city.climate_file is None else str(city.climate_file), application_id, user_id, __version__)