Compare commits

..

6 Commits

23 changed files with 26741 additions and 1365 deletions

View File

@ -9,7 +9,6 @@ import datetime
import flask import flask
import yaml import yaml
from pathlib import Path
from flasgger import LazyJSONEncoder, Swagger from flasgger import LazyJSONEncoder, Swagger
from flask import Response from flask import Response
from flask_restful import Api from flask_restful import Api
@ -18,20 +17,12 @@ import threading
import hub_api.helpers.session_helper as sh import hub_api.helpers.session_helper as sh
from hub_api.control.session import SessionStart, SessionEnd, KeepSessionAlive from hub_api.control.session import SessionStart, SessionEnd, KeepSessionAlive
from hub_api.control.uptime import Uptime from hub_api.control.uptime import Uptime
from hub_api.persistence.full_retrofit_results import FullRetrofitResults from hub_api.buildings.meb import Meb
from hub_api.persistence.retrofit_results import RetrofitResults from hub_api.geolocation.reverse import Reverse
from hub_api.workflow.insel_montly_energy_balance import InselMonthlyEnergyBalance
from hub_api.workflow.costs import Costs
from hub_api.workflow.energy_plus import EnergyPlus
from hub_api.workflow.glb import Glb
from hub_api.energy_plus.idf_generator import IdfGenerator
from hub_api.config import Config
sh.begin_time = datetime.datetime.now() sh.begin_time = datetime.datetime.now()
app = flask.Flask('cerc_api') app = flask.Flask('cerc_api')
app.json_provider_class = LazyJSONEncoder app.json_provider_class = LazyJSONEncoder
app.config['MAX_CONTENT_LENGTH'] = int(Config.max_file_size())
api = Api(app) api = Api(app)
api.add_resource(Uptime, '/v1.4/uptime') api.add_resource(Uptime, '/v1.4/uptime')
@ -39,35 +30,26 @@ api.add_resource(Uptime, '/v1.4/uptime')
# Session # Session
api.add_resource(SessionStart, '/v1.4/session/start') api.add_resource(SessionStart, '/v1.4/session/start')
api.add_resource(SessionEnd, '/v1.4/session/end') api.add_resource(SessionEnd, '/v1.4/session/end')
api.add_resource(KeepSessionAlive, '/v1.4/session/keep-alive') api.add_resource(KeepSessionAlive, '/v1.4/session/keep_alive')
# persistence # Buildings
api.add_resource(RetrofitResults, '/v1.4/persistence/retrofit-results') api.add_resource(Meb, '/v1.4/buildings/meb')
api.add_resource(FullRetrofitResults, '/v1.4/persistence/full-retrofit-results')
# energy plus # GeoLocation
api.add_resource(IdfGenerator, '/v1.4/energy-plus/idf-generator') api.add_resource(Reverse, '/v1.4/geolocation/reverse/<latitude>/<longitude>', endpoint='reverse')
# workflows with open("hub_api/docs/openapi-specs.yml", "r") as stream:
api.add_resource(Costs, '/v1.4/workflow/costs')
api.add_resource(EnergyPlus, '/v1.4/workflow/energy-plus')
api.add_resource(Glb, '/v1.4/workflow/glb')
api.add_resource(InselMonthlyEnergyBalance, '/v1.4/workflow/insel-monthly-energy-balance')
yml_path = Path('./docs/openapi-specs.yml').resolve()
with open(yml_path, "r") as stream:
swagger_config = { swagger_config = {
"headers": [], "headers": [],
"specs": [ "specs": [
{ {
"endpoint": '/api/apispec', "endpoint": '/api/apispec/',
"route": '/api/apispec/apispec.json', "route": '/api/apispec/apispec.json',
"rule_filter": lambda rule: True, # all in "rule_filter": lambda rule: True, # all in
"model_filter": lambda tag: True, # all in "model_filter": lambda tag: True, # all in
} }
], ],
"static_url_path": "/api/static", "static_url_path": "/api/static/",
"specs_route": "/api/api-docs/", "specs_route": "/api/api-docs/",
"openapi": "3.0.0" "openapi": "3.0.0"
} }
@ -81,8 +63,7 @@ with open(yml_path, "r") as stream:
def home(): def home():
return Response(headers={'Access-Control-Allow-Origin': '*'}) return Response(headers={'Access-Control-Allow-Origin': '*'})
sh.debug_mode = True
sh.debug_mode = False
threading.Thread(target=sh.expired_sessions_collector, daemon=True, args="5").start() threading.Thread(target=sh.expired_sessions_collector, daemon=True, args="5").start()
app.run(port=15789, host="0.0.0.0", debug=sh.debug_mode) app.run(port=15789, host="0.0.0.0", debug=sh.debug_mode)

40
hub_api/buildings/meb.py Normal file
View File

@ -0,0 +1,40 @@
import json
from flask import Response, request
from flask_restful import Resource
from hub_api.config import Config
from hub_api.helpers.session_helper import session, refresh_session
class Meb(Resource, Config):
def __init__(self):
super().__init__()
def post(self):
"""
API call for requesting a specified list of enriched buildings
"""
session_id = request.headers.get('session_id', None)
token = request.headers.get('token', None)
application_uuid = request.headers.get('application_uuid', None)
_session = refresh_session(session_id, token, application_uuid)
if _session is None:
return Response(json.dumps({'error': 'unauthorized'}), status=403)
token = {'token': _session['token']}
application_id = session(session_id)['application_id']
user_id = session(session_id)['user_id']
payload = request.get_json()
results = self.export_db_factory.results(user_id, application_id, payload)
if results == {}:
# no data found for the given parameters
return Response(json.dumps({'result': 'succeed', 'results': results}), status=200, headers=token)
# deserialize the response to return pure json
city_name = next(iter(results))
for building_results in results[city_name]:
values = []
for value in building_results['insel meb']:
key = next(iter(value))
values.append({key: json.loads(value[key])})
building_results['insel meb'] = values
return Response(json.dumps({'result': 'succeed', 'results': results}), status=200, headers=token)

View File

