diff --git a/city_model_structure/attributes/occupancy.py b/city_model_structure/attributes/occupancy.py index 6560e302..57ccaa3e 100644 --- a/city_model_structure/attributes/occupancy.py +++ b/city_model_structure/attributes/occupancy.py @@ -2,33 +2,35 @@ Building module SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2020 Project Author Sanam Dabirian sanam.dabirian@mail.concordia.ca +Contributors Pilar Monsalvete pilar_monsalvete@yahoo.es """ +import calendar as cal + class Occupancy: """ Occupancy class """ - def __init__(self, internal_heat_gain, heat_dissipation, occupant_rate, occupant_type, occupant_zone, - number_of_occupants, arrival_time=None, departure_time=None, break_time=None, day_of_week=None, - pd_of_meetings_duration=None, occupant_schedule=None): + def __init__(self): """ Constructor """ - self._internal_heat_gain = internal_heat_gain - self._heat_dissipation = heat_dissipation - self._occupant_rate = occupant_rate - self._occupant_type = occupant_type - self._occupant_zone = occupant_zone - self._occupant_schedule = occupant_schedule - self._number_of_occupants = number_of_occupants - self._arrival_time = arrival_time - self._departure_time = departure_time - self._break_time = break_time - self._day_of_week = day_of_week - self._pd_of_meetings_duration = pd_of_meetings_duration + self._internal_heat_gain = None + self._heat_dissipation = None + self._occupant_rate = None + self._occupant_type = None + self._occupant_zone = None + self._occupant_schedule = None + self._number_of_occupants = None + self._arrival_time = None + self._departure_time = None + self._break_time = None + self._day_of_week = None + self._pd_of_meetings_duration = None + self._complete_year_schedule = None @property def internal_heat_gain(self): @@ -125,3 +127,23 @@ class Occupancy: :return: probability distribution of the meeting duration """ return self._pd_of_meetings_duration + + def get_complete_year_schedule(self, schedules): + if self._complete_year_schedule is None: + self._complete_year_schedule = [] + for i in range(1, 13): + month_range = cal.monthrange(2015, i) + for day in range(1, month_range[1]+1): + if cal.weekday(2015, i, day) < 5: + for j in range(0, 24): + week_schedule = schedules['WD'][j] + self._complete_year_schedule.append(week_schedule) + elif cal.weekday(2015, i, day) == 5: + for j in range(0, 24): + week_schedule = schedules['Sat'][j] + self._complete_year_schedule.append(week_schedule) + else: + for j in range(0, 24): + week_schedule = schedules['Sun'][j] + self._complete_year_schedule.append(week_schedule) + return self._complete_year_schedule diff --git a/city_model_structure/attributes/usage_zone.py b/city_model_structure/attributes/usage_zone.py index e82f9be0..6bcb07e2 100644 --- a/city_model_structure/attributes/usage_zone.py +++ b/city_model_structure/attributes/usage_zone.py @@ -2,6 +2,7 @@ UsageZone module SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca +Contributors Pilar Monsalvete pilar_monsalvete@yahoo.es """ from typing import List @@ -21,10 +22,11 @@ class UsageZone: self._cooling_setpoint = None self._hours_day = None self._days_year = None - # todo: this must come from library, talk to Rabeeh + # todo: mechanical_air_change must come from library, talk to Rabeeh self._mechanical_air_change = ConfigurationHelper().min_air_change self._occupancy = None self._schedules = None + self._heating_schedule = None @property def internal_gains(self) -> List[InternalGains]: @@ -193,3 +195,19 @@ class UsageZone: :param value: Sunday schedules """ self._schedules = value + + @property + def heating_schedule(self): + """ + Get heating schedule + :return: dict{DtaFrame(int)} + """ + return self._heating_schedule + + @heating_schedule.setter + def heating_schedule(self, values): + """ + Get heating schedule + :param values: dict{DtaFrame(int)} + """ + self._heating_schedule = values diff --git a/city_model_structure/monthly_to_hourly_demand.py b/city_model_structure/monthly_to_hourly_demand.py index 13160b50..b52da7a1 100644 --- a/city_model_structure/monthly_to_hourly_demand.py +++ b/city_model_structure/monthly_to_hourly_demand.py @@ -5,16 +5,18 @@ Copyright © 2020 Project Author Pilar Monsalvete pilar_monsalvete@yahoo.es """ import pandas as pd import helpers.constants as cte - +from city_model_structure.attributes.occupancy import Occupancy +import calendar as cal class MonthlyToHourlyDemand: """ MonthlyToHourlyDemand class """ - def __init__(self, building): + def __init__(self, building, conditioning_seasons): self._hourly_heating = pd.DataFrame() self._hourly_cooling = pd.DataFrame() self._building = building + self._conditioning_seasons = conditioning_seasons @property def hourly_heating(self): @@ -23,38 +25,45 @@ class MonthlyToHourlyDemand: :return: [hourly_heating] """ # todo: this method and the insel model have to be reviewed for more than one thermal zone - external_temp = self._building.hourly_external_temperature + external_temp = self._building.external_temperature['hour'] # todo: review index depending on how the schedules are defined, either 8760 or 24 hours - period = 'day' for usage_zone in self._building.usage_zones: - temp_set = usage_zone.heating_setpoint - temp_back = usage_zone.heating_setback - occupancy = usage_zone.occupancy.occupant_schedule(period) - # todo: heating_schedule is still missing - heating_schedule = usage_zone.heating_schedule_month + temp_set = float(usage_zone.heating_setpoint) + temp_back = float(usage_zone.heating_setback) + occupancy = Occupancy().get_complete_year_schedule(usage_zone.schedules['Occupancy']) + heating_schedule = self._conditioning_seasons['heating'] - self._hourly_heating = pd.DataFrame(columns=['monthly to hourly']) + hourly_heating = [] i = 0 - for month in range(0, 12): + temp_grad_day = [] + for month in range(1, 13): temp_grad_month = 0 - for day in cte.days_of_month[month]: + month_range = cal.monthrange(2015, month) + for day in range(1, month_range[1]+1): external_temp_med = 0 for hour in range(0, 24): - external_temp_med += external_temp[i]/24 + external_temp_med += external_temp['inseldb'][i]/24 for hour in range(0, 24): - if external_temp_med < temp_set[i] & heating_schedule[month] == 1: - if occupancy[hour] == 1: - temp_grad_day = temp_set[i] - external_temp[i] + if external_temp_med < temp_set and heating_schedule[month-1] == 1: + if occupancy[hour] > 0: + temp_grad_day.append(temp_set - external_temp['inseldb'][i]) else: - temp_grad_day = temp_back[i] - external_temp[i] + temp_grad_day.append(temp_back - external_temp['inseldb'][i]) else: - temp_grad_day = 0 + temp_grad_day.append(0) - temp_grad_month += temp_grad_day - self._hourly_heating.append(self._building.monthly_heating(month)*temp_grad_day/temp_grad_month) + temp_grad_month += temp_grad_day[i] i += 1 + for day in range(1, month_range[1] + 1): + for hour in range(0, 24): + j = (day - 1) * 24 + hour + monthly_demand = self._building.heating['month']['INSEL'][month-1] + hourly_demand = float(monthly_demand)*float(temp_grad_day[j])/float(temp_grad_month) + hourly_heating.append(hourly_demand) + + self._hourly_heating = pd.DataFrame(data=hourly_heating, columns=['monthly to hourly']) return self._hourly_heating @property diff --git a/data/physics/us_archetypes.xml b/data/physics/us_archetypes.xml index c928bf16..cd378aeb 100644 --- a/data/physics/us_archetypes.xml +++ b/data/physics/us_archetypes.xml @@ -805,4 +805,28 @@ 0.10 0 + + + + 0.15 + 4 + + + 0 + + + + 0 + + + + 3.05 + 3 + 90 + 0.15 + 0.15 + 0.50 + 0 + + diff --git a/factories/occupancy_factory.py b/factories/occupancy_factory.py index 77726e4b..bfa0cb3c 100644 --- a/factories/occupancy_factory.py +++ b/factories/occupancy_factory.py @@ -12,7 +12,7 @@ class OccupancyFactory: """ PhysicsFactor class """ - def __init__(self, handler, city, base_path=Path(Path(__file__).parent.parent.parent / 'data/occupancy')): + def __init__(self, handler, city, base_path=Path(Path(__file__).parent.parent / 'data/occupancy')): self._handler = '_' + handler.lower().replace(' ', '_') self._city = city self._base_path = base_path diff --git a/factories/occupancy_feeders/demo_occupancy_parameters.py b/factories/occupancy_feeders/demo_occupancy_parameters.py index c1e83f09..0cd3b602 100644 --- a/factories/occupancy_feeders/demo_occupancy_parameters.py +++ b/factories/occupancy_feeders/demo_occupancy_parameters.py @@ -2,6 +2,7 @@ PhysicsFactory retrieve the specific physics module for the given region SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca +contributors Pilar Monsalvete pilar_monsalvete@yahoo.es """ import pandas as pd from factories.occupancy_feeders.helpers.occupancy_helper import OccupancyHelper @@ -12,19 +13,27 @@ class DemoOccupancyParameters: def __init__(self, city, base_path): self._city = city self._demo_schedules_path = base_path / 'demo_schedules.xlsx' + print('schedules: ', self._demo_schedules_path) xls = pd.ExcelFile(self._demo_schedules_path) # todo: review for more than one usage_zones per building for building in city.buildings: schedules = dict() occupancy = pd.read_excel(xls, sheet_name=OccupancyHelper.pluto_occupancy_function(building.function), skiprows=[0, 1, 2, 3], nrows=39, usecols="A:AA") + # todo: should we safe the data type? How? for index in range(0, 13): - row = occupancy.iloc[3 * index] - data = row[1:] - name = row[0] - for i in range(1, 3): + data = pd.DataFrame() + columns_names = [] + name = '' + data_type = '' + for i in range(0, 3): row = occupancy.iloc[3*index + i] - data1 = row[1:] - data = pd.concat([data, data1], axis=1) + if i == 0: + name = row[0] + data_type = row[1] + columns_names.append(row[2]) + data1 = row[3:] + data = pd.concat([data, data1], axis=1) + data.columns = columns_names schedules[name] = data building.usage_zones[0].schedules = schedules diff --git a/factories/physics_feeders/us_base_physics_parameters.py b/factories/physics_feeders/us_base_physics_parameters.py index 4bc94987..c3fb461a 100644 --- a/factories/physics_feeders/us_base_physics_parameters.py +++ b/factories/physics_feeders/us_base_physics_parameters.py @@ -40,7 +40,7 @@ class UsBasePhysicsParameters: # ToDo: remove this in the future # ToDo: Raise WrongArchetype if not all the surface types are defined for the given city_object if archetype is None: - print('Building ', building.name, 'has unknown archetype') + print('Building ', building.name, 'has unknown archetype for building type: ', building_type) print('type: ', building_type, UsToLibraryTypes.yoc_to_standard(building.year_of_construction), self._climate_zone) continue diff --git a/factories/physics_feeders/us_new_york_city_physics_parameters.py b/factories/physics_feeders/us_new_york_city_physics_parameters.py index 4640bbbc..1f845780 100644 --- a/factories/physics_feeders/us_new_york_city_physics_parameters.py +++ b/factories/physics_feeders/us_new_york_city_physics_parameters.py @@ -3,7 +3,7 @@ UsNewYorkCityPhysicsParameters import the construction and material information SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ -from factories.physics_feeders.helpers.us_pluto_to_function import UsPlutoToFunction as Pf +from factories.physics_feeders.helpers.us_pluto_to_function import UsPlutoToFunction as pf from factories.physics_feeders.us_base_physics_parameters import UsBasePhysicsParameters @@ -14,4 +14,4 @@ class UsNewYorkCityPhysicsParameters(UsBasePhysicsParameters): def __init__(self, city, base_path): self._city = city climate_zone = 'ASHRAE_2004:4A' - super().__init__(climate_zone, self._city.buildings, Pf.function, base_path) + super().__init__(climate_zone, self._city.buildings, pf.function, base_path) diff --git a/factories/usage_feeders/us_new_york_city_usage_parameters.py b/factories/usage_feeders/us_new_york_city_usage_parameters.py index 60e16783..536d23a4 100644 --- a/factories/usage_feeders/us_new_york_city_usage_parameters.py +++ b/factories/usage_feeders/us_new_york_city_usage_parameters.py @@ -3,7 +3,7 @@ UsNewYorkCityUsageParameters model the usage properties for a NYC building SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ -from factories.usage_feeders.helpers.us_pluto_to_usage import UsPlutoToUsage as Pu +from factories.usage_feeders.helpers.us_pluto_to_usage import UsPlutoToUsage as pu from factories.usage_feeders.us_base_usage_parameters import UsBaseUsageParameters @@ -13,4 +13,4 @@ class UsNewYorkCityUsageParameters(UsBaseUsageParameters): """ def __init__(self, city): self._city = city - super().__init__(self._city, Pu.usage) + super().__init__(self._city, pu.usage) diff --git a/requirements.txt b/requirements.txt index 44eb9e62..1ec3cd7b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -54,4 +54,5 @@ requests~=2.24.0 esoreader~=1.2.3 geomeppy~=0.11.8 pyglet~=1.5.8 -networkx~=2.5 \ No newline at end of file +networkx~=2.5 +xlrd~=1.2.0 diff --git a/tests/test_occupancy_factory.py b/tests/test_occupancy_factory.py index 9a58aa6a..66939e3d 100644 --- a/tests/test_occupancy_factory.py +++ b/tests/test_occupancy_factory.py @@ -9,6 +9,7 @@ from unittest import TestCase from factories.geometry_factory import GeometryFactory from factories.usage_factory import UsageFactory from factories.occupancy_factory import OccupancyFactory +from city_model_structure.attributes.occupancy import Occupancy class TestOccupancyFactory(TestCase): @@ -46,3 +47,4 @@ class TestOccupancyFactory(TestCase): for building in city.buildings: for usage_zone in building.usage_zones: self.assertTrue(usage_zone.schedules) + print(len(Occupancy().get_complete_year_schedule(usage_zone.schedules['Occupancy'])))