Merge remote-tracking branch 'origin/main' into feature/upgrade_ep_to_23.2.0
# Conflicts: # hub/exports/building_energy/idf_files/Minimal.idf
This commit is contained in:
commit
e0e7daf64b
2
.gitignore
vendored
2
.gitignore
vendored
@ -9,4 +9,4 @@
|
|||||||
**/hub/logs/
|
**/hub/logs/
|
||||||
**/__pycache__/
|
**/__pycache__/
|
||||||
**/.idea/
|
**/.idea/
|
||||||
|
cerc_hub.egg-info
|
||||||
|
3
cerc_hub.egg-info/.gitignore
vendored
3
cerc_hub.egg-info/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
# Except this file
|
|
||||||
*
|
|
||||||
!.gitignore
|
|
@ -8,6 +8,8 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
import urllib.request
|
import urllib.request
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import xmltodict
|
import xmltodict
|
||||||
|
|
||||||
import hub.helpers.constants as cte
|
import hub.helpers.constants as cte
|
||||||
@ -28,12 +30,11 @@ class NrcanCatalog(Catalog):
|
|||||||
Nrcan catalog class
|
Nrcan catalog class
|
||||||
"""
|
"""
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
path = str(path / 'nrcan.xml')
|
self._schedules_path = Path(path / 'nrcan_schedules.json').resolve()
|
||||||
|
self._space_types_path = Path(path / 'nrcan_space_types.json').resolve()
|
||||||
|
self._space_compliance_path = Path(path / 'nrcan_space_compliance_2015.json').resolve()
|
||||||
self._content = None
|
self._content = None
|
||||||
self._schedules = {}
|
self._schedules = {}
|
||||||
with open(path, 'r', encoding='utf-8') as xml:
|
|
||||||
self._metadata = xmltodict.parse(xml.read())
|
|
||||||
self._base_url = self._metadata['nrcan']['@base_url']
|
|
||||||
self._load_schedules()
|
self._load_schedules()
|
||||||
self._content = Content(self._load_archetypes())
|
self._content = Content(self._load_archetypes())
|
||||||
|
|
||||||
@ -55,11 +56,9 @@ class NrcanCatalog(Catalog):
|
|||||||
return Schedule(hub_type, raw['values'], data_type, time_step, time_range, day_types)
|
return Schedule(hub_type, raw['values'], data_type, time_step, time_range, day_types)
|
||||||
|
|
||||||
def _load_schedules(self):
|
def _load_schedules(self):
|
||||||
usage = self._metadata['nrcan']
|
|
||||||
url = f'{self._base_url}{usage["schedules"]}'
|
|
||||||
_schedule_types = []
|
_schedule_types = []
|
||||||
with urllib.request.urlopen(url) as json_file:
|
with open(self._schedules_path, 'r') as f:
|
||||||
schedules_type = json.load(json_file)
|
schedules_type = json.load(f)
|
||||||
for schedule_type in schedules_type['tables']['schedules']['table']:
|
for schedule_type in schedules_type['tables']['schedules']['table']:
|
||||||
schedule = NrcanCatalog._extract_schedule(schedule_type)
|
schedule = NrcanCatalog._extract_schedule(schedule_type)
|
||||||
if schedule_type['name'] not in _schedule_types:
|
if schedule_type['name'] not in _schedule_types:
|
||||||
@ -80,14 +79,11 @@ class NrcanCatalog(Catalog):
|
|||||||
|
|
||||||
def _load_archetypes(self):
|
def _load_archetypes(self):
|
||||||
usages = []
|
usages = []
|
||||||
name = self._metadata['nrcan']
|
with open(self._space_types_path, 'r') as f:
|
||||||
url_1 = f'{self._base_url}{name["space_types"]}'
|
space_types = json.load(f)['tables']['space_types']['table']
|
||||||
url_2 = f'{self._base_url}{name["space_types_compliance"]}'
|
|
||||||
with urllib.request.urlopen(url_1) as json_file:
|
|
||||||
space_types = json.load(json_file)['tables']['space_types']['table']
|
|
||||||
space_types = [st for st in space_types if st['space_type'] == 'WholeBuilding']
|
space_types = [st for st in space_types if st['space_type'] == 'WholeBuilding']
|
||||||
with urllib.request.urlopen(url_2) as json_file:
|
with open(self._space_compliance_path, 'r') as f:
|
||||||
space_types_compliance = json.load(json_file)['tables']['space_compliance']['table']
|
space_types_compliance = json.load(f)['tables']['space_compliance']['table']
|
||||||
space_types_compliance = [st for st in space_types_compliance if st['space_type'] == 'WholeBuilding']
|
space_types_compliance = [st for st in space_types_compliance if st['space_type'] == 'WholeBuilding']
|
||||||
space_types_dictionary = {}
|
space_types_dictionary = {}
|
||||||
for space_type in space_types_compliance:
|
for space_type in space_types_compliance:
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<nrcan base_url="https://raw.githubusercontent.com/NREL/openstudio-standards/master/lib/openstudio-standards/standards/necb/">
|
|
||||||
<space_types>NECB2015/data/space_types.json</space_types>
|
|
||||||
<space_types_compliance>NECB2015/qaqc/qaqc_data/space_compliance_2015.json</space_types_compliance>>
|
|
||||||
<schedules>NECB2015/data/schedules.json</schedules>
|
|
||||||
</nrcan>
|
|
8685
hub/data/usage/nrcan_schedules.json
Normal file
8685
hub/data/usage/nrcan_schedules.json
Normal file
File diff suppressed because it is too large
Load Diff
3211
hub/data/usage/nrcan_space_compliance_2015.json
Normal file
3211
hub/data/usage/nrcan_space_compliance_2015.json
Normal file
File diff suppressed because it is too large
Load Diff
26170
hub/data/usage/nrcan_space_types.json
Normal file
26170
hub/data/usage/nrcan_space_types.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -285,11 +285,11 @@ class Idf:
|
|||||||
self._idf.newidfobject(self._COMPACT_SCHEDULE, **_kwargs)
|
self._idf.newidfobject(self._COMPACT_SCHEDULE, **_kwargs)
|
||||||
|
|
||||||
def _write_schedules_file(self, usage, schedule):
|
def _write_schedules_file(self, usage, schedule):
|
||||||
file_name = str((Path(self._output_path) / f'{schedule.type} schedules {usage}.dat').resolve())
|
file_name = str((Path(self._output_path) / f'{schedule.type} schedules {usage}.csv').resolve())
|
||||||
with open(file_name, 'w', encoding='utf8') as file:
|
with open(file_name, 'w', encoding='utf8') as file:
|
||||||
for value in schedule.values:
|
for value in schedule.values:
|
||||||
file.write(f'{str(value)},\n')
|
file.write(f'{str(value)},\n')
|
||||||
return file_name
|
return Path(file_name).name
|
||||||
|
|
||||||
def _add_file_schedule(self, usage, schedule, file_name):
|
def _add_file_schedule(self, usage, schedule, file_name):
|
||||||
_schedule = self._idf.newidfobject(self._FILE_SCHEDULE, Name=f'{schedule.type} schedules {usage}')
|
_schedule = self._idf.newidfobject(self._FILE_SCHEDULE, Name=f'{schedule.type} schedules {usage}')
|
||||||
@ -476,7 +476,7 @@ class Idf:
|
|||||||
|
|
||||||
self._idf.newidfobject(self._PEOPLE,
|
self._idf.newidfobject(self._PEOPLE,
|
||||||
Name=f'{zone_name}_occupancy',
|
Name=f'{zone_name}_occupancy',
|
||||||
Zone_or_ZoneList_Name=zone_name,
|
Zone_or_ZoneList_or_Space_or_SpaceList_Name=zone_name,
|
||||||
Number_of_People_Schedule_Name=f'Occupancy schedules {thermal_zone.usage_name}',
|
Number_of_People_Schedule_Name=f'Occupancy schedules {thermal_zone.usage_name}',
|
||||||
Number_of_People_Calculation_Method="People",
|
Number_of_People_Calculation_Method="People",
|
||||||
Number_of_People=number_of_people,
|
Number_of_People=number_of_people,
|
||||||
@ -493,7 +493,7 @@ class Idf:
|
|||||||
|
|
||||||
self._idf.newidfobject(self._LIGHTS,
|
self._idf.newidfobject(self._LIGHTS,
|
||||||
Name=f'{zone_name}_lights',
|
Name=f'{zone_name}_lights',
|
||||||
Zone_or_ZoneList_Name=zone_name,
|
Zone_or_ZoneList_or_Space_or_SpaceList_Name=zone_name,
|
||||||
Schedule_Name=f'Lighting schedules {thermal_zone.usage_name}',
|
Schedule_Name=f'Lighting schedules {thermal_zone.usage_name}',
|
||||||
Design_Level_Calculation_Method=method,
|
Design_Level_Calculation_Method=method,
|
||||||
Watts_per_Zone_Floor_Area=watts_per_zone_floor_area,
|
Watts_per_Zone_Floor_Area=watts_per_zone_floor_area,
|
||||||
@ -512,7 +512,7 @@ class Idf:
|
|||||||
self._idf.newidfobject(self._APPLIANCES,
|
self._idf.newidfobject(self._APPLIANCES,
|
||||||
Fuel_Type=fuel_type,
|
Fuel_Type=fuel_type,
|
||||||
Name=f'{zone_name}_appliance',
|
Name=f'{zone_name}_appliance',
|
||||||
Zone_or_ZoneList_Name=zone_name,
|
Zone_or_ZoneList_or_Space_or_SpaceList_Name=zone_name,
|
||||||
Schedule_Name=f'Appliance schedules {thermal_zone.usage_name}',
|
Schedule_Name=f'Appliance schedules {thermal_zone.usage_name}',
|
||||||
Design_Level_Calculation_Method=method,
|
Design_Level_Calculation_Method=method,
|
||||||
Power_per_Zone_Floor_Area=watts_per_zone_floor_area,
|
Power_per_Zone_Floor_Area=watts_per_zone_floor_area,
|
||||||
@ -526,7 +526,7 @@ class Idf:
|
|||||||
_infiltration = thermal_zone.infiltration_rate_system_off * cte.HOUR_TO_SECONDS
|
_infiltration = thermal_zone.infiltration_rate_system_off * cte.HOUR_TO_SECONDS
|
||||||
self._idf.newidfobject(self._INFILTRATION,
|
self._idf.newidfobject(self._INFILTRATION,
|
||||||
Name=f'{zone_name}_infiltration',
|
Name=f'{zone_name}_infiltration',
|
||||||
Zone_or_ZoneList_Name=zone_name,
|
Zone_or_ZoneList_or_Space_or_SpaceList_Name=zone_name,
|
||||||
Schedule_Name=schedule,
|
Schedule_Name=schedule,
|
||||||
Design_Flow_Rate_Calculation_Method='AirChanges/Hour',
|
Design_Flow_Rate_Calculation_Method='AirChanges/Hour',
|
||||||
Air_Changes_per_Hour=_infiltration
|
Air_Changes_per_Hour=_infiltration
|
||||||
@ -537,7 +537,7 @@ class Idf:
|
|||||||
_air_change = thermal_zone.mechanical_air_change * cte.HOUR_TO_SECONDS
|
_air_change = thermal_zone.mechanical_air_change * cte.HOUR_TO_SECONDS
|
||||||
self._idf.newidfobject(self._VENTILATION,
|
self._idf.newidfobject(self._VENTILATION,
|
||||||
Name=f'{zone_name}_ventilation',
|
Name=f'{zone_name}_ventilation',
|
||||||
Zone_or_ZoneList_Name=zone_name,
|
Zone_or_ZoneList_or_Space_or_SpaceList_Name=zone_name,
|
||||||
Schedule_Name=schedule,
|
Schedule_Name=schedule,
|
||||||
Design_Flow_Rate_Calculation_Method='AirChanges/Hour',
|
Design_Flow_Rate_Calculation_Method='AirChanges/Hour',
|
||||||
Air_Changes_per_Hour=_air_change
|
Air_Changes_per_Hour=_air_change
|
||||||
@ -707,8 +707,8 @@ class Idf:
|
|||||||
self._idf.intersect_match()
|
self._idf.intersect_match()
|
||||||
|
|
||||||
def _add_shading(self, building):
|
def _add_shading(self, building):
|
||||||
for surface in building.surfaces:
|
for i, surface in enumerate(building.surfaces):
|
||||||
shading = self._idf.newidfobject(self._SHADING, Name=f'{surface.name}')
|
shading = self._idf.newidfobject(self._SHADING, Name=f'{building.name}_{i}')
|
||||||
coordinates = self._matrix_to_list(surface.solid_polygon.coordinates,
|
coordinates = self._matrix_to_list(surface.solid_polygon.coordinates,
|
||||||
self._city.lower_corner)
|
self._city.lower_corner)
|
||||||
shading.setcoords(coordinates)
|
shading.setcoords(coordinates)
|
||||||
@ -716,17 +716,17 @@ class Idf:
|
|||||||
if solar_reflectance is None:
|
if solar_reflectance is None:
|
||||||
solar_reflectance = ConfigurationHelper().short_wave_reflectance
|
solar_reflectance = ConfigurationHelper().short_wave_reflectance
|
||||||
self._idf.newidfobject(self._SHADING_PROPERTY,
|
self._idf.newidfobject(self._SHADING_PROPERTY,
|
||||||
Shading_Surface_Name=f'{surface.name}',
|
Shading_Surface_Name=f'{building.name}_{i}',
|
||||||
Diffuse_Solar_Reflectance_of_Unglazed_Part_of_Shading_Surface=solar_reflectance,
|
Diffuse_Solar_Reflectance_of_Unglazed_Part_of_Shading_Surface=solar_reflectance,
|
||||||
Fraction_of_Shading_Surface_That_Is_Glazed=0)
|
Fraction_of_Shading_Surface_That_Is_Glazed=0)
|
||||||
|
|
||||||
def _add_pure_geometry(self, building, zone_name):
|
def _add_pure_geometry(self, building, zone_name):
|
||||||
for surface in building.surfaces:
|
for index, surface in enumerate(building.surfaces):
|
||||||
outside_boundary_condition = 'Outdoors'
|
outside_boundary_condition = 'Outdoors'
|
||||||
sun_exposure = 'SunExposed'
|
sun_exposure = 'SunExposed'
|
||||||
wind_exposure = 'WindExposed'
|
wind_exposure = 'WindExposed'
|
||||||
idf_surface_type = self.idf_surfaces[surface.type]
|
idf_surface_type = self.idf_surfaces[surface.type]
|
||||||
_kwargs = {'Name': f'{surface.name}',
|
_kwargs = {'Name': f'Building_{building.name}_surface_{index}',
|
||||||
'Surface_Type': idf_surface_type,
|
'Surface_Type': idf_surface_type,
|
||||||
'Zone_Name': zone_name}
|
'Zone_Name': zone_name}
|
||||||
if surface.type == cte.GROUND:
|
if surface.type == cte.GROUND:
|
||||||
@ -735,7 +735,7 @@ class Idf:
|
|||||||
wind_exposure = 'NoWind'
|
wind_exposure = 'NoWind'
|
||||||
if surface.percentage_shared is not None and surface.percentage_shared > 0.5:
|
if surface.percentage_shared is not None and surface.percentage_shared > 0.5:
|
||||||
outside_boundary_condition = 'Surface'
|
outside_boundary_condition = 'Surface'
|
||||||
outside_boundary_condition_object = surface.name
|
outside_boundary_condition_object = f'Building_{building.name}_surface_{index}'
|
||||||
sun_exposure = 'NoSun'
|
sun_exposure = 'NoSun'
|
||||||
wind_exposure = 'NoWind'
|
wind_exposure = 'NoWind'
|
||||||
_kwargs['Outside_Boundary_Condition_Object'] = outside_boundary_condition_object
|
_kwargs['Outside_Boundary_Condition_Object'] = outside_boundary_condition_object
|
||||||
@ -760,12 +760,12 @@ class Idf:
|
|||||||
|
|
||||||
def _add_surfaces(self, building, zone_name):
|
def _add_surfaces(self, building, zone_name):
|
||||||
for thermal_zone in building.thermal_zones_from_internal_zones:
|
for thermal_zone in building.thermal_zones_from_internal_zones:
|
||||||
for boundary in thermal_zone.thermal_boundaries:
|
for index, boundary in enumerate(thermal_zone.thermal_boundaries):
|
||||||
idf_surface_type = self.idf_surfaces[boundary.parent_surface.type]
|
idf_surface_type = self.idf_surfaces[boundary.parent_surface.type]
|
||||||
outside_boundary_condition = 'Outdoors'
|
outside_boundary_condition = 'Outdoors'
|
||||||
sun_exposure = 'SunExposed'
|
sun_exposure = 'SunExposed'
|
||||||
wind_exposure = 'WindExposed'
|
wind_exposure = 'WindExposed'
|
||||||
_kwargs = {'Name': f'{boundary.parent_surface.name}',
|
_kwargs = {'Name': f'Building_{building.name}_surface_{index}',
|
||||||
'Surface_Type': idf_surface_type,
|
'Surface_Type': idf_surface_type,
|
||||||
'Zone_Name': zone_name}
|
'Zone_Name': zone_name}
|
||||||
if boundary.parent_surface.type == cte.GROUND:
|
if boundary.parent_surface.type == cte.GROUND:
|
||||||
@ -774,7 +774,7 @@ class Idf:
|
|||||||
wind_exposure = 'NoWind'
|
wind_exposure = 'NoWind'
|
||||||
if boundary.parent_surface.percentage_shared is not None and boundary.parent_surface.percentage_shared > 0.5:
|
if boundary.parent_surface.percentage_shared is not None and boundary.parent_surface.percentage_shared > 0.5:
|
||||||
outside_boundary_condition = 'Surface'
|
outside_boundary_condition = 'Surface'
|
||||||
outside_boundary_condition_object = boundary.parent_surface.name
|
outside_boundary_condition_object = f'Building_{building.name}_surface_{index}'
|
||||||
sun_exposure = 'NoSun'
|
sun_exposure = 'NoSun'
|
||||||
wind_exposure = 'NoWind'
|
wind_exposure = 'NoWind'
|
||||||
_kwargs['Outside_Boundary_Condition_Object'] = outside_boundary_condition_object
|
_kwargs['Outside_Boundary_Condition_Object'] = outside_boundary_condition_object
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -213,8 +213,6 @@ class Geojson:
|
|||||||
polygon = Polygon(coordinates)
|
polygon = Polygon(coordinates)
|
||||||
polygon.area = igh.ground_area(coordinates)
|
polygon.area = igh.ground_area(coordinates)
|
||||||
surfaces[-1] = Surface(polygon, polygon)
|
surfaces[-1] = Surface(polygon, polygon)
|
||||||
if len(surfaces) > 1:
|
|
||||||
raise ValueError('too many surfaces!!!!')
|
|
||||||
building = Building(f'{building_name}', surfaces, year_of_construction, function)
|
building = Building(f'{building_name}', surfaces, year_of_construction, function)
|
||||||
for alias in building_aliases:
|
for alias in building_aliases:
|
||||||
building.add_alias(alias)
|
building.add_alias(alias)
|
||||||
|
@ -70,7 +70,7 @@ class InselMonthlyEnergyBalance:
|
|||||||
total_day += value
|
total_day += value
|
||||||
for day_type in schedule.day_types:
|
for day_type in schedule.day_types:
|
||||||
total_lighting += total_day * cte.WEEK_DAYS_A_MONTH[month][day_type] \
|
total_lighting += total_day * cte.WEEK_DAYS_A_MONTH[month][day_type] \
|
||||||
* lighting_density / cte.WATTS_HOUR_TO_JULES
|
* lighting_density * cte.WATTS_HOUR_TO_JULES
|
||||||
lighting_demand.append(total_lighting * area)
|
lighting_demand.append(total_lighting * area)
|
||||||
|
|
||||||
for schedule in thermal_zone.appliances.schedules:
|
for schedule in thermal_zone.appliances.schedules:
|
||||||
@ -79,7 +79,7 @@ class InselMonthlyEnergyBalance:
|
|||||||
total_day += value
|
total_day += value
|
||||||
for day_type in schedule.day_types:
|
for day_type in schedule.day_types:
|
||||||
total_appliances += total_day * cte.WEEK_DAYS_A_MONTH[month][day_type] \
|
total_appliances += total_day * cte.WEEK_DAYS_A_MONTH[month][day_type] \
|
||||||
* appliances_density / cte.WATTS_HOUR_TO_JULES
|
* appliances_density * cte.WATTS_HOUR_TO_JULES
|
||||||
appliances_demand.append(total_appliances * area)
|
appliances_demand.append(total_appliances * area)
|
||||||
|
|
||||||
for schedule in thermal_zone.domestic_hot_water.schedules:
|
for schedule in thermal_zone.domestic_hot_water.schedules:
|
||||||
@ -89,7 +89,7 @@ class InselMonthlyEnergyBalance:
|
|||||||
for day_type in schedule.day_types:
|
for day_type in schedule.day_types:
|
||||||
demand = (
|
demand = (
|
||||||
peak_flow * cte.WATER_DENSITY * cte.WATER_HEAT_CAPACITY
|
peak_flow * cte.WATER_DENSITY * cte.WATER_HEAT_CAPACITY
|
||||||
* (service_temperature - cold_water[i_month]) / cte.WATTS_HOUR_TO_JULES
|
* (service_temperature - cold_water[i_month]) * cte.WATTS_HOUR_TO_JULES
|
||||||
)
|
)
|
||||||
total_dhw_demand += total_day * cte.WEEK_DAYS_A_MONTH[month][day_type] * demand
|
total_dhw_demand += total_day * cte.WEEK_DAYS_A_MONTH[month][day_type] * demand
|
||||||
domestic_hot_water_demand.append(total_dhw_demand * area)
|
domestic_hot_water_demand.append(total_dhw_demand * area)
|
||||||
|
@ -4,10 +4,8 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
|
|||||||
Copyright © 2022 Concordia CERC group
|
Copyright © 2022 Concordia CERC group
|
||||||
Project CoderPeter Yefi peteryefi@gmail.com
|
Project CoderPeter Yefi peteryefi@gmail.com
|
||||||
"""
|
"""
|
||||||
import json
|
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
|
|
||||||
from hub.persistence.repositories.application import Application
|
from hub.persistence.repositories.application import Application
|
||||||
from hub.persistence.repositories.city import City
|
from hub.persistence.repositories.city import City
|
||||||
from hub.persistence.repositories.city_object import CityObject
|
from hub.persistence.repositories.city_object import CityObject
|
||||||
@ -75,10 +73,10 @@ class DBControl:
|
|||||||
:
|
:
|
||||||
"""
|
"""
|
||||||
cities = self._city.get_by_user_id_application_id_and_scenario(user_id, application_id, scenario)
|
cities = self._city.get_by_user_id_application_id_and_scenario(user_id, application_id, scenario)
|
||||||
for city in cities:
|
c = [c[0].id for c in cities]
|
||||||
result = self.building_info(name, city[0].id)
|
result = self._city_object.building_in_cities_info(name, c)
|
||||||
if result is not None:
|
if result is not None:
|
||||||
return result
|
return result
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def building_info(self, name, city_id) -> CityObject:
|
def building_info(self, name, city_id) -> CityObject:
|
||||||
@ -90,17 +88,27 @@ class DBControl:
|
|||||||
"""
|
"""
|
||||||
return self._city_object.get_by_name_or_alias_and_city(name, city_id)
|
return self._city_object.get_by_name_or_alias_and_city(name, city_id)
|
||||||
|
|
||||||
def buildings_info(self, request_values, city_id) -> [CityObject]:
|
def building_info_in_cities(self, name, cities) -> CityObject:
|
||||||
|
"""
|
||||||
|
Retrieve the building info from the database
|
||||||
|
:param name: Building name
|
||||||
|
:param cities: [City ID]
|
||||||
|
:return: CityObject
|
||||||
|
"""
|
||||||
|
return self._city_object.get_by_name_or_alias_in_cities(name, cities)
|
||||||
|
|
||||||
|
def buildings_info(self, user_id, application_id, names_or_aliases) -> [CityObject]:
|
||||||
"""
|
"""
|
||||||
Retrieve the buildings info from the database
|
Retrieve the buildings info from the database
|
||||||
:param request_values: Building names
|
:param user_id: User ID
|
||||||
:param city_id: City ID
|
:param application_id: Application ID
|
||||||
|
:param names_or_aliases: A list of names or alias for the buildings
|
||||||
:return: [CityObject]
|
:return: [CityObject]
|
||||||
"""
|
"""
|
||||||
buildings = []
|
results = self._city_object.get_by_name_or_alias_for_user_app(user_id, application_id, names_or_aliases)
|
||||||
for name in request_values['names']:
|
if results is None:
|
||||||
buildings.append(self.building_info(name, city_id))
|
return []
|
||||||
return buildings
|
return results
|
||||||
|
|
||||||
def results(self, user_id, application_id, request_values, result_names=None) -> Dict:
|
def results(self, user_id, application_id, request_values, result_names=None) -> Dict:
|
||||||
"""
|
"""
|
||||||
@ -134,7 +142,7 @@ class DBControl:
|
|||||||
result_names)
|
result_names)
|
||||||
|
|
||||||
for value in _:
|
for value in _:
|
||||||
values = json.loads(value.values)
|
values = value.values
|
||||||
values["building"] = building_name
|
values["building"] = building_name
|
||||||
results[scenario_name].append(values)
|
results[scenario_name].append(values)
|
||||||
return results
|
return results
|
||||||
|
@ -6,14 +6,16 @@ Project Coder Guille Gutierrez Guillermo.GutierrezMorote@concordia.ca
|
|||||||
"""
|
"""
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
from sqlalchemy import select, or_
|
from sqlalchemy import select
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from hub.city_model_structure.building import Building
|
from hub.city_model_structure.building import Building
|
||||||
from hub.persistence.repository import Repository
|
|
||||||
from hub.persistence.models import CityObject as Model
|
from hub.persistence.models import CityObject as Model
|
||||||
|
from hub.persistence.models import City as CityModel
|
||||||
|
from hub.persistence.repository import Repository
|
||||||
|
|
||||||
|
|
||||||
class CityObject(Repository):
|
class CityObject(Repository):
|
||||||
@ -72,7 +74,7 @@ class CityObject(Repository):
|
|||||||
with Session(self.engine) as session:
|
with Session(self.engine) as session:
|
||||||
session.query(Model).filter(Model.name == building.name, Model.city_id == city_id).update(
|
session.query(Model).filter(Model.name == building.name, Model.city_id == city_id).update(
|
||||||
{'name': building.name,
|
{'name': building.name,
|
||||||
'alias': building.alias,
|
'aliases': building.aliases,
|
||||||
'object_type': building.type,
|
'object_type': building.type,
|
||||||
'year_of_construction': building.year_of_construction,
|
'year_of_construction': building.year_of_construction,
|
||||||
'function': building.function,
|
'function': building.function,
|
||||||
@ -100,7 +102,61 @@ class CityObject(Repository):
|
|||||||
logging.error('Error while deleting application %s', err)
|
logging.error('Error while deleting application %s', err)
|
||||||
raise SQLAlchemyError from err
|
raise SQLAlchemyError from err
|
||||||
|
|
||||||
def get_by_name_or_alias_and_city(self, name, city_id) -> Model:
|
def building_in_cities_info(self, name, cities):
|
||||||
|
"""
|
||||||
|
Fetch a city object based on name and city id
|
||||||
|
:param name: city object name
|
||||||
|
:param cities: city identifiers
|
||||||
|
:return: [CityObject] with the provided name or alias belonging to the city with id city_id
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# search by name first
|
||||||
|
with Session(self.engine) as session:
|
||||||
|
city_object = session.execute(select(Model).where(
|
||||||
|
Model.name == name, Model.city_id.in_(cities))
|
||||||
|
).first()
|
||||||
|
if city_object is not None:
|
||||||
|
return city_object[0]
|
||||||
|
# name not found, so search by alias instead
|
||||||
|
city_objects = session.execute(
|
||||||
|
select(Model).where(Model.aliases.contains(name), Model.city_id.in_(cities))
|
||||||
|
).all()
|
||||||
|
for city_object in city_objects:
|
||||||
|
aliases = city_object[0].aliases.replace('{', '').replace('}', '').split(',')
|
||||||
|
for alias in aliases:
|
||||||
|
if alias == name:
|
||||||
|
# force the name as the alias
|
||||||
|
city_object[0].name = name
|
||||||
|
return city_object[0]
|
||||||
|
return None
|
||||||
|
except SQLAlchemyError as err:
|
||||||
|
logging.error('Error while fetching city object by name and city: %s', err)
|
||||||
|
raise SQLAlchemyError from err
|
||||||
|
except IndexError as err:
|
||||||
|
logging.error('Error while fetching city object by name and city, empty result %s', err)
|
||||||
|
raise IndexError from err
|
||||||
|
|
||||||
|
def get_by_name_or_alias_for_user_app(self, user_id, application_id, names) -> Union[Model, None]:
|
||||||
|
"""
|
||||||
|
Fetch city objects belonging to the user and application where the name or alias is in the names list
|
||||||
|
:param user_id: User ID
|
||||||
|
:param application_id: Application ID
|
||||||
|
:param names: a list of building aliases or names
|
||||||
|
:return [CityObject] or None
|
||||||
|
"""
|
||||||
|
with Session(self.engine) as session:
|
||||||
|
cities = session.execute(select(CityModel).where(
|
||||||
|
CityModel.user_id == user_id, CityModel.application_id == application_id
|
||||||
|
)).all()
|
||||||
|
ids = [c[0].id for c in cities]
|
||||||
|
buildings = session.execute(select(Model).where(
|
||||||
|
Model.city_id.in_(ids), Model.name.in_(names)
|
||||||
|
))
|
||||||
|
results = [r[0] for r in buildings]
|
||||||
|
print(ids, buildings)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_by_name_or_alias_and_city(self, name, city_id) -> Union[Model, None]:
|
||||||
"""
|
"""
|
||||||
Fetch a city object based on name and city id
|
Fetch a city object based on name and city id
|
||||||
:param name: city object name
|
:param name: city object name
|
||||||
@ -142,12 +198,12 @@ class CityObject(Repository):
|
|||||||
try:
|
try:
|
||||||
# search by name first
|
# search by name first
|
||||||
with Session(self.engine) as session:
|
with Session(self.engine) as session:
|
||||||
city_object = session.execute(select(Model).where(Model.name == name, Model.city_id in city_ids)).first()
|
city_object = session.execute(select(Model).where(Model.name == name, Model.city_id.in_(tuple(city_ids)))).first()
|
||||||
if city_object is not None:
|
if city_object is not None:
|
||||||
return city_object[0]
|
return city_object[0]
|
||||||
# name not found, so search by alias instead
|
# name not found, so search by alias instead
|
||||||
city_objects = session.execute(
|
city_objects = session.execute(
|
||||||
select(Model).where(Model.aliases.contains(name), Model.city_id in city_ids)
|
select(Model).where(Model.aliases.contains(name), Model.city_id.in_(tuple(city_ids)))
|
||||||
).all()
|
).all()
|
||||||
for city_object in city_objects:
|
for city_object in city_objects:
|
||||||
aliases = city_object[0].aliases.replace('{', '').replace('}', '').split(',')
|
aliases = city_object[0].aliases.replace('{', '').replace('}', '').split(',')
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
Hub version number
|
Hub version number
|
||||||
"""
|
"""
|
||||||
__version__ = '0.1.8.28'
|
__version__ = '0.1.8.36'
|
||||||
|
1
setup.py
1
setup.py
@ -100,6 +100,7 @@ setup(
|
|||||||
('hub/data/geolocation', glob.glob('hub/data/geolocation/*.txt')),
|
('hub/data/geolocation', glob.glob('hub/data/geolocation/*.txt')),
|
||||||
('hub/data/greenery', glob.glob('hub/data/greenery/*.xml')),
|
('hub/data/greenery', glob.glob('hub/data/greenery/*.xml')),
|
||||||
('hub/data/usage', glob.glob('hub/data/usage/*.xml')),
|
('hub/data/usage', glob.glob('hub/data/usage/*.xml')),
|
||||||
|
('hub/data/usage', glob.glob('hub/data/usage/*.json')),
|
||||||
('hub/data/usage', glob.glob('hub/data/usage/*.xlsx')),
|
('hub/data/usage', glob.glob('hub/data/usage/*.xlsx')),
|
||||||
('hub/data/weather', glob.glob('hub/data/weather/*.dat')),
|
('hub/data/weather', glob.glob('hub/data/weather/*.dat')),
|
||||||
('hub/data/weather/epw', glob.glob('hub/data/weather/epw/*.epw')),
|
('hub/data/weather/epw', glob.glob('hub/data/weather/epw/*.epw')),
|
||||||
|
@ -6,7 +6,6 @@ Project Coder Peter Yefi peteryefi@gmail.com
|
|||||||
"""
|
"""
|
||||||
import distutils.spawn
|
import distutils.spawn
|
||||||
import glob
|
import glob
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
@ -248,36 +247,36 @@ TestDBFactory
|
|||||||
for x in building.onsite_electrical_production[cte.MONTH]]
|
for x in building.onsite_electrical_production[cte.MONTH]]
|
||||||
yearly_on_site_electrical_production = [x * cte.WATTS_HOUR_TO_JULES
|
yearly_on_site_electrical_production = [x * cte.WATTS_HOUR_TO_JULES
|
||||||
for x in building.onsite_electrical_production[cte.YEAR]]
|
for x in building.onsite_electrical_production[cte.YEAR]]
|
||||||
results = json.dumps({cte.INSEL_MEB: [
|
results = {cte.INSEL_MEB: {
|
||||||
{'monthly_cooling_peak_load': monthly_cooling_peak_load},
|
'monthly_cooling_peak_load': monthly_cooling_peak_load,
|
||||||
{'yearly_cooling_peak_load': yearly_cooling_peak_load},
|
'yearly_cooling_peak_load': yearly_cooling_peak_load,
|
||||||
{'monthly_heating_peak_load': monthly_heating_peak_load},
|
'monthly_heating_peak_load': monthly_heating_peak_load,
|
||||||
{'yearly_heating_peak_load': yearly_heating_peak_load},
|
'yearly_heating_peak_load': yearly_heating_peak_load,
|
||||||
{'monthly_lighting_peak_load': monthly_lighting_peak_load},
|
'monthly_lighting_peak_load': monthly_lighting_peak_load,
|
||||||
{'yearly_lighting_peak_load': yearly_lighting_peak_load},
|
'yearly_lighting_peak_load': yearly_lighting_peak_load,
|
||||||
{'monthly_appliances_peak_load': monthly_appliances_peak_load},
|
'monthly_appliances_peak_load': monthly_appliances_peak_load,
|
||||||
{'yearly_appliances_peak_load': yearly_appliances_peak_load},
|
'yearly_appliances_peak_load': yearly_appliances_peak_load,
|
||||||
{'monthly_cooling_demand': monthly_cooling_demand},
|
'monthly_cooling_demand': monthly_cooling_demand,
|
||||||
{'yearly_cooling_demand': yearly_cooling_demand},
|
'yearly_cooling_demand': yearly_cooling_demand,
|
||||||
{'monthly_heating_demand': monthly_heating_demand},
|
'monthly_heating_demand': monthly_heating_demand,
|
||||||
{'yearly_heating_demand': yearly_heating_demand},
|
'yearly_heating_demand': yearly_heating_demand,
|
||||||
{'monthly_lighting_electrical_demand': monthly_lighting_electrical_demand},
|
'monthly_lighting_electrical_demand': monthly_lighting_electrical_demand,
|
||||||
{'yearly_lighting_electrical_demand': yearly_lighting_electrical_demand},
|
'yearly_lighting_electrical_demand': yearly_lighting_electrical_demand,
|
||||||
{'monthly_appliances_electrical_demand': monthly_appliances_electrical_demand},
|
'monthly_appliances_electrical_demand': monthly_appliances_electrical_demand,
|
||||||
{'yearly_appliances_electrical_demand': yearly_appliances_electrical_demand},
|
'yearly_appliances_electrical_demand': yearly_appliances_electrical_demand,
|
||||||
{'monthly_domestic_hot_water_heat_demand': monthly_domestic_hot_water_heat_demand},
|
'monthly_domestic_hot_water_heat_demand': monthly_domestic_hot_water_heat_demand,
|
||||||
{'yearly_domestic_hot_water_heat_demand': yearly_domestic_hot_water_heat_demand},
|
'yearly_domestic_hot_water_heat_demand': yearly_domestic_hot_water_heat_demand,
|
||||||
{'monthly_heating_consumption': monthly_heating_consumption},
|
'monthly_heating_consumption': monthly_heating_consumption,
|
||||||
{'yearly_heating_consumption': yearly_heating_consumption},
|
'yearly_heating_consumption': yearly_heating_consumption,
|
||||||
{'monthly_cooling_consumption': monthly_cooling_consumption},
|
'monthly_cooling_consumption': monthly_cooling_consumption,
|
||||||
{'yearly_cooling_consumption': yearly_cooling_consumption},
|
'yearly_cooling_consumption': yearly_cooling_consumption,
|
||||||
{'monthly_domestic_hot_water_consumption': monthly_domestic_hot_water_consumption},
|
'monthly_domestic_hot_water_consumption': monthly_domestic_hot_water_consumption,
|
||||||
{'yearly_domestic_hot_water_consumption': yearly_domestic_hot_water_consumption},
|
'yearly_domestic_hot_water_consumption': yearly_domestic_hot_water_consumption,
|
||||||
{'monthly_distribution_systems_electrical_consumption': monthly_distribution_systems_electrical_consumption},
|
'monthly_distribution_systems_electrical_consumption': monthly_distribution_systems_electrical_consumption,
|
||||||
{'yearly_distribution_systems_electrical_consumption': yearly_distribution_systems_electrical_consumption},
|
'yearly_distribution_systems_electrical_consumption': yearly_distribution_systems_electrical_consumption,
|
||||||
{'monthly_on_site_electrical_production': monthly_on_site_electrical_production},
|
'monthly_on_site_electrical_production': monthly_on_site_electrical_production,
|
||||||
{'yearly_on_site_electrical_production': yearly_on_site_electrical_production}
|
'yearly_on_site_electrical_production': yearly_on_site_electrical_production
|
||||||
]})
|
}}
|
||||||
|
|
||||||
db_building_id = _building.id
|
db_building_id = _building.id
|
||||||
city_objects_id.append(db_building_id)
|
city_objects_id.append(db_building_id)
|
||||||
@ -286,6 +285,8 @@ TestDBFactory
|
|||||||
results, city_object_id=db_building_id)
|
results, city_object_id=db_building_id)
|
||||||
self.assertEqual(17, len(city_objects_id), 'wrong number of results')
|
self.assertEqual(17, len(city_objects_id), 'wrong number of results')
|
||||||
self.assertIsNotNone(city_objects_id[0], 'city_object_id is None')
|
self.assertIsNotNone(city_objects_id[0], 'city_object_id is None')
|
||||||
|
results = control.database.results(control.user_id, control.application_id, request_values)
|
||||||
|
|
||||||
for _id in city_objects_id:
|
for _id in city_objects_id:
|
||||||
control.database.delete_results_by_name('insel meb', city_object_id=_id)
|
control.database.delete_results_by_name('insel meb', city_object_id=_id)
|
||||||
control.database.delete_city(city_id)
|
control.database.delete_city(city_id)
|
||||||
|
@ -5,11 +5,8 @@ Copyright © 2022 Concordia CERC group
|
|||||||
Project Coder Peter Yefi peteryefi@gmail.com
|
Project Coder Peter Yefi peteryefi@gmail.com
|
||||||
"""
|
"""
|
||||||
import distutils.spawn
|
import distutils.spawn
|
||||||
import glob
|
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import subprocess
|
|
||||||
import unittest
|
import unittest
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
@ -18,19 +15,7 @@ import sqlalchemy.exc
|
|||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
from sqlalchemy.exc import ProgrammingError
|
from sqlalchemy.exc import ProgrammingError
|
||||||
|
|
||||||
import hub.helpers.constants as cte
|
|
||||||
from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
|
|
||||||
from hub.exports.exports_factory import ExportsFactory
|
|
||||||
from hub.helpers.data.montreal_function_to_hub_function import MontrealFunctionToHubFunction
|
|
||||||
from hub.imports.construction_factory import ConstructionFactory
|
|
||||||
from hub.imports.energy_systems_factory import EnergySystemsFactory
|
|
||||||
from hub.imports.geometry_factory import GeometryFactory
|
|
||||||
from hub.imports.results_factory import ResultFactory
|
|
||||||
from hub.imports.usage_factory import UsageFactory
|
|
||||||
from hub.imports.weather_factory import WeatherFactory
|
|
||||||
from hub.persistence.db_control import DBControl
|
from hub.persistence.db_control import DBControl
|
||||||
from hub.persistence.models import City, Application, CityObject, SimulationResults
|
|
||||||
from hub.persistence.models import User, UserRoles
|
|
||||||
from hub.persistence.repository import Repository
|
from hub.persistence.repository import Repository
|
||||||
|
|
||||||
|
|
||||||
@ -129,24 +114,12 @@ TestDBFactory
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@unittest.skipIf(control.skip_test, control.skip_reason)
|
@unittest.skipIf(control.skip_test, control.skip_reason)
|
||||||
def test_retrieve_results(self):
|
def test_buildings_info(self):
|
||||||
request_values = {
|
request_values = {
|
||||||
"scenarios": [
|
"buildings": [
|
||||||
{
|
"01002777", "01002773", "01036804"
|
||||||
"current status": ["01002777", "01002773", "01036804"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"skin retrofit": ["01002777", "01002773", "01036804"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"system retrofit and pv": ["01002777", "01002773", "01036804"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"skin and system retrofit with pv": ["01002777", "01002773", "01036804"]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
results = control.database.results(control.user_id, control.application_id, request_values)
|
results = control.database.buildings_info(control.user_id, control.application_id, request_values)
|
||||||
|
self.assertEqual(12, len(results), 'wrong number of results')
|
||||||
print(results)
|
print(results)
|
||||||
|
@ -143,7 +143,6 @@ class TestExports(TestCase):
|
|||||||
EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
|
EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
|
||||||
UsageFactory('nrcan', city).enrich()
|
UsageFactory('nrcan', city).enrich()
|
||||||
WeatherFactory('epw', city).enrich()
|
WeatherFactory('epw', city).enrich()
|
||||||
print(self._output_path)
|
|
||||||
try:
|
try:
|
||||||
EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
|
EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
Loading…
Reference in New Issue
Block a user