@ -3,73 +3,24 @@ Config
SPDX - License - Identifier: LGPL - 3.0 - or -later SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Project Peter Yefi peteryefi@gmail.com Copyright © 2023 Project Peter Yefi peteryefi@gmail.com
""" """
import distutils
import os import os
import platform import platform
from pathlib import Path from pathlib import Path
from dotenv import load_dotenv
import hub.helpers.dictionaries from hub.exports.db_factory import DBFactory as CityExportFactory
from hub.persistence.db_control import DBControl from hub.imports.db_factory import DBFactory
from hub.persistence.repository import Repository
from hub.catalog_factories.energy_systems_catalog_factory import EnergySystemsCatalogFactory
from pymongo import MongoClient
class Config: class Config:
def __init__(self): def __init__(self):
dotenv_path = "{}/.local/etc/hub_api/.env".format(os.path.expanduser('~')) dotenv_path = "{}".format(os.path.expanduser('~'))
if platform.system() == 'Linux': if platform.system() == 'Linux':
dotenv_path = Path(dotenv_path).resolve() dotenv_path = f'{dotenv_path}/.local/etc/hub_api/.env'
environment = 'PROD' environment = 'TEST'
load_dotenv(dotenv_path=dotenv_path) database_name = 'montreal_retrofit_test'
self._database_name = os.getenv(f'{environment}_DB_NAME')
self._database = DBControl(db_name=self._database_name, app_env=environment, dotenv_path=dotenv_path)
self._repository = Repository(db_name=self._database_name, app_env=environment, dotenv_path=dotenv_path)
self._energy_systems_catalog = EnergySystemsCatalogFactory('montreal_custom').catalog
self._dictionaries = {
'pluto': hub.helpers.dictionaries.Dictionaries().pluto_function_to_hub_function,
'htf': hub.helpers.dictionaries.Dictionaries().hft_function_to_hub_function,
'montreal': hub.helpers.dictionaries.Dictionaries().montreal_function_to_hub_function,
'alkis': hub.helpers.dictionaries.Dictionaries().alkis_function_to_hub_function,
'eilat': hub.helpers.dictionaries.Dictionaries().eilat_function_to_hub_function
}
# mongodb
_mongodb = os.getenv(f'{environment}_MONGO_DB')
_mongodb_database = os.getenv(f'{environment}_MONGO_DB_DATABASE')
self._mongodb_collection_prefix = os.getenv(f'{environment}_MONGO_DB_COLLECTION_PREFIX')
_client = MongoClient(_mongodb)
self._montreal_retrofit_db = _client[_mongodb_database]
self.export_db_factory = CityExportFactory(db_name=database_name, app_env=environment,
@property dotenv_path=dotenv_path)
def database(self): self.import_db_factory = DBFactory(db_name=database_name, app_env=environment,
return self._database dotenv_path=dotenv_path)
@property
def repository(self):
return self._repository
@property
def energy_systems_catalog(self):
return self._energy_systems_catalog
@staticmethod
def max_file_size():
return 10 * 1024 * 1024 # 10 MB
@property
def insel(self):
return distutils.spawn.find_executable('insel')
@property
def sra(self):
return distutils.spawn.find_executable('sra')
@property
def montreal_retrofit_db(self):
return self._montreal_retrofit_db
@property
def mongodb_collection_prefix(self):
return self._mongodb_collection_prefix

View File

@ -7,7 +7,7 @@ Copyright © 2022 Project Author name guillermo.gutierrezmorote@concordia.ca
import datetime import datetime
import json import json
import uuid import uuid
from sqlalchemy.exc import SQLAlchemyError
from flask import request, Response from flask import request, Response
from flask_restful import Resource from flask_restful import Resource
@ -22,13 +22,9 @@ class SessionStart(Resource, Config):
def put(self): def put(self):
username = request.headers.get('username', None) username = request.headers.get('username', None)
password = request.headers.get('password', None) password = request.headers.get('password', None)
try: application_uuid = request.headers.get('application-uuid', None)
application_uuid = uuid.UUID(request.headers.get('application-uuid', None))
user_info = self.database.user_login(name=username, password=password, application_uuid=application_uuid)
except (ValueError, SQLAlchemyError, TypeError):
return Response(json.dumps({'error': 'unauthorized'}), status=403)
ip = request.remote_addr ip = request.remote_addr
print(f'received valid login from {ip} {user_info}') user_info = self.export_db_factory.user_login(name=username, password=password, application_uuid=application_uuid)
if user_info: if user_info:
session_id = str(uuid.uuid4()) session_id = str(uuid.uuid4())
token = str(uuid.uuid4()) token = str(uuid.uuid4())
@ -40,45 +36,45 @@ class SessionStart(Resource, Config):
'application_id': user_info.application_id, 'application_id': user_info.application_id,
'application_uuid': application_uuid, 'application_uuid': application_uuid,
'ip': ip, 'ip': ip,
'scenarios': [] 'cities': []
} }
cities = self.database.cities_by_user_and_application(user_info.id, user_info.application_id) cities = self.export_db_factory.cities_by_user_and_application(user_info.id, user_info.application_id)
for city in cities: for city in cities:
if city.scenario not in session['scenarios']: session['cities'].append({
session['scenarios'].append(city.scenario) "name": city.name,
"geometric_level_of_detail": city.level_of_detail
})
sessions[session_id] = session sessions[session_id] = session
response = Response(json.dumps({'scenarios': session['scenarios'], 'result': 'OK'}), status=200) response = Response(json.dumps({'cities': session['cities'], 'result': 'OK'}), status=200)
response.headers['session_id'] = session_id response.headers['session_id'] = session_id
response.headers['token'] = token response.headers['token'] = token
return response return response
return Response(json.dumps({'error': 'unauthorized'}), status=403) return Response(json.dumps({'error': 'unauthorized'}), status=403)
class SessionEnd(Resource): class SessionEnd(Resource):
def __init__(self): def __init__(self):
pass pass
@staticmethod @staticmethod
def put(): def put():
session_id = request.headers.get('session-id', None) session_id = request.headers.get('session_id', None)
token = request.headers.get('token', None) token = request.headers.get('token', None)
application_uuid = request.headers.get('application-uuid', None) application_uuid = request.headers.get('application_uuid', None)
if remove_session(session_id, token, application_uuid): if remove_session(session_id, token, application_uuid):
return Response(json.dumps({'result': 'succeed'}), status=200) return Response(json.dumps({'result': 'succeed'}), status=200)
return Response(json.dumps({'error': 'unauthorized'}), status=403) return Response(json.dumps({'error': 'unauthorized'}), status=403)
class KeepSessionAlive(Resource): class KeepSessionAlive(Resource):
def __init__(self): def __init__(self):
pass pass
@staticmethod @staticmethod
def put(): def put():
session_id = request.headers.get('session-id', None) session_id = request.headers.get('session_id', None)
token = request.headers.get('token', None) token = request.headers.get('token', None)
application_uuid = request.headers.get('application-uuid', None) application_uuid = request.headers.get('application_uuid', None)
_session = refresh_session(session_id, token, application_uuid) _session = refresh_session(session_id, token, application_uuid)
if _session is None: if _session is None:

