144 lines
5.7 KiB
Python
144 lines
5.7 KiB
Python
|
"""
|
||
|
NRCAN usage catalog
|
||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||
|
Copyright © 2022 Concordia CERC group
|
||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||
|
"""
|
||
|
from typing import Dict
|
||
|
import pandas as pd
|
||
|
import json
|
||
|
import urllib.request
|
||
|
import xmltodict
|
||
|
|
||
|
import helpers.constants as cte
|
||
|
from catalog_factories.catalog import Catalog
|
||
|
from catalog_factories.data_models.usages.appliances import Appliances
|
||
|
from catalog_factories.data_models.usages.content import Content
|
||
|
from catalog_factories.data_models.usages.lighting import Lighting
|
||
|
from catalog_factories.data_models.usages.ocupancy import Occupancy
|
||
|
from catalog_factories.data_models.usages.schedule import Schedule
|
||
|
from catalog_factories.data_models.usages.thermal_control import ThermalControl
|
||
|
from catalog_factories.data_models.usages.usage import Usage
|
||
|
from catalog_factories.usage.usage_helper import UsageHelper
|
||
|
from helpers.configuration_helper import ConfigurationHelper as ch
|
||
|
|
||
|
|
||
|
class NrcanCatalog(Catalog):
|
||
|
def __init__(self, path):
|
||
|
path = str(path / 'nrcan.xml')
|
||
|
self._content = None
|
||
|
self._schedules = []
|
||
|
self._lightings = []
|
||
|
self._occupancies = []
|
||
|
self._internal_gains = []
|
||
|
self._appliances = []
|
||
|
self._thermal_controls = []
|
||
|
usages = []
|
||
|
with open(path) as xml:
|
||
|
self._metadata = xmltodict.parse(xml.read())
|
||
|
self._base_url = self._metadata['nrcan']['@base_url']
|
||
|
self._load_schedules()
|
||
|
self._load_archetypes()
|
||
|
|
||
|
def _calculate_hours_day(self, function):
|
||
|
days = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY, cte.SATURDAY, cte.SUNDAY, cte.HOLIDAY]
|
||
|
number_of_days_per_type = [51, 50, 50, 50, 50, 52, 52, 10]
|
||
|
total = 0
|
||
|
for schedule in self._schedules[function]['HVAC Avail']:
|
||
|
yearly_days = number_of_days_per_type[days.index(schedule.day_types[0])]
|
||
|
for value in schedule.values:
|
||
|
total += value * yearly_days
|
||
|
return total / 365
|
||
|
|
||
|
@staticmethod
|
||
|
def _extract_schedule(raw):
|
||
|
nrcan_schedule_type = raw['category']
|
||
|
print(raw)
|
||
|
if 'Heating' in raw['name']:
|
||
|
nrcan_schedule_type = f'{nrcan_schedule_type} Heating'
|
||
|
elif 'Cooling' in raw['name']:
|
||
|
nrcan_schedule_type = f'{nrcan_schedule_type} Cooling'
|
||
|
if nrcan_schedule_type not in UsageHelper().nrcan_schedule_type_to_hub_schedule_type:
|
||
|
return None
|
||
|
hub_type = UsageHelper().nrcan_schedule_type_to_hub_schedule_type[nrcan_schedule_type]
|
||
|
data_type = UsageHelper().nrcan_data_type_to_hub_data_type[raw['units']]
|
||
|
time_step = UsageHelper().nrcan_time_to_hub_time[raw['type']]
|
||
|
# nrcan only uses yearly range for the schedules
|
||
|
time_range = cte.YEAR
|
||
|
day_types = UsageHelper().nrcan_day_type_to_hub_days[raw['day_types']]
|
||
|
return Schedule(hub_type, raw['values'], data_type, time_step, time_range, day_types)
|
||
|
|
||
|
def _load_schedules(self):
|
||
|
usage = self._metadata['nrcan']['standards']['usage']
|
||
|
url = f'{self._base_url}{usage["schedules_location"]}'
|
||
|
with urllib.request.urlopen(url) as json_file:
|
||
|
schedules_type = json.load(json_file)
|
||
|
for schedule_type in schedules_type['tables']['schedules']['table']:
|
||
|
schedule = NrcanCatalog._extract_schedule(schedule_type)
|
||
|
if schedule is not None:
|
||
|
self._schedules.append({schedule_type['name'], schedule})
|
||
|
|
||
|
def _get_schedule(self, name):
|
||
|
if name in self._schedules:
|
||
|
print(f'dictionary found {name}: {self._schedule[name]}')
|
||
|
return self._schedule[name]
|
||
|
|
||
|
def _load_archetypes(self):
|
||
|
usage = self._metadata['nrcan']['standards']['usage']
|
||
|
url = f'{self._base_url}{usage["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']
|
||
|
for space_type in space_types:
|
||
|
usage_type = space_type['space_type']
|
||
|
mechanical_air_change = space_type['ventilation_air_changes']
|
||
|
ventilation_rate = space_type['ventilation_per_area']
|
||
|
if ventilation_rate == 0:
|
||
|
ventilation_rate = space_type['ventilation_per_person']
|
||
|
hours_day = 0 # self._calculate_hours_day(usage_type)
|
||
|
days_year = 365
|
||
|
|
||
|
occupancy_schedule_name = space_type['occupancy_schedule']
|
||
|
self._load_schedule(occupancy_schedule_name)
|
||
|
lighting_schedule_name = space_type['lighting_schedule']
|
||
|
appliances_schedule_name = space_type['electric_equipment_schedule']
|
||
|
# thermal control
|
||
|
heating_setpoint_schedule_name = space_type['heating_setpoint_schedule']
|
||
|
cooling_setpoint_schedule_name = space_type['cooling_setpoint_schedule']
|
||
|
print(usage_type, mechanical_air_change, ventilation_rate, hours_day, days_year, occupancy_schedule_name, lighting_schedule_name, appliances_schedule_name, heating_setpoint_schedule_name, cooling_setpoint_schedule_name)
|
||
|
|
||
|
def _read_archetype_file(self) -> Dict:
|
||
|
"""
|
||
|
reads xml files containing metadata to access to the json file?
|
||
|
:return : Dict
|
||
|
"""
|
||
|
print(self._metadata)
|
||
|
return
|
||
|
|
||
|
def names(self, category=None):
|
||
|
"""
|
||
|
Get the catalog elements names
|
||
|
:parm: for usage catalog category filter does nothing as there is only one category (usages)
|
||
|
"""
|
||
|
_names = {'usages': []}
|
||
|
for usage in self._content.usages:
|
||
|
_names['usages'].append(usage.usage)
|
||
|
return _names
|
||
|
|
||
|
def entries(self, category=None):
|
||
|
"""
|
||
|
Get the catalog elements
|
||
|
:parm: for usage catalog category filter does nothing as there is only one category (usages)
|
||
|
"""
|
||
|
return self._content
|
||
|
|
||
|
def get_entry(self, name):
|
||
|
"""
|
||
|
Get one catalog element by names
|
||
|
:parm: entry name
|
||
|
"""
|
||
|
for usage in self._content.usages:
|
||
|
if usage.usage.lower() == name.lower():
|
||
|
return usage
|
||
|
raise IndexError(f"{name} doesn't exists in the catalog")
|