Partial nrcan usage implementation

This commit is contained in:
Guille Gutierrez 2022-11-15 08:38:41 -05:00
parent 15eea30d81
commit 60b725dad6
6 changed files with 226 additions and 2 deletions

View File

@ -193,7 +193,6 @@ class ComnetCatalog(Catalog):
process_data[usage_type] = usage_parameters[24:26].values.tolist() process_data[usage_type] = usage_parameters[24:26].values.tolist()
schedules_key[usage_type] = usage_parameters[27:28].item() schedules_key[usage_type] = usage_parameters[27:28].item()
return {'lighting': lighting_data, return {'lighting': lighting_data,
'plug loads': plug_loads_data, 'plug loads': plug_loads_data,
'occupancy': occupancy_data, 'occupancy': occupancy_data,

View File

@ -0,0 +1,143 @@
"""
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")

View File

@ -13,6 +13,50 @@ class UsageHelper:
""" """
Usage helper class Usage helper class
""" """
_nrcan_schedule_type_to_hub_schedule_type = {
'Lighting': cte.LIGHTING,
'Occupancy': cte.OCCUPANCY,
'Equipment': cte.APPLIANCES,
'Thermostat Setpoint Cooling': cte.COOLING_SET_POINT, # Compose 'Thermostat Setpoint' + 'Cooling'
'Thermostat Setpoint Heating': cte.HEATING_SET_POINT, # Compose 'Thermostat Setpoint' + 'Heating'
}
_nrcan_data_type_to_hub_data_type = {
'Fraction': cte.FRACTION,
'ON_OFF': cte.ON_OFF,
'TEMPERATURE': cte.ANY_NUMBER
}
_nrcan_time_to_hub_time = {
'Hourly': cte.HOUR
}
_nrcan_day_type_to_hub_days = {
'Default|Wkdy': [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY],
'Sun|Hol': [cte.SUNDAY, cte.HOLIDAY],
'Sat': [cte.SATURDAY],
'Default|WntrDsn|SmrDsn': [cte.MONDAY,
cte.TUESDAY,
cte.WEDNESDAY,
cte.THURSDAY,
cte.FRIDAY,
cte.SATURDAY,
cte.SUNDAY,
cte.HOLIDAY,
cte.WINTER_DESIGN_DAY,
cte.SUMMER_DESIGN_DAY],
'Default': [cte.MONDAY,
cte.TUESDAY,
cte.WEDNESDAY,
cte.THURSDAY,
cte.FRIDAY,
cte.SATURDAY,
cte.SUNDAY,
cte.HOLIDAY,
cte.WINTER_DESIGN_DAY,
cte.SUMMER_DESIGN_DAY]
}
_usage_to_hft = { _usage_to_hft = {
cte.RESIDENTIAL: 'residential', cte.RESIDENTIAL: 'residential',
cte.SINGLE_FAMILY_HOUSE: 'Single family house', cte.SINGLE_FAMILY_HOUSE: 'Single family house',
@ -71,7 +115,7 @@ class UsageHelper:
cte.GREEN_HOUSE: cte.GREEN_HOUSE, cte.GREEN_HOUSE: cte.GREEN_HOUSE,
cte.NON_HEATED: cte.NON_HEATED cte.NON_HEATED: cte.NON_HEATED
} }
_comnet_data_type_to_hub_data_type = { _comnet_data_type_to_hub_data_type = {
'Fraction': cte.FRACTION, 'Fraction': cte.FRACTION,
'OnOff': cte.ON_OFF, 'OnOff': cte.ON_OFF,
@ -93,6 +137,22 @@ class UsageHelper:
'C-14 Gymnasium': 'C-14 Gymnasium' 'C-14 Gymnasium': 'C-14 Gymnasium'
} }
@property
def nrcan_day_type_to_hub_days(self):
return self._nrcan_day_type_to_hub_days
@property
def nrcan_schedule_type_to_hub_schedule_type(self):
return self._nrcan_schedule_type_to_hub_schedule_type
@property
def nrcan_data_type_to_hub_data_type(self):
return self._nrcan_data_type_to_hub_data_type
@property
def nrcan_time_to_hub_time(self):
return self._nrcan_time_to_hub_time
@property @property
def comnet_data_type_to_hub_data_type(self): def comnet_data_type_to_hub_data_type(self):
return self._comnet_data_type_to_hub_data_type return self._comnet_data_type_to_hub_data_type

View File

@ -8,6 +8,7 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
from pathlib import Path from pathlib import Path
from typing import TypeVar from typing import TypeVar
from catalog_factories.usage.comnet_catalog import ComnetCatalog from catalog_factories.usage.comnet_catalog import ComnetCatalog
from catalog_factories.usage.nrcan_catalog import NrcanCatalog
Catalog = TypeVar('Catalog') Catalog = TypeVar('Catalog')
@ -25,6 +26,14 @@ class UsageCatalogFactory:
""" """
return ComnetCatalog(self._path) return ComnetCatalog(self._path)
@property
def _nrcan(self):
"""
Retrieve NRCAN catalog
"""
# nrcan retrieves the data directly from github
return NrcanCatalog(self._path)
@property @property
def catalog(self) -> Catalog: def catalog(self) -> Catalog:
""" """

9
data/usage/nrcan.xml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<nrcan base_url="https://raw.githubusercontent.com/NREL/openstudio-standards/master/lib/openstudio-standards/standards/necb/">
<standards>
<usage>
<space_types_location>NECB2020/data/space_types.json</space_types_location>
<schedules_location>NECB2015/data/schedules.json</schedules_location>
</usage>
</standards>
</nrcan>

View File

@ -15,3 +15,7 @@ class TestConstructionCatalog(TestCase):
self.assertIsNotNone(catalog, 'catalog is none') self.assertIsNotNone(catalog, 'catalog is none')
content = catalog.entries() content = catalog.entries()
self.assertEqual(len(content.usages), 32, 'Wrong number of usages') self.assertEqual(len(content.usages), 32, 'Wrong number of usages')
def test_nrcan_catalog(self):
catalog = UsageCatalogFactory('nrcan').catalog
self.assertIsNotNone(catalog, 'catalog is none')