26456
hub_api/data/cities15000.txt Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,54 +0,0 @@
import datetime
import json
import os
from pathlib import Path
from flask import request, Response, make_response, send_file
from flask_restful import Resource
from hub.city_model_structure.city import City
from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
from hub.version import __version__ as version
from hub_api.config import Config
from hub_api.helpers.session_helper import refresh_session, session
class IdfGenerator(Resource, Config):
def __init__(self):
self._tmp_path = (Path(__file__).parent / 'tmp').resolve()
super().__init__()
def post(self):
"""
API call generate the IDF file for the input data
"""
session_id = request.headers.get('session-id', None)
token = request.headers.get('token', None)
application_uuid = request.headers.get('application-uuid', None)
_session = refresh_session(session_id, token, application_uuid)
if _session is None:
return Response(json.dumps({'error': 'unauthorized'}), status=403)
else:
token = _session['token']
application_id = session(session_id)['application_id']
user_id = session(session_id)['user_id']
tmp_path = (self._tmp_path / token).resolve()
try:
os.mkdir(tmp_path)
except FileExistsError:
pass
payload = request.get_json()
for key, value in payload.items():
db_city = self.database.get_city(self.database.building(value, user_id, application_id, key).city_id)
if version != db_city.hub_release:
return Response(json.dumps({
'error': 'The selected building belongs to an old hub release and cannot be loaded.'
}), status=422)
idf_file = tmp_path/db_city.name
city = City.load_compressed(db_city.pickle_path, idf_file)
EnergyBuildingsExportsFactory('idf', city, tmp_path, target_buildings=[value]).export()
response = make_response(send_file(idf_file))
response.headers['token'] = token
return response

View File

@ -1,2 +0,0 @@
*
!.gitignore

View File

@ -0,0 +1,43 @@
import json
import math
from pathlib import Path
from flask import Response, request
from flask.views import MethodView
from hub_api.config import Config
from hub_api.helpers.session_helper import refresh_session
class Reverse(MethodView, Config):
def __init__(self):
super().__init__()
self._reverse_path = Path(Path(__file__).parent.parent / 'data/cities15000.txt').resolve()
def get(self, latitude: float, longitude: float):
session_id = request.headers.get('session_id', None)
token = request.headers.get('token', None)
application_uuid = request.headers.get('application_uuid', None)
_session = refresh_session(session_id, token, application_uuid)
if _session is None:
return Response(json.dumps({'error': 'unauthorized'}), status=403)
token = {'token': _session['token']}
latitude = float(latitude)
longitude = float(longitude)
distance = math.inf
country = 'unknown'
city = 'unknown'
with open(self._reverse_path, 'r') as f:
for line_number, line in enumerate(f):
fields = line.split('\t')
file_city_name = fields[1]
file_latitude = float(fields[4])
file_longitude = float(fields[5])
file_country_code = fields[8]
new_distance = math.sqrt(pow((latitude-file_latitude),2) + pow((longitude-file_longitude),2))
if distance > new_distance:
distance = new_distance
country = file_country_code
city = file_city_name
return Response(json.dumps({'country': country, 'city':city}), status=200, headers=token)

View File

@ -3,13 +3,9 @@ Session helper
SPDX - License - Identifier: LGPL - 3.0 - or -later SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Project Author name guillermo.gutierrezmorote@concordia.ca Copyright © 2022 Project Author name guillermo.gutierrezmorote@concordia.ca
""" """
import datetime
import shutil
import time
import uuid import uuid
from copy import deepcopy import datetime
from glob import glob import time
from pathlib import Path
sessions = {} sessions = {}
begin_time = None begin_time = None
@ -20,26 +16,17 @@ construction_catalog = None
usage_catalog = None usage_catalog = None
debug_mode = False debug_mode = False
def expired_sessions_collector(session_timeout_duration): def expired_sessions_collector(session_timeout_duration):
""" """
Goes through each session in sessions and removes expired session(s) Goes through each session in sessions and removes expired session(s)
""" """
while True: while True:
if bool(sessions): if bool(sessions):
_sessions = deepcopy(sessions) for _session in list(sessions):
for session_uuid in _sessions: _expire = datetime.datetime.strptime(sessions[session]['expire'], '%Y-%m-%d %H:%M:%S.%f')
_expire = datetime.datetime.strptime(_sessions[session_uuid]['expire'], '%Y-%m-%d %H:%M:%S.%f')
if _expire < datetime.datetime.now(): if _expire < datetime.datetime.now():
print("session for user: ", _sessions[session_uuid]['user'], "expired.") print("session with session_id: ", session, "expired.")
response_path = (Path(__file__).parent.parent / f'response_files/{session_uuid}').resolve() del sessions[session]
if response_path.exists():
shutil.rmtree(response_path)
del sessions[session_uuid]
existing_directories = glob(f'{Path(__file__).parent.parent.resolve()}/response_files/*')
for directory in existing_directories:
if directory not in _sessions.keys():
shutil.rmtree(directory)
time.sleep(60 * int(session_timeout_duration)) time.sleep(60 * int(session_timeout_duration))
@ -51,7 +38,7 @@ def _validate_session(session_id, token, application_uuid):
_session = session(session_id) _session = session(session_id)
if debug_mode: if debug_mode:
token = _session['token'] token = _session['token']
return bool(_session) and (_session['token'] == token) and str(_session['application_uuid']) == application_uuid return bool(_session) and (_session['token'] == token) and _session['application_uuid'] == application_uuid
except KeyError: except KeyError:
return False return False

View File

