diff --git a/hub/catalog_factories/data_models/usages/domestic_hot_water.py b/hub/catalog_factories/data_models/usages/domestic_hot_water.py index edf4b8ab..a34a4c65 100644 --- a/hub/catalog_factories/data_models/usages/domestic_hot_water.py +++ b/hub/catalog_factories/data_models/usages/domestic_hot_water.py @@ -14,8 +14,9 @@ class DomesticHotWater: """ DomesticHotWater class """ - def __init__(self, density, service_temperature, schedules): + def __init__(self, density, peak_flow, service_temperature, schedules): self._density = density + self._peak_flow = peak_flow self._service_temperature = service_temperature self._schedules = schedules @@ -27,6 +28,14 @@ class DomesticHotWater: """ return self._density + @property + def peak_flow(self) -> Union[None, float]: + """ + Get domestic hot water peak_flow density in m3 per second and m2 + :return: None or float + """ + return self._peak_flow + @property def service_temperature(self) -> Union[None, float]: """ diff --git a/hub/catalog_factories/usage/comnet_catalog.py b/hub/catalog_factories/usage/comnet_catalog.py index 2c267477..7c226722 100644 --- a/hub/catalog_factories/usage/comnet_catalog.py +++ b/hub/catalog_factories/usage/comnet_catalog.py @@ -104,6 +104,7 @@ class ComnetCatalog(Catalog): density = float(density) * cte.BTU_H_TO_WATTS * occupancy_density domestic_hot_water_service_temperature = self._schedules[schedule_name]['WtrHtrSetPt'][0].values[0] domestic_hot_water = DomesticHotWater(density, + None, domestic_hot_water_service_temperature, self._schedules[schedule_name]['Service Hot Water'] ) diff --git a/hub/catalog_factories/usage/nrcan_catalog.py b/hub/catalog_factories/usage/nrcan_catalog.py index a7e8dda9..0f60dc68 100644 --- a/hub/catalog_factories/usage/nrcan_catalog.py +++ b/hub/catalog_factories/usage/nrcan_catalog.py @@ -79,10 +79,8 @@ class NrcanCatalog(Catalog): url = f'{self._base_url}{name["space_types_location"]}' with urllib.request.urlopen(url) as json_file: space_types = json.load(json_file)['tables']['space_types']['table'] -# space_types = [st for st in space_types if st['building_type'] == 'Space Function'] space_types = [st for st in space_types if st['space_type'] == 'WholeBuilding'] for space_type in space_types: -# usage_type = space_type['space_type'] usage_type = space_type['building_type'] occupancy_schedule_name = space_type['occupancy_schedule'] lighting_schedule_name = space_type['lighting_schedule'] @@ -127,14 +125,10 @@ class NrcanCatalog(Catalog): if appliances_radiative_fraction is not None and appliances_latent_fraction is not None: appliances_convective_fraction = 1 - appliances_radiative_fraction - appliances_latent_fraction - # peak flow in m3/day/m2 - domestic_hot_water_peak_flow = space_type['service_water_heating_peak_flow_per_area'] + # peak flow in gallons/h/ft2 + domestic_hot_water_peak_flow = space_type['service_water_heating_peak_flow_per_area'] \ + * cte.GALLONS_TO_QUBIC_METERS / cte.HOUR_TO_SECONDS * pow(cte.METERS_TO_FEET, 2) domestic_hot_water_service_temperature = space_type['service_water_heating_target_temperature'] - average_domestic_hot_water_inlet_temperature = 16.5 - # result in W/m2 - domestic_hot_water_density = domestic_hot_water_peak_flow / 24 / 3.6 * 4184 \ - * (domestic_hot_water_service_temperature - - average_domestic_hot_water_inlet_temperature) occupancy = Occupancy(occupancy_density, None, @@ -157,7 +151,8 @@ class NrcanCatalog(Catalog): hvac_availability, heating_schedule, cooling_schedule) - domestic_hot_water = DomesticHotWater(domestic_hot_water_density, + domestic_hot_water = DomesticHotWater(None, + domestic_hot_water_peak_flow, domestic_hot_water_service_temperature, domestic_hot_water_load_schedule) diff --git a/hub/city_model_structure/building_demand/domestic_hot_water.py b/hub/city_model_structure/building_demand/domestic_hot_water.py index f48bf68a..d91977e5 100644 --- a/hub/city_model_structure/building_demand/domestic_hot_water.py +++ b/hub/city_model_structure/building_demand/domestic_hot_water.py @@ -14,6 +14,7 @@ class DomesticHotWater: """ def __init__(self): self._density = None + self._peak_flow = None self._service_temperature = None self._schedules = None @@ -34,6 +35,22 @@ class DomesticHotWater: if value is not None: self._density = float(value) + @property + def peak_flow(self) -> Union[None, float]: + """ + Get domestic hot water peak_flow density in m3 per second and m2 + :return: None or float + """ + return self._peak_flow + + @peak_flow.setter + def peak_flow(self, value): + """ + Set domestic hot water peak_flow density in m3 per second and m2 + :return: None or float + """ + self._peak_flow = value + @property def service_temperature(self) -> Union[None, float]: """ diff --git a/hub/city_model_structure/city_object.py b/hub/city_model_structure/city_object.py index ab50250c..bf95b040 100644 --- a/hub/city_model_structure/city_object.py +++ b/hub/city_model_structure/city_object.py @@ -35,6 +35,7 @@ class CityObject: self._max_z = ConfigurationHelper().min_coordinate self._centroid = None self._external_temperature = dict() + self._ground_temperature = dict() self._global_horizontal = dict() self._diffuse = dict() self._beam = dict() @@ -158,6 +159,24 @@ class CityObject: """ self._external_temperature = value + # todo: this is the new format we will use to get rid of the data frames + @property + def ground_temperature(self) -> dict: + """ + Get ground temperature under the city object in Celsius at different depths in meters for different time steps + example of use: {month: {0.5: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10]}} + :return: dict{dict{[float]}} + """ + return self._ground_temperature + + @ground_temperature.setter + def ground_temperature(self, value): + """ + Set ground temperature under the city object in Celsius at different depths + :param value: dict{dict{[float]}} + """ + self._ground_temperature = value + @property def global_horizontal(self) -> dict: """ diff --git a/hub/config/configuration.ini b/hub/config/configuration.ini index 58bf8d87..24d01eea 100644 --- a/hub/config/configuration.ini +++ b/hub/config/configuration.ini @@ -17,4 +17,6 @@ convective_heat_transfer_coefficient_exterior = 20 #W/mK soil_conductivity = 3 #m -soil_thickness = 0.5 \ No newline at end of file +soil_thickness = 0.5 +#C +cold_water_temperature = 10 \ No newline at end of file diff --git a/hub/helpers/configuration_helper.py b/hub/helpers/configuration_helper.py index 88cc5671..d9bf5372 100644 --- a/hub/helpers/configuration_helper.py +++ b/hub/helpers/configuration_helper.py @@ -138,3 +138,11 @@ class ConfigurationHelper: :return: 0.5 """ return self._config.getfloat('buildings', 'soil_thickness').real + + @property + def cold_water_temperature(self) -> float: + """ + Get configured cold water temperature in Celsius + :return: 10 + """ + return self._config.getfloat('buildings', 'cold_water_temperature').real diff --git a/hub/helpers/constants.py b/hub/helpers/constants.py index 2caaee0d..2a20c263 100644 --- a/hub/helpers/constants.py +++ b/hub/helpers/constants.py @@ -8,13 +8,17 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca # universal constants KELVIN = 273.15 +WATER_DENSITY = 1000 +WATER_HEAT_CAPACITY = 4182 # converters HOUR_TO_MINUTES = 60 MINUTES_TO_SECONDS = 60 +HOUR_TO_SECONDS = 3600 METERS_TO_FEET = 3.28084 BTU_H_TO_WATTS = 0.29307107 KILO_WATTS_HOUR_TO_JULES = 3600000 +GALLONS_TO_QUBIC_METERS = 0.0037854117954011185 # time SECOND = 'second' diff --git a/hub/imports/usage/comnet_usage_parameters.py b/hub/imports/usage/comnet_usage_parameters.py index 2747c1af..62fcb2c9 100644 --- a/hub/imports/usage/comnet_usage_parameters.py +++ b/hub/imports/usage/comnet_usage_parameters.py @@ -19,6 +19,7 @@ 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: @@ -55,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) + self._assign_values(usage, archetype_usage, volume_per_area, building.ground_temperature) usage.percentage = 1 self._calculate_reduced_values_from_extended_library(usage, archetype_usage) @@ -70,7 +71,7 @@ class ComnetUsageParameters: raise KeyError('archetype not found') @staticmethod - def _assign_values(usage, archetype, volume_per_area): + def _assign_values(usage, archetype, volume_per_area, ground_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. @@ -105,6 +106,12 @@ 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) + 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 \ + / (archetype.domestic_hot_water.service_temperature - cold_temperature) + _domestic_hot_water.peak_flow = peak_flow _domestic_hot_water.schedules = archetype.domestic_hot_water.schedules usage.domestic_hot_water = _domestic_hot_water diff --git a/hub/imports/usage/nrcan_usage_parameters.py b/hub/imports/usage/nrcan_usage_parameters.py index bc80487b..08520b89 100644 --- a/hub/imports/usage/nrcan_usage_parameters.py +++ b/hub/imports/usage/nrcan_usage_parameters.py @@ -16,6 +16,7 @@ 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: @@ -62,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) + self._assign_values(usage, archetype_usage, volume_per_area, building.ground_temperature) self._assign_comnet_extra_values(usage, comnet_archetype_usage) usage.percentage = 1 self._calculate_reduced_values_from_extended_library(usage, archetype_usage) @@ -78,7 +79,7 @@ class NrcanUsageParameters: raise KeyError('archetype not found') @staticmethod - def _assign_values(usage, archetype, volume_per_area): + def _assign_values(usage, archetype, volume_per_area, ground_temperature): if archetype.mechanical_air_change > 0: usage.mechanical_air_change = archetype.mechanical_air_change elif archetype.ventilation_rate > 0: @@ -113,8 +114,11 @@ class NrcanUsageParameters: _control.hvac_availability_schedules = archetype.thermal_control.hvac_availability_schedules usage.thermal_control = _control _domestic_hot_water = DomesticHotWater() - _domestic_hot_water.density = archetype.domestic_hot_water.density + _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) + _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 usage.domestic_hot_water = _domestic_hot_water diff --git a/hub/imports/usage/usage_helper.py b/hub/imports/usage/usage_helper.py new file mode 100644 index 00000000..2d6ac514 --- /dev/null +++ b/hub/imports/usage/usage_helper.py @@ -0,0 +1,43 @@ +""" +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 47ddb5fe..2b53bc24 100644 --- a/hub/imports/weather/epw_weather_parameters.py +++ b/hub/imports/weather/epw_weather_parameters.py @@ -32,14 +32,13 @@ class EpwWeatherParameters: _ = file.readline().split(',') line = file.readline().split(',') number_records = int(line[1]) - depth_measurement_ground_temperature = [] - ground_temperature = [] + ground_temperature = {} for i in range(0, number_records): - depth_measurement_ground_temperature.append(line[i*16+2]) + depth_measurement_ground_temperature = line[i*16+2] temperatures = [] for j in range(0, 12): temperatures.append(line[i*16+j+6]) - ground_temperature.append(temperatures) + ground_temperature[depth_measurement_ground_temperature] = temperatures file.close() except SystemExit: sys.stderr.write(f'Error: weather file {self._path} not found. Please download it from ' @@ -74,6 +73,7 @@ class EpwWeatherParameters: sys.exit() for building in self._city.buildings: + building.ground_temperature[cte.MONTH] = ground_temperature if cte.HOUR in building.external_temperature: del building.external_temperature[cte.HOUR] new_value = pd.DataFrame(self._weather_values[['dry_bulb_temperature_c']].to_numpy(), columns=['epw'])