Merge branch 'master' into shared_surfaces_method

This commit is contained in:
Pilar 2023-03-08 09:16:56 -05:00
commit d0cfa90dfe
23 changed files with 9613 additions and 670 deletions

View File

@ -40,7 +40,7 @@ class NrelCatalog(Catalog):
_catalog_windows = [] _catalog_windows = []
windows = self._constructions['library']['windows']['window'] windows = self._constructions['library']['windows']['window']
for window in windows: for window in windows:
frame_ratio = window['frame_ratio']['#text'] frame_ratio = float(window['frame_ratio']['#text'])
g_value = window['shgc'] g_value = window['shgc']
overall_u_value = float(window['conductivity']['#text']) / float(window['thickness']['#text']) overall_u_value = float(window['conductivity']['#text']) / float(window['thickness']['#text'])
name = window['@name'] name = window['@name']
@ -54,9 +54,9 @@ class NrelCatalog(Catalog):
for material in materials: for material in materials:
material_id = material['@id'] material_id = material['@id']
name = material['@name'] name = material['@name']
solar_absorptance = material['solar_absorptance']['#text'] solar_absorptance = float(material['solar_absorptance']['#text'])
thermal_absorptance = material['thermal_absorptance']['#text'] thermal_absorptance = float(material['thermal_absorptance']['#text'])
visible_absorptance = material['visible_absorptance']['#text'] visible_absorptance = float(material['visible_absorptance']['#text'])
no_mass = False no_mass = False
thermal_resistance = None, thermal_resistance = None,
conductivity = None, conductivity = None,
@ -64,11 +64,11 @@ class NrelCatalog(Catalog):
specific_heat = None specific_heat = None
if 'no_mass' in material and material['no_mass'] == 'true': if 'no_mass' in material and material['no_mass'] == 'true':
no_mass = True no_mass = True
thermal_resistance = material['thermal_resistance']['#text'] thermal_resistance = float(material['thermal_resistance']['#text'])
else: else:
conductivity = material['conductivity']['#text'] conductivity = float(material['conductivity']['#text'])
density = material['density']['#text'] density = float(material['density']['#text'])
specific_heat = material['specific_heat']['#text'] specific_heat = float(material['specific_heat']['#text'])
_material = Material(material_id, _material = Material(material_id,
name, name,
solar_absorptance, solar_absorptance,
@ -96,7 +96,7 @@ class NrelCatalog(Catalog):
material_id = layer['material'][0] material_id = layer['material'][0]
thickness = 0 thickness = 0
if 'thickness' in layer: if 'thickness' in layer:
thickness = layer['thickness']['#text'] thickness = float(layer['thickness']['#text'])
for material in self._catalog_materials: for material in self._catalog_materials:
if str(material_id) == str(material.id): if str(material_id) == str(material.id):
layers.append(Layer(layer_id, layer_name, material, thickness)) layers.append(Layer(layer_id, layer_name, material, thickness))
@ -114,18 +114,20 @@ class NrelCatalog(Catalog):
climate_zone = archetype['@climate_zone'] climate_zone = archetype['@climate_zone']
construction_period = \ construction_period = \
ConstructionHelper().reference_standard_to_construction_period[archetype['@reference_standard']] ConstructionHelper().reference_standard_to_construction_period[archetype['@reference_standard']]
average_storey_height = archetype['average_storey_height']['#text'] average_storey_height = float(archetype['average_storey_height']['#text'])
thermal_capacity = str(float(archetype['thermal_capacity']['#text']) * 1000) thermal_capacity = float(archetype['thermal_capacity']['#text']) * 1000
extra_loses_due_to_thermal_bridges = archetype['extra_loses_due_to_thermal_bridges']['#text'] extra_loses_due_to_thermal_bridges = float(archetype['extra_loses_due_to_thermal_bridges']['#text'])
indirect_heated_ratio = archetype['indirect_heated_ratio']['#text'] indirect_heated_ratio = float(archetype['indirect_heated_ratio']['#text'])
infiltration_rate_for_ventilation_system_off = archetype['infiltration_rate_for_ventilation_system_off']['#text'] infiltration_rate_for_ventilation_system_off = \
infiltration_rate_for_ventilation_system_on = archetype['infiltration_rate_for_ventilation_system_on']['#text'] float(archetype['infiltration_rate_for_ventilation_system_off']['#text'])
infiltration_rate_for_ventilation_system_on = \
float(archetype['infiltration_rate_for_ventilation_system_on']['#text'])
archetype_constructions = [] archetype_constructions = []
for archetype_construction in archetype['constructions']['construction']: for archetype_construction in archetype['constructions']['construction']:
for construction in self._catalog_constructions: for construction in self._catalog_constructions:
if construction.id == archetype_construction['@id']: if construction.id == archetype_construction['@id']:
window_ratio = archetype_construction['window_ratio']['#text'] window_ratio = float(archetype_construction['window_ratio']['#text'])
window_id = archetype_construction['window'] window_id = archetype_construction['window']
_construction = None _construction = None
_window = None _window = None

View File

@ -29,6 +29,7 @@ from hub.helpers.geometry_helper import GeometryHelper
from hub.helpers.location import Location from hub.helpers.location import Location
from hub.city_model_structure.energy_system import EnergySystem from hub.city_model_structure.energy_system import EnergySystem
from hub.city_model_structure.lca_material import LcaMaterial from hub.city_model_structure.lca_material import LcaMaterial
import pandas as pd
class City: class City:
@ -447,8 +448,31 @@ class City:
def merge(self, city) -> City: def merge(self, city) -> City:
_merge_city = self.copy _merge_city = self.copy
selected_city_object = None
building = None
# set initial minimum radiation to a higher number
min_radiation = 1999999
for city_object in city.city_objects: for city_object in city.city_objects:
_merge_city.add_city_object(city_object) if city_object.type == 'building':
building = city_object
building_radiation = 0
for surface in city_object.surfaces:
radiation = surface.global_irradiance
if 'year' not in radiation and 'month' not in radiation:
continue
elif "year" in radiation:
building_radiation += radiation["year"].iloc[0]
elif "month" in radiation and "year" not in radiation:
surface.global_irradiance["year"] = pd.DataFrame({radiation["month"].sum()})
building_radiation += radiation["year"].iloc[0]
if building_radiation < min_radiation:
min_radiation = building_radiation
selected_city_object = city_object
# merge the city object with the minimum radiation
if selected_city_object is not None:
_merge_city.add_city_object(selected_city_object)
else:
_merge_city.add_city_object(building)
return _merge_city return _merge_city
@property @property

View File

@ -130,10 +130,7 @@ class Idf:
self._idf.newidfobject(self._MATERIAL_NOMASS, self._idf.newidfobject(self._MATERIAL_NOMASS,
Name=layer.material.name, Name=layer.material.name,
Roughness=self._ROUGHNESS, Roughness=self._ROUGHNESS,
Thermal_Resistance=layer.material.thermal_resistance, Thermal_Resistance=layer.material.thermal_resistance
Thermal_Absorptance=layer.material.thermal_absorptance,
Solar_Absorptance=layer.material.solar_absorptance,
Visible_Absorptance=layer.material.visible_absorptance
) )
else: else:
self._idf.newidfobject(self._MATERIAL, self._idf.newidfobject(self._MATERIAL,

View File

@ -20,7 +20,7 @@ _CONSTRUCTION_CODE = {
cte.GROUND_WALL: '6', cte.GROUND_WALL: '6',
cte.ATTIC_FLOOR: '7', cte.ATTIC_FLOOR: '7',
cte.INTERIOR_SLAB: '8' cte.INTERIOR_SLAB: '8'
} }
class InselMonthlyEnergyBalance(Insel): class InselMonthlyEnergyBalance(Insel):
@ -35,8 +35,13 @@ class InselMonthlyEnergyBalance(Insel):
self._insel_files_paths.append(building.name + '.insel') self._insel_files_paths.append(building.name + '.insel')
file_name_out = building.name + '.out' file_name_out = building.name + '.out'
output_path = Path(self._path / file_name_out).resolve() output_path = Path(self._path / file_name_out).resolve()
self._contents.append(self.generate_meb_template(building, output_path, self._radiation_calculation_method, if building.internal_zones is not None:
self._weather_format)) for internal_zone in building.internal_zones:
if internal_zone.thermal_zones is None:
break
self._contents.append(
self.generate_meb_template(building, output_path, self._radiation_calculation_method,self._weather_format)
)
self._export() self._export()
def _export(self): def _export(self):

View File

@ -8,6 +8,7 @@ from hub.persistence import City
from hub.persistence import Application from hub.persistence import Application
from hub.persistence import User from hub.persistence import User
from hub.persistence import CityObject from hub.persistence import CityObject
from hub.persistence import SimulationResults
class DBFactory: class DBFactory:
@ -20,6 +21,7 @@ class DBFactory:
self._application = Application(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) self._application = Application(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path)
self._user = User(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) self._user = User(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path)
self._city_object = CityObject(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) self._city_object = CityObject(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path)
self._simulation_results = SimulationResults(db_name=db_name, dotenv_path=dotenv_path, app_env=app_env)
def get_city(self, city_id): def get_city(self, city_id):
""" """
@ -42,6 +44,34 @@ class DBFactory:
""" """
return self._city.get_by_user(user_id) return self._city.get_by_user(user_id)
def get_simulation_results_by_id(self, sim_id):
"""
Retrieve a single simulation results
:param sim_id: the id of the simulation results to get
"""
return self._simulation_results.get_simulation_results_by_id(sim_id)
def get_simulation_results_by_name(self, name):
"""
Retrieve a single simulation results
:param name: the name of the simulation results to get
"""
return self._simulation_results.get_simulation_results_by_name(name)
def get_simulation_results_by_city_id(self, city_id):
"""
Retrieve a single simulation results
:param city_id: the city id of the simulation results to get
"""
return self._simulation_results.get_simulation_results_by_city_id(city_id)
def get_simulation_results_by_city_object_id(self, city_object_id):
"""
Retrieve a single simulation results
:param city_object_id: the city object id of the simulation results to get
"""
return self._simulation_results.get_simulation_results_by_city_object_id(city_object_id)
def application_info(self, application_uuid): def application_info(self, application_uuid):
return self._application.get_by_uuid(application_uuid) return self._application.get_by_uuid(application_uuid)

View File

@ -6,6 +6,7 @@ Project CoderPeter Yefi peteryefi@gmail.com
""" """
from hub.persistence import User from hub.persistence import User
class UserFactory: class UserFactory:
""" """
UserFactory class UserFactory class
@ -14,17 +15,19 @@ class UserFactory:
def __init__(self, db_name, app_env, dotenv_path): def __init__(self, db_name, app_env, dotenv_path):
self._user_repo = User(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) self._user_repo = User(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path)
def login_user(self, email: str, password: str): def login_user(self, name: str, password: str, application_id: int):
""" """
Retrieve a single city from postgres Retrieve a single city from postgres
:param email: the email of the user :param name: the email of the user
:param password: the password of the user :param password: the password of the user
:param application_id: the id of the application accessing hub
""" """
return self._user_repo.get_user_by_email_and_password(email, password) return self._user_repo.get_by_name_application_and_password(name, password, application_id)
def get_user_by_email(self, email): def get_by_name_and_application(self, name: str, application: int):
""" """
Retrieve a single user Retrieve a single user
:param email: the email of the user to get :param name: user name
:param application: application accessing hub
""" """
return self._user_repo.get_by_email(email) return self._user_repo.get_by_name_and_application(name, application)

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@ log_dir = (Path(__file__).parent.parent / 'logs').resolve()
log_file = (log_dir / 'hub.log').resolve() log_file = (log_dir / 'hub.log').resolve()
try: try:
if not os.path.isfile(log_file): if not os.path.isfile(log_file):
if not os.path.exists:
os.mkdir(log_dir) os.mkdir(log_dir)
with open(log_file, 'x'): with open(log_file, 'x'):
pass pass

View File

@ -38,7 +38,7 @@ class NrcanPhysicsParameters:
archetype = self._search_archetype(nrcan_catalog, function, building.year_of_construction, self._climate_zone) archetype = self._search_archetype(nrcan_catalog, function, building.year_of_construction, self._climate_zone)
except KeyError: except KeyError:
sys.stderr.write(f'Building {building.name} has unknown construction archetype for building function: ' sys.stderr.write(f'Building {building.name} has unknown construction archetype for building function: '
f'{building.function} and building year of construction: {building.year_of_construction} ' f'{building.function}, building year of construction: {building.year_of_construction} '
f'and climate zone {self._climate_zone}\n') f'and climate zone {self._climate_zone}\n')
return return
# if building has no thermal zones defined from geometry, and the building will be divided in storeys, # if building has no thermal zones defined from geometry, and the building will be divided in storeys,
@ -51,7 +51,7 @@ class NrcanPhysicsParameters:
for thermal_zone in internal_zone.thermal_zones: for thermal_zone in internal_zone.thermal_zones:
thermal_zone.total_floor_area = thermal_zone.footprint_area thermal_zone.total_floor_area = thermal_zone.footprint_area
else: else:
number_of_storeys = int(float(building.eave_height) / float(building.average_storey_height)) number_of_storeys = int(building.eave_height / building.average_storey_height)
thermal_zone = building.internal_zones[0].thermal_zones[0] thermal_zone = building.internal_zones[0].thermal_zones[0]
thermal_zone.total_floor_area = thermal_zone.footprint_area * number_of_storeys thermal_zone.total_floor_area = thermal_zone.footprint_area * number_of_storeys
else: else:
@ -69,7 +69,7 @@ class NrcanPhysicsParameters:
nrcan_archetypes = nrcan_catalog.entries('archetypes') nrcan_archetypes = nrcan_catalog.entries('archetypes')
for building_archetype in nrcan_archetypes: for building_archetype in nrcan_archetypes:
construction_period_limits = building_archetype.construction_period.split('_') construction_period_limits = building_archetype.construction_period.split('_')
if int(construction_period_limits[0]) <= int(year_of_construction) < int(construction_period_limits[1]): if int(construction_period_limits[0]) <= year_of_construction < int(construction_period_limits[1]):
if (str(function) == str(building_archetype.function)) and \ if (str(function) == str(building_archetype.function)) and \
(climate_zone == str(building_archetype.climate_zone)): (climate_zone == str(building_archetype.climate_zone)):
return building_archetype return building_archetype
@ -135,12 +135,12 @@ class NrcanPhysicsParameters:
# The agreement is that the layers are defined from outside to inside # The agreement is that the layers are defined from outside to inside
external_layer = construction_archetype.layers[0] external_layer = construction_archetype.layers[0]
external_surface = thermal_boundary.parent_surface external_surface = thermal_boundary.parent_surface
external_surface.short_wave_reflectance = 1 - float(external_layer.material.solar_absorptance) external_surface.short_wave_reflectance = 1 - external_layer.material.solar_absorptance
external_surface.long_wave_emittance = 1 - float(external_layer.material.solar_absorptance) external_surface.long_wave_emittance = 1 - external_layer.material.solar_absorptance
internal_layer = construction_archetype.layers[len(construction_archetype.layers) - 1] internal_layer = construction_archetype.layers[len(construction_archetype.layers) - 1]
internal_surface = thermal_boundary.internal_surface internal_surface = thermal_boundary.internal_surface
internal_surface.short_wave_reflectance = 1 - float(internal_layer.material.solar_absorptance) internal_surface.short_wave_reflectance = 1 - internal_layer.material.solar_absorptance
internal_surface.long_wave_emittance = 1 - float(internal_layer.material.solar_absorptance) internal_surface.long_wave_emittance = 1 - internal_layer.material.solar_absorptance
for thermal_opening in thermal_boundary.thermal_openings: for thermal_opening in thermal_boundary.thermal_openings:
if construction_archetype.window is not None: if construction_archetype.window is not None:

View File

@ -58,7 +58,7 @@ class NrelPhysicsParameters:
for thermal_zone in internal_zone.thermal_zones: for thermal_zone in internal_zone.thermal_zones:
thermal_zone.total_floor_area = thermal_zone.footprint_area thermal_zone.total_floor_area = thermal_zone.footprint_area
else: else:
number_of_storeys = int(float(building.eave_height) / float(building.average_storey_height)) number_of_storeys = int(building.eave_height / building.average_storey_height)
thermal_zone = building.internal_zones[0].thermal_zones[0] thermal_zone = building.internal_zones[0].thermal_zones[0]
thermal_zone.total_floor_area = thermal_zone.footprint_area * number_of_storeys thermal_zone.total_floor_area = thermal_zone.footprint_area * number_of_storeys
else: else:
@ -78,7 +78,7 @@ class NrelPhysicsParameters:
construction_period_limits = building_archetype.construction_period.split(' - ') construction_period_limits = building_archetype.construction_period.split(' - ')
if construction_period_limits[1] == 'PRESENT': if construction_period_limits[1] == 'PRESENT':
construction_period_limits[1] = 3000 construction_period_limits[1] = 3000
if int(construction_period_limits[0]) <= int(year_of_construction) < int(construction_period_limits[1]): if int(construction_period_limits[0]) <= year_of_constructionF < int(construction_period_limits[1]):
if (str(function) == str(building_archetype.function)) and \ if (str(function) == str(building_archetype.function)) and \
(climate_zone == str(building_archetype.climate_zone)): (climate_zone == str(building_archetype.climate_zone)):
return building_archetype return building_archetype
@ -130,12 +130,12 @@ class NrelPhysicsParameters:
# The agreement is that the layers are defined from outside to inside # The agreement is that the layers are defined from outside to inside
external_layer = construction_archetype.layers[0] external_layer = construction_archetype.layers[0]
external_surface = thermal_boundary.parent_surface external_surface = thermal_boundary.parent_surface
external_surface.short_wave_reflectance = 1 - float(external_layer.material.solar_absorptance) external_surface.short_wave_reflectance = 1 - external_layer.material.solar_absorptance
external_surface.long_wave_emittance = 1 - float(external_layer.material.solar_absorptance) external_surface.long_wave_emittance = 1 - external_layer.material.solar_absorptance
internal_layer = construction_archetype.layers[len(construction_archetype.layers) - 1] internal_layer = construction_archetype.layers[len(construction_archetype.layers) - 1]
internal_surface = thermal_boundary.internal_surface internal_surface = thermal_boundary.internal_surface
internal_surface.short_wave_reflectance = 1 - float(internal_layer.material.solar_absorptance) internal_surface.short_wave_reflectance = 1 - internal_layer.material.solar_absorptance
internal_surface.long_wave_emittance = 1 - float(internal_layer.material.solar_absorptance) internal_surface.long_wave_emittance = 1 - internal_layer.material.solar_absorptance
for thermal_opening in thermal_boundary.thermal_openings: for thermal_opening in thermal_boundary.thermal_openings:
if construction_archetype.window is not None: if construction_archetype.window is not None:

View File

@ -7,6 +7,7 @@ Project CoderPeter Yefi peteryefi@gmail.com
from hub.city_model_structure.city import City from hub.city_model_structure.city import City
from hub.persistence import City as CityRepository from hub.persistence import City as CityRepository
from hub.persistence import SimulationResults from hub.persistence import SimulationResults
from hub.persistence import Application
class DBFactory: class DBFactory:
@ -17,6 +18,7 @@ class DBFactory:
def __init__(self, db_name, dotenv_path, app_env): def __init__(self, db_name, dotenv_path, app_env):
self._city_repository = CityRepository(db_name=db_name, dotenv_path=dotenv_path, app_env=app_env) self._city_repository = CityRepository(db_name=db_name, dotenv_path=dotenv_path, app_env=app_env)
self._simulation_results = SimulationResults(db_name=db_name, dotenv_path=dotenv_path, app_env=app_env) self._simulation_results = SimulationResults(db_name=db_name, dotenv_path=dotenv_path, app_env=app_env)
self._application = Application(db_name=db_name, dotenv_path=dotenv_path, app_env=app_env)
def persist_city(self, city: City, pickle_path, application_id: int, user_id: int): def persist_city(self, city: City, pickle_path, application_id: int, user_id: int):
""" """
@ -36,6 +38,24 @@ class DBFactory:
""" """
return self._city_repository.update(city_id, city) return self._city_repository.update(city_id, city)
def persist_application(self, name: str, description: str, application_uuid: str):
"""
Creates an application
:param name: name of application
:param description: the description of the application
:param application_uuid: the uuid of the application to be created
"""
return self._application.insert(name, description, application_uuid)
def update_application(self, name: str, description: str, application_uuid: str):
"""
Update an application
:param name: name of application
:param description: the description of the application
:param application_uuid: the uuid of the application to be created
"""
return self._application.update(application_uuid, name, description)
def delete_city(self, city_id): def delete_city(self, city_id):
""" """
Deletes a single city from postgres Deletes a single city from postgres
@ -43,6 +63,13 @@ class DBFactory:
""" """
self._city_repository.delete(city_id) self._city_repository.delete(city_id)
def delete_application(self, application_uuid):
"""
Deletes a single application from postgres
:param application_uuid: the id of the application to get
"""
self._application.delete(application_uuid)
def add_simulation_results(self, name, values, city_id=None, city_object_id=None): def add_simulation_results(self, name, values, city_id=None, city_object_id=None):
""" """
Add simulation results to the city or to the city_object Add simulation results to the city or to the city_object
@ -51,4 +78,4 @@ class DBFactory:
:param city_id: city id or None :param city_id: city id or None
:param city_object_id: city object id or None :param city_object_id: city object id or None
""" """
self._simulation_results.insert(name, values,city_id, city_object_id) self._simulation_results.insert(name, values, city_id, city_object_id)

View File

@ -35,8 +35,8 @@ class InselMonthlyEnergyBalance:
demand[i] = '0' demand[i] = '0'
heating.append(demand[0]) heating.append(demand[0])
cooling.append(demand[1]) cooling.append(demand[1])
monthly_heating = pd.DataFrame(heating, columns=[cte.INSEL_MEB]) monthly_heating = pd.DataFrame(heating, columns=[cte.INSEL_MEB]).astype(float)
monthly_cooling = pd.DataFrame(cooling, columns=[cte.INSEL_MEB]) monthly_cooling = pd.DataFrame(cooling, columns=[cte.INSEL_MEB]).astype(float)
return monthly_heating, monthly_cooling return monthly_heating, monthly_cooling
def enrich(self): def enrich(self):
@ -46,8 +46,8 @@ class InselMonthlyEnergyBalance:
if insel_output_file_path.is_file(): if insel_output_file_path.is_file():
building.heating[cte.MONTH], building.cooling[cte.MONTH] = self._demand(insel_output_file_path) building.heating[cte.MONTH], building.cooling[cte.MONTH] = self._demand(insel_output_file_path)
building.heating[cte.YEAR] = pd.DataFrame( building.heating[cte.YEAR] = pd.DataFrame(
[building.heating[cte.MONTH][cte.INSEL_MEB].sum()], columns=[cte.INSEL_MEB] [building.heating[cte.MONTH][cte.INSEL_MEB].astype(float).sum()], columns=[cte.INSEL_MEB]
) )
building.cooling[cte.YEAR] = pd.DataFrame( building.cooling[cte.YEAR] = pd.DataFrame(
[building.cooling[cte.MONTH][cte.INSEL_MEB].sum()], columns=[cte.INSEL_MEB] [building.cooling[cte.MONTH][cte.INSEL_MEB].astype(float).sum()], columns=[cte.INSEL_MEB]
) )

View File

@ -4,6 +4,7 @@ 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
""" """
from hub.persistence import User from hub.persistence import User
from hub.persistence import UserRoles from hub.persistence import UserRoles
@ -16,17 +17,17 @@ class UserFactory:
def __init__(self, db_name, app_env, dotenv_path): def __init__(self, db_name, app_env, dotenv_path):
self._user_repo = User(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) self._user_repo = User(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path)
def create_user(self, name: str, email: str, password: str, role: UserRoles): def create_user(self, name: str, application_id: int, password: str, role: UserRoles):
""" """
Creates a new user Creates a new user
:param name: the name of the user :param name: the name of the user
:param email: the email of the user :param application_id: the application id of the user
:param password: the password of the user :param password: the password of the user
:param role: the role of the user :param role: the role of the user
""" """
return self._user_repo.insert(name, email, password, role) return self._user_repo.insert(name, password, role, application_id)
def update_user(self, user_id: int, name: str, email: str, password: str, role: UserRoles): def update_user(self, user_id: int, name: str, password: str, role: UserRoles):
""" """
Creates a new user Creates a new user
:param user_id: the id of the user :param user_id: the id of the user
@ -35,11 +36,11 @@ class UserFactory:
:param password: the password of the user :param password: the password of the user
:param role: the role of the user :param role: the role of the user
""" """
return self._user_repo.update(user_id, name, email, password, role) return self._user_repo.update(user_id, name, password, role)
def delete_user(self, user_id): def delete_user(self, user_id):
""" """
Retrieve a single user Retrieve a single user
:param user_id: the id of the user to delete :param user_id: the id of the user to delete
""" """
return self._user_repo.delete_user(user_id) return self._user_repo.delete(user_id)

View File

@ -12,6 +12,7 @@ from sqlalchemy import DateTime
from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.dialects.postgresql import JSONB
from hub.persistence.configuration import Models from hub.persistence.configuration import Models
class SimulationResults(Models): class SimulationResults(Models):
""" """
A model representation of an application A model representation of an application

View File

@ -42,6 +42,7 @@ class Application(Repository):
if application is None: if application is None:
try: try:
application = Model(name=name, description=description, application_uuid=application_uuid) application = Model(name=name, description=description, application_uuid=application_uuid)
self.session.add(application) self.session.add(application)
self.session.commit() self.session.commit()
return application return application

View File

@ -6,7 +6,6 @@ Project Coder Peter Yefi peteryefi@gmail.com
""" """
import datetime import datetime
import pickle
from typing import Union, Dict from typing import Union, Dict
from sqlalchemy import select from sqlalchemy import select
@ -60,6 +59,9 @@ class City(Repository):
for building in city.buildings: for building in city.buildings:
object_usage = '' object_usage = ''
for internal_zone in building.internal_zones: for internal_zone in building.internal_zones:
if internal_zone is None or internal_zone.usages is None:
object_usage = 'Unknown'
else:
for usage in internal_zone.usages: for usage in internal_zone.usages:
object_usage = f'{object_usage}{usage.name}_{usage.percentage} ' object_usage = f'{object_usage}{usage.name}_{usage.percentage} '
object_usage = object_usage.rstrip() object_usage = object_usage.rstrip()
@ -101,6 +103,7 @@ class City(Repository):
:return: a city :return: a city
""" """
try: try:
self.session.query(CityObject).filter(CityObject.city_id == city_id).delete()
self.session.query(Model).filter(Model.id == city_id).delete() self.session.query(Model).filter(Model.id == city_id).delete()
self.session.commit() self.session.commit()
except SQLAlchemyError as err: except SQLAlchemyError as err:

View File

@ -125,6 +125,50 @@ class SimulationResults(Repository):
except SQLAlchemyError as err: except SQLAlchemyError as err:
logger.error(f'Error while fetching city by city_id: {err}') logger.error(f'Error while fetching city by city_id: {err}')
def get_simulation_results_by_id(self, sim_id) -> [Model]:
"""
Fetch simulation results by id
:param sim_id: a simulation result identifier
:return: [Model] with the provided sim_id
"""
try:
return self.session.execute(select(Model).where(Model.id == sim_id)).first()
except SQLAlchemyError as err:
logger.error(f'Error while fetching simulation results by sim_id: {err}')
def get_simulation_results_by_name(self, name) -> [Model]:
"""
Fetch simulation results by name
:param name: the name of the simulation results
:return: [Model] with the provided name
"""
try:
return self.session.execute(select(Model).where(Model.name == name))
except SQLAlchemyError as err:
logger.error(f'Error while fetching simulation results by name: {err}')
def get_simulation_results_by_city_id(self, city_id) -> [Model]:
"""
Fetch simulation results by name
:param city_id: the id of the city
:return: [Model] with the provided city id
"""
try:
return self.session.execute(select(Model).where(Model.city_id == city_id))
except SQLAlchemyError as err:
logger.error(f'Error while fetching simulation results by name: {err}')
def get_simulation_results_by_city_object_id(self, city_object_id) -> [Model]:
"""
Fetch simulation results by name
:param city_object_id: the id of the city object
:return: [Model] with the provided city object id
"""
try:
return self.session.execute(select(Model).where(Model.city_object_id == city_object_id))
except SQLAlchemyError as err:
logger.error(f'Error while fetching simulation results by name: {err}')
def get_city_object(self, city_object_id) -> [CityObject]: def get_city_object(self, city_object_id) -> [CityObject]:
""" """
Fetch a city object based city id Fetch a city object based city id

View File

@ -6,8 +6,12 @@ Project Coder Guille Gutierrez Guillermo.GutierrezMorote@concordia.ca
""" """
from pathlib import Path from pathlib import Path
from unittest import TestCase from unittest import TestCase
from hub.imports.geometry_factory import GeometryFactory from hub.imports.geometry_factory import GeometryFactory
import platform
from hub.exports.exports_factory import ExportsFactory
import subprocess
from subprocess import SubprocessError, TimeoutExpired, CalledProcessError
from hub.imports.results_factory import ResultFactory
class TestCityMerge(TestCase): class TestCityMerge(TestCase):
@ -20,6 +24,16 @@ class TestCityMerge(TestCase):
:return: None :return: None
""" """
self._example_path = (Path(__file__).parent / 'tests_data').resolve() self._example_path = (Path(__file__).parent / 'tests_data').resolve()
self._output_path = (Path(__file__).parent / 'tests_outputs').resolve()
self._weather_file = (self._example_path / 'CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').resolve()
self._climate_file = (self._example_path / 'New_York.cli').resolve()
self._executable = None
if platform.system() == 'Linux':
self._executable = 'citysim_sra'
if platform.system() == 'Darwin':
self._executable = 'citysim_sra'
elif platform.system() == 'Windows':
self._executable = 'shortwave_integer'
def _get_citygml(self, file): def _get_citygml(self, file):
file_path = (self._example_path / file).resolve() file_path = (self._example_path / file).resolve()
@ -34,3 +48,23 @@ class TestCityMerge(TestCase):
self.assertEqual(len(city_1.city_objects), 1, 'Wrong amount of city_objects found in city_1') self.assertEqual(len(city_1.city_objects), 1, 'Wrong amount of city_objects found in city_1')
self.assertEqual(len(city_2.city_objects), 1, 'Wrong amount of city_objects found in city_2') self.assertEqual(len(city_2.city_objects), 1, 'Wrong amount of city_objects found in city_2')
self.assertEqual(len(city.city_objects), 2, 'Wrong amount of city_objects found in city') self.assertEqual(len(city.city_objects), 2, 'Wrong amount of city_objects found in city')
def test_merge_with_radiation(self):
city_one = self._get_citygml('one_building_in_kelowna.gml')
city_two = self._get_citygml('pluto_building.gml')
city_two.name = 'New_York'
city_two.climate_file = self._climate_file
try:
ExportsFactory(export_type='sra', city=city_two, path=self._output_path, weather_file=self._weather_file,
target_buildings=city_two.buildings, weather_format='epw').export()
subprocess.run([self._executable, f'{str(self._output_path)}/{city_two.name}_sra.xml'], stdout=subprocess.DEVNULL)
except (SubprocessError, TimeoutExpired, CalledProcessError) as error:
raise Exception(error)
ResultFactory('sra', city_two, self._output_path).enrich()
merged_city = city_one.merge(city_two)
self.assertEqual(len(merged_city.buildings), 2)
self.assertEqual(merged_city.buildings[1].surfaces[0].global_irradiance['year'].iloc[0], 254.3453196347032)
self.assertEqual(merged_city.buildings[0].surfaces[0].global_irradiance, {})
self.assertEqual(merged_city.buildings[0].surfaces[2].global_irradiance, {})
self.assertEqual(city_one.buildings[0].surfaces[0].global_irradiance, merged_city.buildings[0].surfaces[0].global_irradiance)
self.assertEqual(city_two.buildings[0].surfaces[0].global_irradiance, merged_city.buildings[1].surfaces[0].global_irradiance)

View File

@ -269,7 +269,7 @@ class TestConstructionFactory(TestCase):
path=file_path, path=file_path,
height_field='citygml_me', height_field='citygml_me',
year_of_construction_field='ANNEE_CONS', year_of_construction_field='ANNEE_CONS',
function_field='LIBELLE_UT', function_field='CODE_UTILI',
function_to_hub=Dictionaries().montreal_function_to_hub_function).city function_to_hub=Dictionaries().montreal_function_to_hub_function).city
ConstructionFactory('nrcan', city).enrich() ConstructionFactory('nrcan', city).enrich()

View File

@ -9,12 +9,12 @@ from hub.imports.geometry_factory import GeometryFactory
from hub.imports.db_factory import DBFactory from hub.imports.db_factory import DBFactory
from hub.imports.user_factory import UserFactory from hub.imports.user_factory import UserFactory
from hub.exports.db_factory import DBFactory as ExportDBFactory from hub.exports.db_factory import DBFactory as ExportDBFactory
from hub.persistence.base_repo import BaseRepo from hub.persistence.repository import Repository
from sqlalchemy import create_engine from sqlalchemy import create_engine
from hub.persistence.models import City from hub.persistence.models import City, Application, CityObject
from hub.persistence.models import User, UserRoles from hub.persistence.models import User, UserRoles
from pickle import loads
from sqlalchemy.exc import ProgrammingError from sqlalchemy.exc import ProgrammingError
import uuid
class TestDBFactory(TestCase): class TestDBFactory(TestCase):
@ -29,8 +29,8 @@ class TestDBFactory(TestCase):
:return: None :return: None
""" """
# Create test database # Create test database
repo = BaseRepo(db_name='test_db', app_env='TEST', dotenv_path='/usr/local/etc/hub/.env') repo = Repository(db_name='test_db', app_env='TEST', dotenv_path='/usr/local/etc/hub/.env')
eng = create_engine(f'postgresql://{repo.config.get_db_user()}@/{repo.config.get_db_user()}') eng = create_engine(f'postgresql://{repo.configuration.get_db_user()}@/{repo.configuration.get_db_user()}')
try: try:
# delete test database if it exists # delete test database if it exists
@ -45,56 +45,58 @@ class TestDBFactory(TestCase):
cnn.execute('commit') cnn.execute('commit')
cnn.execute("CREATE DATABASE test_db") cnn.execute("CREATE DATABASE test_db")
cnn.close() cnn.close()
Application.__table__.create(bind=repo.engine, checkfirst=True)
User.__table__.create(bind=repo.engine, checkfirst=True) User.__table__.create(bind=repo.engine, checkfirst=True)
City.__table__.create(bind=repo.engine, checkfirst=True) City.__table__.create(bind=repo.engine, checkfirst=True)
CityObject.__table__.create(bind=repo.engine, checkfirst=True)
city_file = "tests_data/C40_Final.gml" city_file = "tests_data/C40_Final.gml"
cls.city = GeometryFactory('citygml', city_file).city cls.city = GeometryFactory('citygml', city_file).city
cls._db_factory = DBFactory(db_name='test_db', app_env='TEST', dotenv_path='../.env') cls._db_factory = DBFactory(db_name='test_db', app_env='TEST', dotenv_path='../.env')
cls._export_db_factory = ExportDBFactory(db_name='test_db', app_env='TEST', dotenv_path='../.env') cls._export_db_factory = ExportDBFactory(db_name='test_db', app_env='TEST', dotenv_path='../.env')
user_factory = UserFactory(db_name='test_db', app_env='TEST', dotenv_path='../.env') user_factory = UserFactory(db_name='test_db', app_env='TEST', dotenv_path='../.env')
cls._user = user_factory.create_user("Admin", "admin@hub.com", "Admin@123", UserRoles.Admin) cls.unique_id = str(uuid.uuid4())
cls.application = cls._db_factory.persist_application("test", "test application", cls.unique_id)
cls._user = user_factory.create_user("Admin", cls.application.id, "Admin@123", UserRoles.Admin)
cls.pickle_path = 'tests_data/pickle_path.bz2'
def test_save_application(self):
self.assertEqual(self.application.name, "test")
self.assertEqual(self.application.description, "test application")
self.assertEqual(str(self.application.application_uuid), self.unique_id)
def test_save_city(self): def test_save_city(self):
saved_city = self._db_factory.persist_city(self._user.id, self.city) self.city.name = "Montréal"
saved_city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id)
self.assertEqual(saved_city.name, 'Montréal') self.assertEqual(saved_city.name, 'Montréal')
pickled_city = loads(saved_city.city) self.assertEqual(saved_city.pickle_path, self.pickle_path)
self.assertEqual(len(pickled_city.buildings), 10) self.assertEqual(saved_city.level_of_detail, self.city.level_of_detail.geometry)
self.assertEqual(pickled_city.buildings[0].floor_area, 1990.9913970530033)
self._db_factory.delete_city(saved_city.id) self._db_factory.delete_city(saved_city.id)
def test_save_same_city_with_same_hub_version(self):
first_city = self._db_factory.persist_city(self._user.id, self.city)
second_city = self._db_factory.persist_city(self._user.id, self.city)
self.assertEqual(second_city['message'], f'Same version of {self.city.name} exist')
self.assertEqual(first_city.name, 'Montréal')
self.assertEqual(first_city.country_code, 'ca')
self._db_factory.delete_city(first_city.id)
def test_get_city_by_name(self): def test_get_city_by_name(self):
city = self._db_factory.persist_city(self._user.id, self.city) city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id)
retrieved_city = self._export_db_factory.get_city_by_name(city.name) retrieved_city = self._export_db_factory.get_city_by_name(city.name)
self.assertEqual(retrieved_city[0].lower_corner[0], 610610.7547462888) self.assertEqual(retrieved_city[0].application_id, 1)
self._db_factory.delete_city(city.id)
def test_get_city_by_user(self):
city = self._db_factory.persist_city(self._user.id, self.city)
retrieved_city = self._export_db_factory.get_city_by_user(self._user.id)
self.assertEqual(retrieved_city[0].user_id, self._user.id) self.assertEqual(retrieved_city[0].user_id, self._user.id)
self._db_factory.delete_city(city.id) self._db_factory.delete_city(city.id)
def test_get_city_by_user(self):
city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id)
retrieved_city = self._export_db_factory.get_city_by_user(self._user.id)
self.assertEqual(retrieved_city[0].pickle_path, self.pickle_path)
self._db_factory.delete_city(city.id)
def test_get_city_by_id(self): def test_get_city_by_id(self):
city = self._db_factory.persist_city(self._user.id, self.city) city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id)
retrieved_city = self._export_db_factory.get_city(city.id) retrieved_city = self._export_db_factory.get_city(city.id)
self.assertEqual(retrieved_city.upper_corner[0], 610818.6731258357) self.assertEqual(retrieved_city.level_of_detail, self.city.level_of_detail.geometry)
self._db_factory.delete_city(city.id) self._db_factory.delete_city(city.id)
def test_get_update_city(self): def test_get_update_city(self):
city = self._db_factory.persist_city(self._user.id, self.city) city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id)
self.city.longitude = 1.43589 self.city.name = "Ottawa"
self.city.latitude = -9.38928339
self._db_factory.update_city(city.id, self.city) self._db_factory.update_city(city.id, self.city)
updated_city = self._export_db_factory.get_city(city.id) updated_city = self._export_db_factory.get_city(city.id)
self.assertEqual(updated_city.longitude, 1.43589) self.assertEqual(updated_city.name, self.city.name)
self.assertEqual(updated_city.latitude, -9.38928339)
self._db_factory.delete_city(city.id) self._db_factory.delete_city(city.id)

View File

@ -156,6 +156,6 @@ class TestGeometryFactory(TestCase):
city = self._get_city(file, 'geojson', city = self._get_city(file, 'geojson',
height_field='citygml_me', height_field='citygml_me',
year_of_construction_field='ANNEE_CONS', year_of_construction_field='ANNEE_CONS',
function_field='LIBELLE_UT') function_field='CODE_UTILI')
GeometryHelper.city_mapping(city, plot=False) GeometryHelper.city_mapping(city, plot=False)
self.assertTrue(False) self.assertTrue(False)

File diff suppressed because it is too large Load Diff

Binary file not shown.