@ -1,231 +0,0 @@
"""
Mockup Building module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
import hub.helpers.constants as cte
from hub.helpers.dictionaries import Dictionaries
from hub_api.mockup.properties import *
class Building:
"""
Building class
"""
def __init__(self, building_info, results, catalog_archetype):
self._function = building_info.function
self._area = building_info.area
self._volume = building_info.volume
self._total_heating_area = building_info.total_heating_area
self._wall_area = building_info.wall_area
self._windows_area = building_info.windows_area
self._roof_area = building_info.roof_area
self._total_pv_area = building_info.total_pv_area
self._energy_systems_archetype_name = building_info.system_name
self._heating_consumption = {
cte.YEAR: results['insel meb']['yearly_heating_consumption'],
cte.MONTH: results['insel meb']['monthly_heating_consumption']
}
self._cooling_consumption = {
cte.YEAR: results['insel meb']['yearly_cooling_consumption'],
cte.MONTH: results['insel meb']['monthly_cooling_consumption']
}
self._domestic_hot_water_consumption = {
cte.YEAR: results['insel meb']['yearly_domestic_hot_water_consumption'],
cte.MONTH: results['insel meb']['monthly_domestic_hot_water_consumption']
}
self._lighting_electrical_demand = {
cte.YEAR: results['insel meb']['yearly_lighting_electrical_demand'],
cte.MONTH: results['insel meb']['monthly_lighting_electrical_demand']
}
self._appliances_electrical_demand = {
cte.YEAR: results['insel meb']['yearly_appliances_electrical_demand'],
cte.MONTH: results['insel meb']['monthly_appliances_electrical_demand']
}
self._heating_peak_load = {
cte.YEAR: results['insel meb']['yearly_heating_peak_load'],
cte.MONTH: results['insel meb']['monthly_heating_peak_load']
}
self._cooling_peak_load = {
cte.YEAR: results['insel meb']['yearly_cooling_peak_load'],
cte.MONTH: results['insel meb']['monthly_cooling_peak_load']
}
self._lighting_peak_load = {
cte.YEAR: results['insel meb']['yearly_lighting_peak_load'],
cte.MONTH: results['insel meb']['monthly_lighting_peak_load']
}
self._appliances_peak_load = {
cte.YEAR: results['insel meb']['yearly_appliances_peak_load'],
cte.MONTH: results['insel meb']['monthly_appliances_peak_load']
}
self._onsite_electrical_production = {
cte.YEAR: results['insel meb']['yearly_on_site_electrical_production'],
cte.MONTH: results['insel meb']['monthly_on_site_electrical_production']
}
self._catalog_archetype = catalog_archetype
@property
def function(self):
"""
Get building function
:return: str
"""
return self._function
@property
def volume(self):
"""
Get building volume in m3
:return: float
"""
return self._volume
@property
def thermal_zones_from_internal_zones(self) -> [ThermalZone]:
"""
Get building thermal zones
:return: [ThermalZone]
"""
ground = ThermalBoundary()
ground.type = 'Ground'
ground.opaque_area = self._area
roof = ThermalBoundary()
roof.type = 'Roof'
roof.opaque_area = self._roof_area
wall = ThermalBoundary()
wall.type = 'Wall'
wall.opaque_area = self._wall_area
wall.window_ratio = self._windows_area / (self._windows_area + self._wall_area)
thermal_zone = ThermalZone()
thermal_zone.total_floor_area = self._total_heating_area
thermal_zone.thermal_boundaries = [roof, wall, ground]
return [thermal_zone]
@property
def roofs(self) -> [Roof]:
"""
Get building roofs
:return: [Roof]
"""
polygon = Polygon()
polygon.area = self._roof_area
roof = Roof()
roof.solid_polygon = polygon
roof.solar_collectors_area_reduction_factor = self._total_pv_area / self._roof_area
return [roof]
@property
def heating_consumption(self):
"""
Get building heating consumption in J
:return: dict
"""
return self._heating_consumption
@property
def cooling_consumption(self):
"""
Get building cooling consumption in J
:return: dict
"""
return self._cooling_consumption
@property
def domestic_hot_water_consumption(self):
"""
Get building domestic hot water consumption in J
:return: dict
"""
return self._domestic_hot_water_consumption
@property
def lighting_electrical_demand(self):
"""
Get building lighting demand in J
:return: dict
"""
return self._lighting_electrical_demand
@property
def appliances_electrical_demand(self):
"""
Get building appliances electrical demand in J
:return: dict
"""
return self._appliances_electrical_demand
@property
def heating_peak_load(self):
"""
Get building heating peak load in W
:return: dict
"""
return self._heating_peak_load
@property
def cooling_peak_load(self):
"""
Get building cooling peak load in W
:return: dict
"""
return self._cooling_peak_load
@property
def lighting_peak_load(self):
"""
Get building lighting peak load in W
:return: dict
"""
return self._lighting_peak_load
@property
def appliances_peak_load(self):
"""
Get building appliances peak load in W
:return: dict
"""
return self._appliances_peak_load
@property
def onsite_electrical_production(self):
"""
Get building onsite electrical production in J
:return: dict
"""
return self._onsite_electrical_production
@property
def energy_systems_archetype_name(self):
"""
Get energy systems archetype name
:return: dict
"""
return self._energy_systems_archetype_name
@property
def energy_systems(self) -> [EnergySystem]:
"""
Get building energy systems
:return: [EnergySystem]
"""
_energy_systems = []
for system in self._catalog_archetype.systems:
_hub_demand_types = []
for demand_type in system.demand_types:
# todo: generalize this when we have more catalogs
_hub_demand_types.append(Dictionaries().montreal_demand_type_to_hub_energy_demand_type[demand_type])
demands = _hub_demand_types
fuel_type = Dictionaries().montreal_custom_fuel_to_hub_fuel[system.generation_systems[0].fuel_type]
generic_generation_system = GenericGenerationSystem()
generic_generation_system.fuel_type = fuel_type
generation_system = GenerationSystem()
generation_system.generic_generation_system = generic_generation_system
energy_system = EnergySystem()
energy_system.generation_system = generation_system
energy_system.demand_types = demands
_energy_systems.append(energy_system)
return _energy_systems

View File

@ -1,267 +0,0 @@
"""
Properties module to be used by mockup building
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from __future__ import annotations
class EnergySystem:
"""
EnergySystem class
"""
def __init__(self):
self._generation_system = None
self._demand_types = None
@property
def generation_system(self) -> GenerationSystem:
"""
Get building generation system
:return: GenerationSystem
"""
return self._generation_system
@generation_system.setter
def generation_system(self, value):
"""
Set building generation system
:param value: GenerationSystem
"""
self._generation_system = value
@property
def demand_types(self):
"""
Get demand types covered by an energy system
:return: [str]
"""
return self._demand_types
@demand_types.setter
def demand_types(self, value):
"""
Set demand types covered by an energy system
:param value: [str]
"""
self._demand_types = value
class GenerationSystem:
"""
GenerationSystem class
"""
def __init__(self):
self._generic_generation_system = None
@property
def generic_generation_system(self) -> GenericGenerationSystem:
"""
Get generic generation system
:return: GenericGenerationSystem
"""
return self._generic_generation_system
@generic_generation_system.setter
def generic_generation_system(self, value):
"""
Set generic generation system
:param value: GenericGenerationSystem
"""
self._generic_generation_system = value
class GenericGenerationSystem:
"""
GenericGenerationSystem class
"""
def __init__(self):
self._fuel_type = None
@property
def fuel_type(self):
"""
Get fuel type of generic generation system
:return: str
"""
return self._fuel_type
@fuel_type.setter
def fuel_type(self, value):
"""
Set fuel type of generic generation system
:param value: str
"""
self._fuel_type = value
class ThermalZone:
"""
ThermalZone class
"""
def __init__(self):
self._thermal_boundaries = None
self._total_floor_area = None
@property
def thermal_boundaries(self) -> [ThermalBoundary]:
"""
Get thermal boundaries
:return: [ThermalBoundary]
"""
return self._thermal_boundaries
@thermal_boundaries.setter
def thermal_boundaries(self, value):
"""
Set thermal boundaries
:param value: [ThermalBoundary]
"""
self._thermal_boundaries = value
@property
def total_floor_area(self):
"""
Get total floor area in m2
:return: float
"""
return self._total_floor_area
@total_floor_area.setter
def total_floor_area(self, value):
"""
Get total floor area in m2
:param value: float
"""
self._total_floor_area = value
class ThermalBoundary:
"""
ThermalBoundary class
"""
def __init__(self):
self._type = None
self._opaque_area = None
self._window_ratio = None
@property
def type(self):
"""
Get thermal boundary type
:return: str
"""
return self._type
@type.setter
def type(self, value):
"""
Set thermal boundary type
:param value: str
"""
self._type = value
@property
def opaque_area(self):
"""
Get thermal boundary opaque area in m2
:return: float
"""
return self._opaque_area
@opaque_area.setter
def opaque_area(self, value):
"""
Set thermal boundary opaque area in m2
:param value: float
"""
self._opaque_area = value
@property
def window_ratio(self):
"""
Get thermal boundary window ratio
:return: float
"""
return self._window_ratio
@window_ratio.setter
def window_ratio(self, value):
"""
Set thermal boundary window ratio
:param value: float
"""
self._window_ratio = value
class Roof:
"""
Roof class
"""
def __init__(self):
self._solid_polygon = None
self._solar_collectors_area_reduction_factor = None
@property
def solid_polygon(self) -> Polygon:
"""
Get solid polygon
:return: Polygon
"""
return self._solid_polygon
@solid_polygon.setter
def solid_polygon(self, value):
"""
Set solid polygon
:param value: Polygon
"""
self._solid_polygon = value
@property
def solar_collectors_area_reduction_factor(self):
"""
Get solar-collectors-area reduction factor
:return: float
"""
return self._solar_collectors_area_reduction_factor
@solar_collectors_area_reduction_factor.setter
def solar_collectors_area_reduction_factor(self, value):
"""
Set solar-collectors-area reduction factor
:param value: float
"""
self._solar_collectors_area_reduction_factor = value
class Polygon:
"""
Polygon class
"""
def __init__(self):
self._area = None
@property
def area(self):
"""
Get polygon area in m2
:return: float
"""
return self._area
@area.setter
def area(self, value):
"""
Set polygon area in m2
:param value: float
"""
self._area = value

