From 88e2cb679698f02931e7592c276a5a27fdc9a1fe Mon Sep 17 00:00:00 2001 From: p_monsalvete Date: Mon, 20 Mar 2023 14:15:57 -0400 Subject: [PATCH] introduced dhw to the meb exporter --- hub/city_model_structure/building.py | 17 ++ .../building_demand/thermal_zone.py | 3 + .../building_demand/usage_zone.py | 250 ------------------ .../results/insel_monthly_energry_balance.py | 1 + hub/imports/usage/comnet_usage_parameters.py | 12 +- hub/imports/usage/nrcan_usage_parameters.py | 16 +- hub/imports/usage/usage_helper.py | 43 --- hub/imports/weather/epw_weather_parameters.py | 18 +- hub/imports/weather/helpers/weather.py | 37 ++- 9 files changed, 86 insertions(+), 311 deletions(-) delete mode 100644 hub/city_model_structure/building_demand/usage_zone.py delete mode 100644 hub/imports/usage/usage_helper.py diff --git a/hub/city_model_structure/building.py b/hub/city_model_structure/building.py index d5129142..327cc0c7 100644 --- a/hub/city_model_structure/building.py +++ b/hub/city_model_structure/building.py @@ -38,6 +38,7 @@ class Building(CityObject): self._shell = None self._alias = None self._type = 'building' + self._cold_water_temperature = dict() self._heating = dict() self._cooling = dict() self._lighting_electrical_demand = dict() @@ -265,6 +266,22 @@ class Building(CityObject): if value is not None: self._storeys_above_ground = int(value) + @property + def cold_water_temperature(self) -> {float}: + """ + Get cold water temperature in degrees Celsius + :return: dict{DataFrame(float)} + """ + return self._cold_water_temperature + + @cold_water_temperature.setter + def cold_water_temperature(self, value): + """ + Set cold water temperature in degrees Celsius + :param value: dict{DataFrame(float)} + """ + self._cold_water_temperature = value + @property def heating(self) -> dict: """ diff --git a/hub/city_model_structure/building_demand/thermal_zone.py b/hub/city_model_structure/building_demand/thermal_zone.py index 667c0631..65b40b29 100644 --- a/hub/city_model_structure/building_demand/thermal_zone.py +++ b/hub/city_model_structure/building_demand/thermal_zone.py @@ -600,11 +600,14 @@ class ThermalZone: """ self._domestic_hot_water = DomesticHotWater() _mean_peak_density_load = 0 + _mean_peak_flow = 0 _mean_service_temperature = 0 for usage in self.usages: _mean_peak_density_load += usage.percentage * usage.domestic_hot_water.density + _mean_peak_flow += usage.percentage * usage.domestic_hot_water.peak_flow _mean_service_temperature += usage.percentage * usage.domestic_hot_water.service_temperature self._domestic_hot_water.density = _mean_peak_density_load + self._domestic_hot_water.peak_flow = _mean_peak_flow self._domestic_hot_water.service_temperature = _mean_service_temperature _domestic_hot_water_reference = self.usages[0].domestic_hot_water diff --git a/hub/city_model_structure/building_demand/usage_zone.py b/hub/city_model_structure/building_demand/usage_zone.py deleted file mode 100644 index 6357cff8..00000000 --- a/hub/city_model_structure/building_demand/usage_zone.py +++ /dev/null @@ -1,250 +0,0 @@ -""" -UsageZone module -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Concordia CERC group -Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca -Code contributors: Guille Gutierrez guillermo.gutierrezmorote@concordia.ca -""" -import uuid -from typing import Union, List -import hub.helpers.constants as cte -from hub.city_model_structure.building_demand.occupancy import Occupancy -from hub.city_model_structure.building_demand.lighting import Lighting -from hub.city_model_structure.building_demand.appliances import Appliances -from hub.city_model_structure.building_demand.thermal_control import ThermalControl -from hub.city_model_structure.building_demand.internal_gain import InternalGain - - -class UsageZone: - """ - UsageZone class - """ - def __init__(self): - self._id = None - self._usage = None - self._percentage = None - self._internal_gains = None - self._hours_day = None - self._days_year = None - self._mechanical_air_change = None - self._occupancy = None - self._lighting = None - self._appliances = None - self._thermal_control = None - - @property - def id(self): - """ - Get usage zone id, a universally unique identifier randomly generated - :return: str - """ - if self._id is None: - self._id = uuid.uuid4() - return self._id - - @property - def usage(self) -> Union[None, str]: - """ - Get usage zone usage - :return: None or str - """ - return self._usage - - @usage.setter - def usage(self, value): - """ - Set usage zone usage - :param value: str - """ - if value is not None: - self._usage = str(value) - - @property - def percentage(self): - """ - Get usage zone percentage in range[0,1] - :return: float - """ - return self._percentage - - @percentage.setter - def percentage(self, value): - """ - Set usage zone percentage in range[0,1] - :param value: float - """ - if value is not None: - self._percentage = float(value) - - @property - def internal_gains(self) -> List[InternalGain]: - """ - Calculates and returns the list of all internal gains defined - :return: InternalGains - """ - if self._internal_gains is None: - if self.occupancy is not None: - if self.occupancy.latent_internal_gain is not None: - _internal_gain = InternalGain() - _internal_gain.type = cte.OCCUPANCY - _total_heat_gain = (self.occupancy.sensible_convective_internal_gain - + self.occupancy.sensible_radiative_internal_gain - + self.occupancy.latent_internal_gain) - _internal_gain.average_internal_gain = _total_heat_gain - _internal_gain.latent_fraction = 0 - _internal_gain.radiative_fraction = 0 - _internal_gain.convective_fraction = 0 - if _total_heat_gain != 0: - _internal_gain.latent_fraction = self.occupancy.latent_internal_gain / _total_heat_gain - _internal_gain.radiative_fraction = self.occupancy.sensible_radiative_internal_gain / _total_heat_gain - _internal_gain.convective_fraction = self.occupancy.sensible_convective_internal_gain / _total_heat_gain - _internal_gain.schedules = self.occupancy.occupancy_schedules - self._internal_gains = [_internal_gain] - if self.lighting is not None: - _internal_gain = InternalGain() - _internal_gain.type = cte.LIGHTING - _internal_gain.average_internal_gain = self.lighting.density - _internal_gain.latent_fraction = self.lighting.latent_fraction - _internal_gain.radiative_fraction = self.lighting.radiative_fraction - _internal_gain.convective_fraction = self.lighting.convective_fraction - _internal_gain.schedules = self.lighting.schedules - if self._internal_gains is not None: - self._internal_gains.append(_internal_gain) - else: - self._internal_gains = [_internal_gain] - if self.appliances is not None: - _internal_gain = InternalGain() - _internal_gain.type = cte.APPLIANCES - _internal_gain.average_internal_gain = self.appliances.density - _internal_gain.latent_fraction = self.appliances.latent_fraction - _internal_gain.radiative_fraction = self.appliances.radiative_fraction - _internal_gain.convective_fraction = self.appliances.convective_fraction - _internal_gain.schedules = self.appliances.schedules - if self._internal_gains is not None: - self._internal_gains.append(_internal_gain) - else: - self._internal_gains = [_internal_gain] - return self._internal_gains - - @internal_gains.setter - def internal_gains(self, value): - """ - Set usage zone internal gains - :param value: [InternalGain] - """ - self._internal_gains = value - - @property - def hours_day(self) -> Union[None, float]: - """ - Get usage zone usage hours per day - :return: None or float - """ - return self._hours_day - - @hours_day.setter - def hours_day(self, value): - """ - Set usage zone usage hours per day - :param value: float - """ - if value is not None: - self._hours_day = float(value) - - @property - def days_year(self) -> Union[None, float]: - """ - Get usage zone usage days per year - :return: None or float - """ - return self._days_year - - @days_year.setter - def days_year(self, value): - """ - Set usage zone usage days per year - :param value: float - """ - if value is not None: - self._days_year = float(value) - - @property - def mechanical_air_change(self) -> Union[None, float]: - """ - Get usage zone mechanical air change in air change per hour (ACH) - :return: None or float - """ - return self._mechanical_air_change - - @mechanical_air_change.setter - def mechanical_air_change(self, value): - """ - Set usage zone mechanical air change in air change per hour (ACH) - :param value: float - """ - if value is not None: - self._mechanical_air_change = float(value) - - @property - def occupancy(self) -> Union[None, Occupancy]: - """ - Get occupancy in the usage zone - :return: None or Occupancy - """ - return self._occupancy - - @occupancy.setter - def occupancy(self, value): - """ - Set occupancy in the usage zone - :param value: Occupancy - """ - self._occupancy = value - - @property - def lighting(self) -> Union[None, Lighting]: - """ - Get lighting information - :return: None or Lighting - """ - return self._lighting - - @lighting.setter - def lighting(self, value): - """ - Set lighting information - :param value: Lighting - """ - self._lighting = value - - @property - def appliances(self) -> Union[None, Appliances]: - """ - Get appliances information - :return: None or Appliances - """ - return self._appliances - - @appliances.setter - def appliances(self, value): - """ - Set appliances information - :param value: Appliances - """ - self._appliances = value - - @property - def thermal_control(self) -> Union[None, ThermalControl]: - """ - Get thermal control of this thermal zone - :return: None or ThermalControl - """ - return self._thermal_control - - @thermal_control.setter - def thermal_control(self, value): - """ - Set thermal control for this thermal zone - :param value: ThermalControl - """ - self._thermal_control = value diff --git a/hub/imports/results/insel_monthly_energry_balance.py b/hub/imports/results/insel_monthly_energry_balance.py index 50b5f34d..3ded75d2 100644 --- a/hub/imports/results/insel_monthly_energry_balance.py +++ b/hub/imports/results/insel_monthly_energry_balance.py @@ -10,6 +10,7 @@ import pandas as pd import csv import hub.helpers.constants as cte + class InselMonthlyEnergyBalance: """ Import SRA results diff --git a/hub/imports/usage/comnet_usage_parameters.py b/hub/imports/usage/comnet_usage_parameters.py index 9e1836d2..163bd3fe 100644 --- a/hub/imports/usage/comnet_usage_parameters.py +++ b/hub/imports/usage/comnet_usage_parameters.py @@ -8,6 +8,7 @@ import copy import sys import numpy +from hub.hub_logger import logger import hub.helpers.constants as cte from hub.helpers.dictionaries import Dictionaries from hub.city_model_structure.building_demand.usage import Usage @@ -19,7 +20,6 @@ from hub.city_model_structure.building_demand.domestic_hot_water import Domestic from hub.city_model_structure.attributes.schedule import Schedule from hub.city_model_structure.building_demand.internal_gain import InternalGain from hub.catalog_factories.usage_catalog_factory import UsageCatalogFactory -from hub.imports.usage.usage_helper import UsageHelper class ComnetUsageParameters: @@ -42,8 +42,8 @@ class ComnetUsageParameters: try: archetype_usage = self._search_archetypes(comnet_catalog, usage_name) except KeyError: - sys.stderr.write(f'Building {building.name} has unknown usage archetype for building function:' - f' {building.function}') + logger.error(f'Building {building.name} has unknown usage archetype for usage: {usage_name}') + sys.stderr.write(f'Building {building.name} has unknown usage archetype for usage: {usage_name}') continue for internal_zone in building.internal_zones: @@ -56,7 +56,7 @@ class ComnetUsageParameters: volume_per_area = internal_zone.volume / internal_zone.area usage = Usage() usage.name = usage_name - self._assign_values(usage, archetype_usage, volume_per_area, building.ground_temperature) + self._assign_values(usage, archetype_usage, volume_per_area, building.cold_water_temperature) usage.percentage = 1 self._calculate_reduced_values_from_extended_library(usage, archetype_usage) @@ -71,7 +71,7 @@ class ComnetUsageParameters: raise KeyError('archetype not found') @staticmethod - def _assign_values(usage, archetype, volume_per_area, ground_temperature): + def _assign_values(usage, archetype, volume_per_area, cold_water_temperature): # Due to the fact that python is not a typed language, the wrong object type is assigned to # usage.occupancy when writing usage.occupancy = archetype.occupancy. # Same happens for lighting and appliances. Therefore, this walk around has been done. @@ -106,7 +106,7 @@ class ComnetUsageParameters: _domestic_hot_water = DomesticHotWater() _domestic_hot_water.density = archetype.domestic_hot_water.density _domestic_hot_water.service_temperature = archetype.domestic_hot_water.service_temperature - cold_temperature = UsageHelper().cold_water_temperature(ground_temperature) + cold_temperature = cold_water_temperature[cte.YEAR]['epw'] peak_flow = 0 if (archetype.domestic_hot_water.service_temperature - cold_temperature) > 0: peak_flow = archetype.domestic_hot_water.density / cte.WATER_DENSITY / cte.WATER_HEAT_CAPACITY \ diff --git a/hub/imports/usage/nrcan_usage_parameters.py b/hub/imports/usage/nrcan_usage_parameters.py index ff008709..67a5e380 100644 --- a/hub/imports/usage/nrcan_usage_parameters.py +++ b/hub/imports/usage/nrcan_usage_parameters.py @@ -7,6 +7,7 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca import sys +from hub.hub_logger import logger import hub.helpers.constants as cte from hub.helpers.dictionaries import Dictionaries from hub.city_model_structure.building_demand.usage import Usage @@ -16,7 +17,6 @@ from hub.city_model_structure.building_demand.appliances import Appliances from hub.city_model_structure.building_demand.thermal_control import ThermalControl from hub.city_model_structure.building_demand.domestic_hot_water import DomesticHotWater from hub.catalog_factories.usage_catalog_factory import UsageCatalogFactory -from hub.imports.usage.usage_helper import UsageHelper class NrcanUsageParameters: @@ -41,16 +41,16 @@ class NrcanUsageParameters: try: archetype_usage = self._search_archetypes(nrcan_catalog, usage_name) except KeyError: - sys.stderr.write(f'Building {building.name} has unknown usage archetype for building function:' - f' {building.function}') + logger.error(f'Building {building.name} has unknown usage archetype for usage: {usage_name}\n') + sys.stderr.write(f'Building {building.name} has unknown usage archetype for usage: {usage_name}\n') continue usage_name = Dictionaries().hub_usage_to_comnet_usage[building.function] try: comnet_archetype_usage = self._search_archetypes(comnet_catalog, usage_name) except KeyError: - sys.stderr.write(f'Building {building.name} has unknown usage archetype for building function:' - f' {building.function}') + logger.error(f'Building {building.name} has unknown usage archetype for usage: {usage_name}\n') + sys.stderr.write(f'Building {building.name} has unknown usage archetype for usage: {usage_name}\n') continue for internal_zone in building.internal_zones: @@ -63,7 +63,7 @@ class NrcanUsageParameters: volume_per_area = internal_zone.volume / internal_zone.area usage = Usage() usage.name = usage_name - self._assign_values(usage, archetype_usage, volume_per_area, building.ground_temperature) + self._assign_values(usage, archetype_usage, volume_per_area, building.cold_water_temperature) self._assign_comnet_extra_values(usage, comnet_archetype_usage) usage.percentage = 1 self._calculate_reduced_values_from_extended_library(usage, archetype_usage) @@ -79,7 +79,7 @@ class NrcanUsageParameters: raise KeyError('archetype not found') @staticmethod - def _assign_values(usage, archetype, volume_per_area, ground_temperature): + def _assign_values(usage, archetype, volume_per_area, cold_water_temperature): if archetype.mechanical_air_change > 0: usage.mechanical_air_change = archetype.mechanical_air_change elif archetype.ventilation_rate > 0: @@ -116,7 +116,7 @@ class NrcanUsageParameters: _domestic_hot_water = DomesticHotWater() _domestic_hot_water.peak_flow = archetype.domestic_hot_water.peak_flow _domestic_hot_water.service_temperature = archetype.domestic_hot_water.service_temperature - cold_temperature = UsageHelper().cold_water_temperature(ground_temperature) + cold_temperature = cold_water_temperature[cte.YEAR]['epw'] _domestic_hot_water.density = archetype.domestic_hot_water.peak_flow * cte.WATER_DENSITY * cte.WATER_HEAT_CAPACITY \ * (archetype.domestic_hot_water.service_temperature - cold_temperature) _domestic_hot_water.schedules = archetype.domestic_hot_water.schedules diff --git a/hub/imports/usage/usage_helper.py b/hub/imports/usage/usage_helper.py deleted file mode 100644 index 2d6ac514..00000000 --- a/hub/imports/usage/usage_helper.py +++ /dev/null @@ -1,43 +0,0 @@ -""" -Usage helper -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 sys -from hub.hub_logger import logger -import hub.helpers.constants as cte -from hub.helpers.configuration_helper import ConfigurationHelper as ch - - -class UsageHelper: - """ - UsageHelper class - """ - - @staticmethod - def cold_water_temperature(ground_temperature): - keys = ground_temperature.keys() - depths = ground_temperature[keys[0]].keys() - if keys[0] == cte.YEAR: - # taking the deeper temperature available - _cold_temperature = ground_temperature[cte.YEAR][depths[len(depths-1)]] - elif keys[0] == cte.MONTH: - _cold_temperature = 0 - for i in range(0, 12): - # taking the deeper temperature available - _cold_temperature += ground_temperature[cte.MONTH][depths[len(depths-1)]] / 12 - elif keys[0] == cte.HOUR: - _cold_temperature = 0 - for i in range(0, 8760): - # taking the deeper temperature available - _cold_temperature += ground_temperature[cte.HOUR][depths[len(depths-1)]] / 8760 - else: - _cold_temperature = ch().cold_water_temperature - logger.error(f'Cold water temperature could not be calculated. Assigned default value = ' - f'{ch().cold_water_temperature} degrees Celsius\n') - sys.stderr.write(f'Cold water temperature could not be calculated. Assigned default value = ' - f'{ch().cold_water_temperature} degrees Celsius\n') - - return _cold_temperature diff --git a/hub/imports/weather/epw_weather_parameters.py b/hub/imports/weather/epw_weather_parameters.py index 2b53bc24..89315ab0 100644 --- a/hub/imports/weather/epw_weather_parameters.py +++ b/hub/imports/weather/epw_weather_parameters.py @@ -111,10 +111,24 @@ class EpwWeatherParameters: building.beam[cte.HOUR] = new_value else: pd.concat([building.beam[cte.HOUR], new_value], axis=1) + + new_value = wh().cold_water_temperature(building.external_temperature[cte.HOUR]['epw']) + if cte.HOUR not in building.cold_water_temperature: + building.cold_water_temperature[cte.HOUR] = new_value + else: + pd.concat([building.cold_water_temperature[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']]) + 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']]) + building.external_temperature[cte.YEAR] = \ + wh(). get_yearly_mean_values(building.external_temperature[cte.HOUR][['epw']]) + if cte.MONTH not in building.cold_water_temperature: + building.cold_water_temperature[cte.MONTH] = wh().get_monthly_mean_values( + building.cold_water_temperature[cte.HOUR][['epw']]) + if cte.YEAR not in building.cold_water_temperature: + building.cold_water_temperature[cte.YEAR] = wh().get_yearly_mean_values( + building.cold_water_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 ae4c9ad2..f595f41e 100644 --- a/hub/imports/weather/helpers/weather.py +++ b/hub/imports/weather/helpers/weather.py @@ -4,6 +4,7 @@ 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 math import hub.helpers.constants as cte import pandas as pd @@ -20,7 +21,7 @@ class Weather: def sky_temperature(ambient_temperature): """ Get sky temperature from ambient temperature in Celsius - :return: float + :return: List[float] """ # Swinbank - Source sky model approximation(1963) based on cloudiness statistics(32 %) in United States # ambient temperatures( in °C) @@ -32,6 +33,37 @@ class Weather: values.append(value) return values + @staticmethod + def cold_water_temperature(ambient_temperature): + """ + Get cold water temperature from ambient temperature in Celsius + :return: dict + """ + # Equation from "TOWARDS DEVELOPMENT OF AN ALGORITHM FOR MAINS WATER TEMPERATURE", 2004, Jay Burch + # and Craig Christensen, National Renewable Energy Laboratory + # ambient temperatures( in °C) + # cold water temperatures( in °C) + ambient_temperature_fahrenheit = [] + average_temperature = 0 + maximum_temperature = -1000 + minimum_temperature = 1000 + for temperature in ambient_temperature: + value = temperature * 9 / 5 + 32 + ambient_temperature_fahrenheit.append(value) + average_temperature += value / 8760 + if value > maximum_temperature: + maximum_temperature = value + if value < minimum_temperature: + minimum_temperature = value + delta_temperature = maximum_temperature - minimum_temperature + ratio = 0.4 + 0.01 * (average_temperature - 44) + lag = 35 - 1 * (average_temperature - 44) + cold_temperature = [] + for temperature in ambient_temperature_fahrenheit: + radians = (0.986 * (temperature-15-lag) - 90) * math.pi / 180 + cold_temperature.append((average_temperature + 6 + ratio * (delta_temperature/2) * math.sin(radians) - 32) * 5/9) + return pd.DataFrame(cold_temperature, columns=['epw']) + def get_monthly_mean_values(self, values): out = None if values is not None: @@ -41,7 +73,8 @@ class Weather: del out['month'] return out - def get_yearly_mean_values(self, values): + @staticmethod + def get_yearly_mean_values(values): return values.mean() def get_total_month(self, values):