View File

@ -1,45 +0,0 @@
import json
from flask import Response, request
from flask_restful import Resource
from hub_api.config import Config
from hub_api.helpers.session_helper import refresh_session
from hub_api.persistence.mock import dic
class FullRetrofitResults(Resource, Config):
def __init__(self):
super().__init__()
def post(self):
session_id = request.headers.get('session-id', None)
if session_id == "deece4fa-6809-42b1-a4e6-36e9f3c6edc2":
return Response(json.dumps(dic), status=200)
token = request.headers.get('token', None)
application_uuid = request.headers.get('application-uuid', None)
_session = refresh_session(session_id, token, application_uuid)
results = {'current status': [],
'skin retrofit': [],
'system retrofit and pv': [],
'skin and system retrofit with pv': []
}
if _session is None:
return Response(json.dumps({'error': 'unauthorized'}), status=403)
else:
response_token = {'token': _session['token']}
json_request = request.get_json()
for scenario in json_request['scenarios']:
for key, buildings in scenario.items():
mongodb_collection = f'{self.mongodb_collection_prefix}{key.replace(" ", "_")}'
building_query = ''
for building in buildings:
building_query = f'{building_query} {{"alias": "{building}"}},'
query = f'{{"$or": [{building_query[:-1]}]}}'
cursor = self.montreal_retrofit_db[mongodb_collection].find(json.loads(query))
for result in cursor:
del result['_id']
result['building'] = result['alias']
results[key].append(result)
return Response(json.dumps({'result': 'succeed', 'results': results}), status=200, headers=response_token)

File diff suppressed because one or more lines are too long

View File

@ -1,142 +0,0 @@
import json
import threading
from co2_emission.co2_emission import Co2Emission
from costs.cost import Cost
from flask import Response, request
from flask_restful import Resource
from hub_api.config import Config
from hub_api.helpers.session_helper import session, refresh_session
from hub_api.mockup.building import Building
from hub_api.persistence.mock import dic
class RetrofitResults(Resource, Config):
def __init__(self):
super().__init__()
self._scenario_ids = {'current status': 0,
'skin retrofit': 1,
'system retrofit and pv': 2,
'skin and system retrofit with pv': 3
}
def _calculate_building(self, building_results, user_id, application_id, scenario, scenario_id):
building_info = self.database.building(building_results['building'], user_id, application_id, scenario)
archetype = self.energy_systems_catalog.get_entry(building_info.system_name)
mockup_building = Building(building_info, building_results, archetype)
life_cycle = Cost(mockup_building, retrofit_scenario=scenario_id).life_cycle
operational_co2 = Co2Emission(mockup_building).operational_co2
global_capital_costs = life_cycle[f'Scenario {scenario_id}']['global_capital_costs']
global_operational_costs = life_cycle[f'Scenario {scenario_id}']['global_operational_costs']
global_capital_incomes = life_cycle[f'Scenario {scenario_id}']['global_capital_incomes']
global_maintenance_costs = life_cycle[f'Scenario {scenario_id}']['global_maintenance_costs']
building_results['total_heating_area'] = building_info.total_heating_area
building_results['year_of_construction'] = building_info.year_of_construction
building_results['function'] = building_info.function
building_results['costs'] = {
'total_capital_costs_skin': life_cycle[f'Scenario {scenario_id}']['total_capital_costs_skin'],
'total_capital_costs_systems': life_cycle[f'Scenario {scenario_id}']['total_capital_costs_systems'],
'end_of_life_costs': life_cycle[f'Scenario {scenario_id}']['end_of_life_costs'],
'total_operational_costs': life_cycle[f'Scenario {scenario_id}']['total_operational_costs'],
'total_maintenance_costs': life_cycle[f'Scenario {scenario_id}']['total_maintenance_costs'],
'operational_incomes': life_cycle[f'Scenario {scenario_id}']['operational_incomes'],
'capital_incomes': life_cycle[f'Scenario {scenario_id}']['capital_incomes'],
'global_capital_costs': {
'B2010_opaque_walls': global_capital_costs['B2010_opaque_walls'].tolist(),
'B2020_transparent': global_capital_costs['B2020_transparent'].tolist(),
'B3010_opaque_roof': global_capital_costs['B3010_opaque_roof'].tolist(),
'B10_superstructure': global_capital_costs['B10_superstructure'].tolist(),
'D3020_heat_generating_systems': global_capital_costs['D3020_heat_generating_systems'].tolist(),
'D3030_cooling_generation_systems': global_capital_costs['D3030_cooling_generation_systems'].tolist(),
'D3080_other_hvac_ahu': global_capital_costs['D3080_other_hvac_ahu'].tolist(),
'D5020_lighting_and_branch_wiring': global_capital_costs['D5020_lighting_and_branch_wiring'].tolist(),
'D301010_photovoltaic_system': global_capital_costs['D301010_photovoltaic_system'].tolist(),
},
'global_end_of_life_costs': life_cycle[f'Scenario {scenario_id}']['global_end_of_life_costs'][
'End_of_life_costs'].tolist(),
'global_operational_costs': {
'fixed_costs_electricity_peak': global_operational_costs['Fixed_costs_electricity_peak'].tolist(),
'fixed_costs_electricity_monthly': global_operational_costs['Fixed_costs_electricity_monthly'].tolist(),
'variable_costs_electricity': global_operational_costs['Variable_costs_electricity'].tolist(),
'fixed_costs_gas': global_operational_costs['Fixed_costs_gas'].tolist(),
'variable_costs_gas': global_operational_costs['Variable_costs_gas'].tolist()
},
'global_maintenance_costs': {
'heating_maintenance': global_maintenance_costs['Heating_maintenance'].tolist(),
'cooling_maintenance': global_maintenance_costs['Cooling_maintenance'].tolist(),
'pv_maintenance': global_maintenance_costs['PV_maintenance'].tolist(),
},
'global_operational_incomes': life_cycle[f'Scenario {scenario_id}']['global_operational_incomes'][
'Incomes electricity'].tolist(),
'global_capital_incomes': {
'subsidies_construction': global_capital_incomes['Subsidies construction'].tolist(),
'subsidies_hvac': global_capital_incomes['Subsidies HVAC'].tolist(),
'subsidies_pv': global_capital_incomes['Subsidies PV'].tolist()
}
}
building_results['operational_co2'] = operational_co2
def post(self):
"""
API call for requesting a specified list of enriched persistence
"""
# todo: cost and co2 libs are using default canadians values, in the future need to be optionally API configurable
session_id = request.headers.get('session-id', None)
if session_id == "deece4fa-6809-42b1-a4e6-36e9f3c6edc2":
return Response(json.dumps(dic), status=200)
token = request.headers.get('token', None)
application_uuid = request.headers.get('application-uuid', None)
_session = refresh_session(session_id, token, application_uuid)
if _session is None:
return Response(json.dumps({'error': 'unauthorized'}), status=403)
else:
token = {'token': _session['token']}
application_id = session(session_id)['application_id']
user_id = session(session_id)['user_id']
payload = request.get_json()
if 'scenarios' not in payload:
return Response(json.dumps({'error': 'Bad request'}), status=400, headers=token)
results = self.database.results(user_id, application_id, payload)
if results == {}:
# no data found for the given parameters
return Response(json.dumps({'result': 'succeed', 'results': results}), status=200, headers=token)
# deserialize the response to return pure json
t = []
for scenario in results:
scenario_id = self._scenario_ids[scenario]
for building_results in results[scenario]:
f = threading.Thread(
target=self._calculate_building,
args=(building_results, user_id, application_id, scenario, scenario_id)
)
t.append(f)
f.start()
for f in t:
f.join()
return Response(json.dumps({'result': 'succeed', 'results': results}), status=200, headers=token)
def get(self):
session_id = request.headers.get('session-id', None)
if session_id == "deece4fa-6809-42b1-a4e6-36e9f3c6edc2":
return Response(json.dumps(dic), status=200)
token = request.headers.get('token', None)
application_uuid = request.headers.get('application-uuid', None)
_session = refresh_session(session_id, token, application_uuid)
results = {'meb': []}
if _session is None:
return Response(json.dumps({'error': 'unauthorized'}), status=403)
else:
response_token = {'token': _session['token']}
buildings = request.get_json()['buildings']
building_query = ''
for building in buildings:
building_query = f'{building_query} {{"alias": "{building}"}},'
query = f'{{"$or": [{building_query[:-1]}]}}'
cursor = self.mongodb_meb.find(json.loads(query))
for result in cursor:
del result['_id']
results['meb'].append(result)
return Response(json.dumps({'result': 'succeed', 'results': results}), status=200, headers=response_token)

View File

@ -1,2 +0,0 @@
*
!.gitignore

View File

@ -1,16 +0,0 @@
from unittest import TestCase
from hub_api.config import Config
from hub.persistence.models import City, Application, CityObject, SimulationResults, User
class TestApi(TestCase):
def test_create_database(self):
config = Config()
Application.__table__.create(bind=config.repository.engine, checkfirst=True)
User.__table__.create(bind=config.repository.engine, checkfirst=True)
City.__table__.create(bind=config.repository.engine, checkfirst=True)
CityObject.__table__.create(bind=config.repository.engine, checkfirst=True)
SimulationResults.__table__.create(bind=config.repository.engine, checkfirst=True)
config.database.create_user('Administrator', )
self.assertTrue(True)

View File

@ -1,14 +0,0 @@
from flask_restful import Resource
from hub_api.config import Config
class Costs(Resource, Config):
def __init__(self):
super().__init__()
def post(self):
"""
API call for performing the cost workflow
"""
raise NotImplementedError()

View File

@ -1,117 +0,0 @@
import json
import os
import shutil
import zipfile
from pathlib import Path
from flask import Response, request, send_file, make_response
from flask_restful import Resource
from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
from hub.imports.construction_factory import ConstructionFactory
from hub.imports.geometry_factory import GeometryFactory
from hub.imports.usage_factory import UsageFactory
from hub.imports.weather_factory import WeatherFactory
from werkzeug.utils import secure_filename
from hub_api.config import Config
from hub_api.helpers.session_helper import refresh_session
class EnergyPlus(Resource, Config):
def __init__(self):
super().__init__()
self._extensions = ['.geojson', '.gml']
self._tmp_path = (Path(__file__).parent / 'tmp').resolve()
self._response_path = (Path(__file__).parent.parent / 'response_files').resolve()
self._city = None
def _allowed_extensions(self, filename):
self._file_extension = Path(filename).suffix
return self._file_extension in self._extensions
def _geojson(self, file_path):
try:
height_field = request.form.get('height_field')
year_of_construction_field = request.form.get('year_of_construction_field')
function_field = request.form.get('function_field')
function_dictionary = self._dictionaries[request.form.get('function_to_hub')]
return GeometryFactory('geojson',
path=file_path,
height_field=height_field,
year_of_construction_field=year_of_construction_field,
function_field=function_field,
function_to_hub=function_dictionary).city
except KeyError:
return None
def _citygml(self, file_path):
try:
year_of_construction_field = request.form.get('year_of_construction_field')
function_field = request.form.get('function_field')
function_dictionary = self._dictionaries[request.form.get('function_dictionary_name')]
hub_crs = request.form.get('hub_crs')
return GeometryFactory('citygml',
path=file_path,
year_of_construction_field=year_of_construction_field,
function_field=function_field,
function_to_hub=function_dictionary,
hub_crs=hub_crs).city
except KeyError:
return None
def post(self):
"""
API call for performing the monthly energy balance workflow
"""
session_id = request.headers.get('session-id', None)
token = request.headers.get('token', None)
application_uuid = request.headers.get('application-uuid', None)
_session = refresh_session(session_id, token, application_uuid)
if _session is None:
return Response(json.dumps({'error': 'unauthorized'}), status=403)
else:
response_token = {'token': _session['token']}
tmp_path = (self._tmp_path / token).resolve()
response_path = (self._response_path / session_id).resolve()
try:
os.mkdir(tmp_path)
except FileExistsError:
pass
try:
os.mkdir(response_path)
except FileExistsError:
pass
geometry_file = request.files['geometry_file']
if not self._allowed_extensions(geometry_file.filename):
shutil.rmtree(tmp_path)
return Response(json.dumps({'error': 'Unsupported media type'}), status=415, headers=response_token)
filename = secure_filename(geometry_file.filename)
file_path = os.path.join(tmp_path, filename)
geometry_file.save(file_path)
if self._file_extension == '.geojson':
self._city = self._geojson(file_path)
else:
self._city = self._citygml(file_path)
if self._city is None:
shutil.rmtree(tmp_path)
return Response(json.dumps({'error': 'Bad request'}), status=400, headers=response_token)
construction_handler = request.form.get('construction_handler')
usage_handler = request.form.get('usage_handler')
WeatherFactory('epw', self._city).enrich()
ConstructionFactory(construction_handler, self._city).enrich()
UsageFactory(usage_handler, self._city).enrich()
_idf = EnergyBuildingsExportsFactory('idf', self._city, tmp_path).export()
_idf.run()
result_files = [
str((tmp_path / f'{self._city.name}_out.csv').resolve()),
str((tmp_path / f'{self._city.name}_out.eso').resolve()),
str((tmp_path / f'{self._city.name}.idf').resolve()),
]
result_zip = (response_path / f'{token}.zip').resolve()
with zipfile.ZipFile(result_zip, 'w') as zf:
for result_file in result_files:
zf.write(result_file, Path(result_file).name)
shutil.rmtree(tmp_path)
response = make_response(send_file(result_zip))
response.headers['token'] = token
return response

View File

@ -1,105 +0,0 @@
import glob
import json
import os
import shutil
import zipfile
from pathlib import Path
from flask import Response, request, send_file, make_response
from flask_restful import Resource
from hub.exports.exports_factory import ExportsFactory
from hub.imports.geometry_factory import GeometryFactory
from werkzeug.utils import secure_filename
from hub_api.config import Config
from hub_api.helpers.session_helper import refresh_session
class Glb(Resource, Config):
def __init__(self):
super().__init__()
self._extensions = ['.geojson', '.gml']
self._tmp_path = (Path(__file__).parent / 'tmp').resolve()
self._response_path = (Path(__file__).parent.parent / 'response_files').resolve()
self._city = None
def _allowed_extensions(self, filename):
self._file_extension = Path(filename).suffix
return self._file_extension in self._extensions
def _geojson(self, file_path):
try:
height_field = request.form.get('height_field')
year_of_construction_field = request.form.get('year_of_construction_field')
function_field = request.form.get('function_field')
function_dictionary = self._dictionaries[request.form.get('function_to_hub')]
return GeometryFactory('geojson',
path=file_path,
height_field=height_field,
year_of_construction_field=year_of_construction_field,
function_field=function_field,
function_to_hub=function_dictionary).city
except KeyError:
return None
def _citygml(self, file_path):
try:
year_of_construction_field = request.form.get('year_of_construction_field')
function_field = request.form.get('function_field')
function_dictionary = self._dictionaries[request.form.get('function_dictionary_name')]
hub_crs = request.form.get('hub_crs')
return GeometryFactory('citygml',
path=file_path,
year_of_construction_field=year_of_construction_field,
function_field=function_field,
function_to_hub=function_dictionary,
hub_crs=hub_crs).city
except KeyError:
return None
def post(self):
"""
API call for performing the monthly energy balance workflow
"""
session_id = request.headers.get('session-id', None)
token = request.headers.get('token', None)
application_uuid = request.headers.get('application-uuid', None)
_session = refresh_session(session_id, token, application_uuid)
if _session is None:
return Response(json.dumps({'error': 'unauthorized'}), status=403)
else:
response_token = {'token': _session['token']}
tmp_path = (self._tmp_path / token).resolve()
response_path = (self._response_path / session_id).resolve()
try:
os.mkdir(tmp_path)
except FileExistsError:
pass
try:
os.mkdir(response_path)
except FileExistsError:
pass
geometry_file = request.files['geometry_file']
if not self._allowed_extensions(geometry_file.filename):
shutil.rmtree(tmp_path)
return Response(json.dumps({'error': 'Unsupported media type'}), status=415, headers=response_token)
filename = secure_filename(geometry_file.filename)
file_path = os.path.join(tmp_path, filename)
geometry_file.save(file_path)
if self._file_extension == '.geojson':
self._city = self._geojson(file_path)
else:
self._city = self._citygml(file_path)
if self._city is None:
shutil.rmtree(tmp_path)
return Response(json.dumps({'error': 'Bad request'}), status=400, headers=response_token)
ExportsFactory('glb', self._city, tmp_path).export()
result_files = glob.glob(f'{tmp_path}/*.glb')
result_zip = (response_path / f'{token}.zip').resolve()
with zipfile.ZipFile(result_zip, 'w') as zf:
for result_file in result_files:
zf.write(result_file, Path(result_file).name)
shutil.rmtree(tmp_path)
response = make_response(send_file(result_zip))
response.headers['token'] = token
return response

View File

@ -1,137 +0,0 @@
import json
import os
import shutil
import subprocess
from pathlib import Path
import hub.helpers.constants as cte
from flask import Response, request
from flask_restful import Resource
from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
from hub.exports.exports_factory import ExportsFactory
from hub.imports.construction_factory import ConstructionFactory
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 werkzeug.utils import secure_filename
from hub_api.config import Config
from hub_api.helpers.session_helper import refresh_session
class InselMonthlyEnergyBalance(Resource, Config):
def __init__(self):
super().__init__()
self._extensions = ['.geojson', '.gml']
self._tmp_path = (Path(__file__).parent / 'tmp').resolve()
self._city = None
def _allowed_extensions(self, filename):
self._file_extension = Path(filename).suffix
return self._file_extension in self._extensions
def _geojson(self, file_path):
try:
height_field = request.form.get('height_field')
year_of_construction_field = request.form.get('year_of_construction_field')
function_field = request.form.get('function_field')
function_dictionary = self._dictionaries[request.form.get('function_to_hub')]
return GeometryFactory('geojson',
path=file_path,
height_field=height_field,
year_of_construction_field=year_of_construction_field,
function_field=function_field,
function_to_hub=function_dictionary).city
except KeyError:
return None
def _citygml(self, file_path):
# try:
year_of_construction_field = request.form.get('year_of_construction_field')
if year_of_construction_field == '':
year_of_construction_field = None
function_field = request.form.get('function_field')
if function_field == '':
function_field = None
function_dictionary = self._dictionaries[request.form.get('function_to_hub')]
return GeometryFactory('citygml',
path=file_path,
year_of_construction_field=year_of_construction_field,
function_field=function_field,
function_to_hub=function_dictionary).city
# except KeyError:
# return None
def post(self):
"""
API call for performing the monthly energy balance workflow
"""
session_id = request.headers.get('session-id', None)
token = request.headers.get('token', None)
application_uuid = request.headers.get('application-uuid', None)
_session = refresh_session(session_id, token, application_uuid)
if _session is None:
return Response(json.dumps({'error': 'unauthorized'}), status=403)
else:
response_token = {'token': _session['token']}
tmp_path = (self._tmp_path / token).resolve()
try:
os.mkdir(tmp_path)
except FileExistsError:
pass
geometry_file = request.files['geometry_file']
if not self._allowed_extensions(geometry_file.filename):
shutil.rmtree(tmp_path)
return Response(json.dumps({'error': 'Unsupported media type'}), status=415, headers=response_token)
filename = secure_filename(geometry_file.filename)
file_path = os.path.join(tmp_path, filename)
geometry_file.save(file_path)
if self._file_extension == '.geojson':
self._city = self._geojson(file_path)
else:
self._city = self._citygml(file_path)
if self._city is None:
shutil.rmtree(tmp_path)
return Response(json.dumps({'error': 'Bad request'}), status=400, headers=response_token)
construction_handler = request.form.get('construction_handler')
usage_handler = request.form.get('usage_handler')
WeatherFactory('epw', self._city).enrich()
ConstructionFactory(construction_handler, self._city).enrich()
UsageFactory(usage_handler, self._city).enrich()
ExportsFactory('sra', self._city, tmp_path).export()
sra_file = str((tmp_path / f'{self._city.name}_sra.xml').resolve())
subprocess.run([self.sra, sra_file], stdout=subprocess.DEVNULL)
ResultFactory('sra', self._city, tmp_path).enrich()
EnergyBuildingsExportsFactory('insel_monthly_energy_balance', self._city, tmp_path).export()
for building in self._city.buildings:
insel_path = (tmp_path / f'{building.name}.insel')
subprocess.run([self.insel, str(insel_path)], stdout=subprocess.DEVNULL)
ResultFactory('insel_monthly_energy_balance', self._city, tmp_path).enrich()
results = {}
for building in self._city.buildings:
results[building.name] = {
'total_heating_area': building.floor_area * building.storeys_above_ground,
'year_of_construction': building.year_of_construction,
'function': building.function,
'monthly_heating_demand': building.heating_demand[cte.MONTH],
'yearly_heating_demand': building.heating_demand[cte.YEAR],
'monthly_cooling_demand': building.cooling_demand[cte.MONTH],
'yearly_cooling_demand': building.cooling_demand[cte.YEAR],
'monthly_lighting_peak_load': building.lighting_peak_load[cte.MONTH],
'yearly_lighting_peak_load': building.lighting_peak_load[cte.YEAR],
'monthly_appliances_peak_load': building.appliances_peak_load[cte.MONTH],
'yearly_appliances_peak_load': building.appliances_peak_load[cte.YEAR],
'monthly_cooling_peak_load': building.cooling_peak_load[cte.MONTH],
'yearly_cooling_peak_load': building.cooling_peak_load[cte.YEAR],
'monthly_heating_peak_load': building.heating_peak_load[cte.MONTH],
'yearly_heating_peak_load': building.heating_peak_load[cte.YEAR],
'monthly_lighting_electrical_demand': building.lighting_electrical_demand[cte.MONTH],
'yearly_lighting_electrical_demand': building.lighting_electrical_demand[cte.YEAR],
'monthly_appliances_electrical_demand': building.appliances_electrical_demand[cte.MONTH],
'yearly_appliances_electrical_demand': building.appliances_electrical_demand[cte.YEAR],
'monthly_domestic_hot_water_heat_demand': building.domestic_hot_water_heat_demand[cte.MONTH],
'yearly_domestic_hot_water_heat_demand': building.domestic_hot_water_heat_demand[cte.YEAR],
}
shutil.rmtree(tmp_path)
return Response(json.dumps({'result': 'succeed', 'results': results}), status=200, headers=response_token)

View File

@ -1,2 +0,0 @@
.gitignore
!.gitignore

View File

@ -27,9 +27,3 @@ flasgger
cerc-hub cerc-hub
python-dotenv python-dotenv
mapbox_earcut mapbox_earcut
cerc-costs
cerc-co2-emission
werkzeug
sqlalchemy
pathlib
pymongo