initialised project from existing gamification service
This commit is contained in:
commit
385d89ba9d
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
.DS_Store
|
||||
.idea
|
||||
venv
|
||||
hub_api/__pycache__
|
||||
utils/__pycache__
|
1
cache/.gitignore
vendored
Normal file
1
cache/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
!.gitignore
|
8768
data/CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw
Normal file
8768
data/CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw
Normal file
File diff suppressed because it is too large
Load Diff
2992
data/DomparkBuilding.gltf
Normal file
2992
data/DomparkBuilding.gltf
Normal file
File diff suppressed because one or more lines are too long
8764
data/Montreal.cli
Normal file
8764
data/Montreal.cli
Normal file
File diff suppressed because it is too large
Load Diff
8764
data/None.cli
Normal file
8764
data/None.cli
Normal file
File diff suppressed because it is too large
Load Diff
BIN
data/dompark.3dm
Normal file
BIN
data/dompark.3dm
Normal file
Binary file not shown.
15600
data/dompark.idf
Normal file
15600
data/dompark.idf
Normal file
File diff suppressed because it is too large
Load Diff
14
data/dompark_air_source_hp.csv
Normal file
14
data/dompark_air_source_hp.csv
Normal file
@ -0,0 +1,14 @@
|
||||
,Monthly HP Electricity Demand (kWh),Monthly Fuel Consumption of Auxiliary Heater (m3)
|
||||
Jan,1889256.0,7.91282225
|
||||
Feb,1682865.12,6.06568241
|
||||
Mar,1387834.62,0.068873927
|
||||
Apr,590983.75,-0.0
|
||||
May,0.0,11.7348537
|
||||
Jun,0.0,1.51432967
|
||||
Jul,0.0,0.327990711
|
||||
Aug,0.0,0.534922004
|
||||
Sept,0.0,5.72120714
|
||||
Oct,372460.781,0.000307567185
|
||||
Nov,829471.875,-0.0
|
||||
Dec,1673153.38,2.49482107
|
||||
Total,8426025.526,36.375810449184996
|
|
103532
data/energy+.idd
Normal file
103532
data/energy+.idd
Normal file
File diff suppressed because it is too large
Load Diff
143
gamification.py
Normal file
143
gamification.py
Normal file
@ -0,0 +1,143 @@
|
||||
"""
|
||||
Main
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2021 Project Author name guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
|
||||
import datetime
|
||||
from pathlib import Path
|
||||
|
||||
import flask
|
||||
from apispec import APISpec
|
||||
from apispec.ext.marshmallow import MarshmallowPlugin
|
||||
from flask import Response
|
||||
from flask_apispec.extension import FlaskApiSpec
|
||||
from flask_restful import Api
|
||||
|
||||
from catalog_factories.construction_catalog_factory import ConstructionCatalogFactory
|
||||
from catalog_factories.greenery_catalog_factory import GreeneryCatalogFactory
|
||||
from catalog_factories.usage_catalog_factory import UsageCatalogFactory
|
||||
from imports.construction_factory import ConstructionFactory
|
||||
from imports.geometry_factory import GeometryFactory
|
||||
from imports.life_cycle_assessment_factory import LifeCycleAssessment
|
||||
from imports.schedules_factory import SchedulesFactory
|
||||
from imports.usage_factory import UsageFactory
|
||||
from imports.weather_factory import WeatherFactory
|
||||
from hub_api.city_info import CityInfo
|
||||
from hub_api.geometry import Geometry
|
||||
from hub_api.greenery_catalog import GreeneryCatalogEntries
|
||||
from hub_api.greenery_catalog import GreeneryCatalogEntry
|
||||
from hub_api.greenery_catalog import GreeneryCatalogNames
|
||||
from hub_api.construction_catalog import ConstructionCatalogEntries
|
||||
from hub_api.construction_catalog import ConstructionCatalogEntry
|
||||
from hub_api.construction_catalog import ConstructionCatalogNames
|
||||
from hub_api.usage_catalog import UsageCatalogEntries
|
||||
from hub_api.usage_catalog import UsageCatalogEntry
|
||||
from hub_api.usage_catalog import UsageCatalogNames
|
||||
from hub_api.heat_pump import HeatPump
|
||||
from hub_api.lca import MaterialLCACatalog
|
||||
from hub_api.lca import MaterialLCACalculations
|
||||
from hub_api.construction import Construction
|
||||
from hub_api.usage import Usage
|
||||
from hub_api.energy_demand import EnergyDemand
|
||||
from hub_api.session import SessionStart, SessionEnd, KeepSessionAlive
|
||||
from hub_api.uptime import Uptime
|
||||
from hub_api.greenery import Greenery
|
||||
import hub_api.helpers.session_helper as sh
|
||||
|
||||
app = flask.Flask('gamification')
|
||||
api = Api(app)
|
||||
|
||||
|
||||
api.add_resource(Uptime, '/v1.3/uptime')
|
||||
api.add_resource(Geometry, '/v1.3/geometry')
|
||||
api.add_resource(GreeneryCatalogEntry, '/v1.3/greenery-catalog/entry')
|
||||
api.add_resource(GreeneryCatalogEntries, '/v1.3/greenery-catalog/entries')
|
||||
api.add_resource(GreeneryCatalogNames, '/v1.3/greenery-catalog/names')
|
||||
api.add_resource(ConstructionCatalogEntry, '/v1.3/construction-catalog/entry')
|
||||
api.add_resource(ConstructionCatalogEntries, '/v1.3/construction-catalog/entries')
|
||||
api.add_resource(ConstructionCatalogNames, '/v1.3/construction-catalog/names')
|
||||
api.add_resource(Construction, '/v1.3/construction')
|
||||
api.add_resource(UsageCatalogEntry, '/v1.3/usage-catalog/entry')
|
||||
api.add_resource(UsageCatalogEntries, '/v1.3/usage-catalog/entries')
|
||||
api.add_resource(UsageCatalogNames, '/v1.3/usage-catalog/names')
|
||||
api.add_resource(Usage, '/v1.3/usage')
|
||||
api.add_resource(EnergyDemand, '/v1.3/energy-demand')
|
||||
# api.add_resource(LCA, '/v1.3/lca')
|
||||
api.add_resource(MaterialLCACatalog, '/v1.3/material_lca_catalog/entries')
|
||||
api.add_resource(MaterialLCACalculations, '/v1.3/material_lca_catalog/calculations')
|
||||
api.add_resource(HeatPump, '/v1.3/heat-pump')
|
||||
api.add_resource(SessionStart, '/v1.3/session/start')
|
||||
api.add_resource(SessionEnd, '/v1.3/session/end')
|
||||
api.add_resource(KeepSessionAlive, '/v1.3/session/keep_alive')
|
||||
api.add_resource(CityInfo, '/v1.3/city_info')
|
||||
api.add_resource(Greenery, '/v1.3/greenery')
|
||||
|
||||
# Add api documentation
|
||||
app.config.update({
|
||||
'APISPEC_SPEC': APISpec(
|
||||
title='Gamification Service',
|
||||
version='v1.3',
|
||||
plugins=[MarshmallowPlugin()],
|
||||
openapi_version='2.0.0'
|
||||
),
|
||||
'APISPEC_SWAGGER_URL': '/swagger/', # URI to access API Doc JSON
|
||||
'APISPEC_SWAGGER_UI_URL': '/swagger-ui/' # URI to access UI of API Doc
|
||||
})
|
||||
docs = FlaskApiSpec(app)
|
||||
docs.register(HeatPump)
|
||||
|
||||
sh.begin_time = datetime.datetime.now()
|
||||
# initialize catalogs
|
||||
sh.greenery_catalog = GreeneryCatalogFactory('nrel').catalog
|
||||
sh.construction_catalog = ConstructionCatalogFactory('nrel').catalog
|
||||
sh.usage_catalog = UsageCatalogFactory('comnet').catalog
|
||||
|
||||
|
||||
# Enrich the city
|
||||
data_path = (Path(__file__).parent / 'data').resolve()
|
||||
rihno_path = (Path(data_path / 'dompark.3dm')).resolve()
|
||||
city = GeometryFactory('rhino', rihno_path).city
|
||||
|
||||
|
||||
for building in city.buildings:
|
||||
# Rihno files have no information about the function or the year of construction
|
||||
building.year_of_construction = 1995
|
||||
building.function = 'industry'
|
||||
building.human_readable_name = "Dompark"
|
||||
|
||||
ConstructionFactory('nrel', city).enrich()
|
||||
UsageFactory('comnet', city).enrich()
|
||||
SchedulesFactory('comnet', city).enrich()
|
||||
LifeCycleAssessment('material', city).enrich()
|
||||
LifeCycleAssessment('machine', city).enrich()
|
||||
LifeCycleAssessment('fuel', city).enrich()
|
||||
LifeCycleAssessment('vehicle', city).enrich()
|
||||
|
||||
montreal_weather_file = (Path(__file__).parent / './data/CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').resolve()
|
||||
city.climate_file = (data_path / f'{city.climate_reference_city}.cli').resolve()
|
||||
WeatherFactory('epw', city, file_name=montreal_weather_file).enrich()
|
||||
|
||||
# Rihno files have no information about the building location
|
||||
city.name = 'Montreal'
|
||||
city.climate_reference_city = 'Montreal'
|
||||
|
||||
# SRA Calculations
|
||||
|
||||
for building in city.buildings:
|
||||
building.heated = True
|
||||
building.cooled = True
|
||||
building.attic_heated = 0
|
||||
building.basement_heated = 0
|
||||
for surface in building.surfaces:
|
||||
surface.swr = 0.2
|
||||
|
||||
# Pass the city to the session helper to be used as default status.
|
||||
sh.city = city
|
||||
|
||||
@app.route("/")
|
||||
def home():
|
||||
return Response(headers={'Access-Control-Allow-Origin': '*'})
|
||||
|
||||
|
||||
app.run(port=15789, host="0.0.0.0", debug=False)
|
72
hub_api/city_info.py
Normal file
72
hub_api/city_info.py
Normal file
@ -0,0 +1,72 @@
|
||||
"""
|
||||
City info
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Project Author name guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
import json
|
||||
|
||||
from flask import Response, request
|
||||
from flask_restful import Resource
|
||||
|
||||
from hub_api.helpers.session_helper import refresh_session
|
||||
|
||||
|
||||
class CityInfo(Resource):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get():
|
||||
session = refresh_session(request)
|
||||
if session is None:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
headers = session.headers
|
||||
|
||||
city = session.city
|
||||
# TODO: this is only for dompark project and need to be removed in future versions.
|
||||
floor_area = 0
|
||||
wall_construction = 'unknown'
|
||||
floor_construction = 'unknown'
|
||||
roof_construction = 'unknown'
|
||||
window_type = 'unknown'
|
||||
building_dic = {}
|
||||
for building in city.buildings:
|
||||
|
||||
usages = [] # This is only valid for dompark project as all the building have the same usage
|
||||
if building.lower_corner[2] == 0:
|
||||
floor_area += building.floor_area
|
||||
for internal_zone in building.internal_zones:
|
||||
for usage_zone in internal_zone.usage_zones:
|
||||
usages.append({'percentage': usage_zone.percentage, 'usage': usage_zone.usage})
|
||||
for thermal_zone in internal_zone.thermal_zones:
|
||||
for thermal_boundary in thermal_zone.thermal_boundaries:
|
||||
if thermal_boundary.parent_surface.type == 'Ground':
|
||||
floor_construction = thermal_boundary.construction_name
|
||||
elif thermal_boundary.parent_surface.type == 'Wall':
|
||||
wall_construction = thermal_boundary.construction_name
|
||||
for thermal_opening in thermal_boundary.thermal_openings:
|
||||
if thermal_opening.construction_name is not None:
|
||||
window_type = thermal_opening.construction_name
|
||||
break
|
||||
else:
|
||||
roof_construction = thermal_boundary.construction_name
|
||||
name = building.human_readable_name
|
||||
year_of_construction = str(building.year_of_construction)
|
||||
building_dic = {
|
||||
'name': str(name),
|
||||
'floor_area': str(floor_area),
|
||||
'year_of_construction': str(year_of_construction),
|
||||
'usages': usages,
|
||||
'wall_construction': wall_construction,
|
||||
'floor_construction': floor_construction,
|
||||
'roof_construction': roof_construction,
|
||||
'window_type': window_type,
|
||||
'default_archetype': 'industry ASHRAE_2004:4A non_standard_dompark'
|
||||
}
|
||||
|
||||
buildings = [building_dic]
|
||||
response = {'city_name': 'Montreal',
|
||||
'climate_reference_city': str(city.climate_reference_city),
|
||||
'buildings': buildings
|
||||
}
|
||||
return Response(json.dumps(response), headers=headers)
|
78
hub_api/construction.py
Normal file
78
hub_api/construction.py
Normal file
@ -0,0 +1,78 @@
|
||||
"""
|
||||
Construction
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Project Author name guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
import json
|
||||
import uuid
|
||||
|
||||
from flask import Response, request
|
||||
from flask_restful import Resource
|
||||
|
||||
from city_model_structure.building_demand.layer import Layer
|
||||
from city_model_structure.building_demand.material import Material
|
||||
from hub_api.helpers.session_helper import refresh_session
|
||||
|
||||
|
||||
class Construction(Resource):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def put():
|
||||
session = refresh_session(request)
|
||||
if session is None:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
headers = session.headers
|
||||
city = session.city
|
||||
try:
|
||||
building_names = request.json['building_names']
|
||||
constructions = request.json['constructions']
|
||||
for construction in constructions:
|
||||
construction_type = construction['type']
|
||||
layers = construction['layers']
|
||||
for building_name in building_names:
|
||||
for building in city.buildings:
|
||||
if building.human_readable_name != building_name:
|
||||
continue
|
||||
for internal_zone in building.internal_zones:
|
||||
for thermal_zone in internal_zone.thermal_zones:
|
||||
thermal_zone.additional_thermal_bridge_u_value = request.json['extra_loses_due_to_thermal_bridges']
|
||||
thermal_zone.indirect_heated_area_ratio = request.json['indirect_heated_ratio']
|
||||
thermal_zone.infiltration_rate_system_on = request.json['infiltration_rate_for_ventilation_system_on']
|
||||
thermal_zone.infiltration_rate_system_off = request.json['infiltration_rate_for_ventilation_system_off']
|
||||
for thermal_boundary in thermal_zone.thermal_boundaries:
|
||||
if thermal_boundary.parent_surface.type == construction_type:
|
||||
_layers = []
|
||||
for layer in layers:
|
||||
_layer = Layer()
|
||||
thermal_resistance = layer['material']['thermal_resistance']
|
||||
conductivity = layer['material']['conductivity']
|
||||
density = layer['material']['density']
|
||||
specific_heat = layer['material']['specific_heat']
|
||||
_layer.thickness = layer['thickness']
|
||||
_material = Material()
|
||||
_material.id = layer['material']['id']
|
||||
_material.name = layer['material']['name']
|
||||
_material.solar_absorptance = layer['material']['solar_absorptance']
|
||||
_material.thermal_absorptance = layer['material']['thermal_absorptance']
|
||||
_material.visible_absorptance = layer['material']['visible_absorptance']
|
||||
_material.no_mass = layer['material']['no_mass']
|
||||
_material.thermal_resistance = (thermal_resistance if thermal_resistance != '' else None)
|
||||
_material.conductivity = (conductivity if conductivity != '' else None)
|
||||
_material.density = (density if density != '' else None)
|
||||
_material.specific_heat = (specific_heat if specific_heat != '' else None)
|
||||
_layer.material = _material
|
||||
_layers.append(_layer)
|
||||
thermal_boundary.layers = _layers
|
||||
if 'window' in construction.keys():
|
||||
for thermal_opening in thermal_boundary.thermal_openings:
|
||||
thermal_opening.frame_ratio = construction['window']['frame_ratio']
|
||||
thermal_opening.g_value = construction['window']['g_value']
|
||||
thermal_opening.overall_u_value = construction['window']['overall_u_value']
|
||||
response = {'result': 'succeed'}
|
||||
except KeyError as ex:
|
||||
response = {'error': f'Mandatory parameter {ex} is missing'}
|
||||
return Response(json.dumps(response), headers=headers, status=400)
|
||||
|
||||
return Response(json.dumps(response), headers=headers)
|
194
hub_api/construction_catalog.py
Normal file
194
hub_api/construction_catalog.py
Normal file
@ -0,0 +1,194 @@
|
||||
"""
|
||||
Construction catalog
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Project Author name guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from flask import request, Response
|
||||
from flask_restful import Resource
|
||||
|
||||
from catalog_factories.data_models.construction.archetype import Archetype
|
||||
from catalog_factories.data_models.construction.construction import Construction
|
||||
from catalog_factories.data_models.construction.material import Material
|
||||
from catalog_factories.data_models.construction.window import Window
|
||||
from hub_api.helpers.session_helper import refresh_session
|
||||
|
||||
|
||||
class ToJson:
|
||||
@staticmethod
|
||||
def archetype_to_json(archetype):
|
||||
constructions = []
|
||||
for construction in archetype.constructions:
|
||||
constructions.append(ToJson.construction_to_json(construction))
|
||||
dictionary = {
|
||||
'name': archetype.name,
|
||||
'function': archetype.function,
|
||||
'construction_period': archetype.construction_period,
|
||||
'average_storey_height': archetype.average_storey_height,
|
||||
'thermal_capacity': archetype.thermal_capacity,
|
||||
'extra_loses_due_to_thermal_bridges': archetype.extra_loses_due_to_thermal_bridges,
|
||||
'indirect_heated_ratio': archetype.indirect_heated_ratio,
|
||||
'infiltration_rate_for_ventilation_system_off': archetype.infiltration_rate_for_ventilation_system_off,
|
||||
'infiltration_rate_for_ventilation_system_on': archetype.infiltration_rate_for_ventilation_system_on,
|
||||
'constructions': constructions
|
||||
}
|
||||
return dictionary
|
||||
|
||||
@staticmethod
|
||||
def construction_to_json(construction):
|
||||
layers = []
|
||||
for layer in construction.layers:
|
||||
layers.append(ToJson.layer_to_json(layer))
|
||||
dictionary = {'name': construction.name,
|
||||
'type': construction.type,
|
||||
'layers': layers
|
||||
}
|
||||
if construction.window is not None:
|
||||
dictionary['window_ratio'] = construction.window_ratio
|
||||
dictionary['window'] = ToJson.window_to_json(construction.window)
|
||||
return dictionary
|
||||
|
||||
@staticmethod
|
||||
def window_to_json(window):
|
||||
if window is None:
|
||||
return {}
|
||||
dictionary = {
|
||||
'name': window.name,
|
||||
'frame_ratio': window.frame_ratio,
|
||||
'g_value': window.g_value,
|
||||
'overall_u_value': str(window.overall_u_value)
|
||||
}
|
||||
return dictionary
|
||||
|
||||
@staticmethod
|
||||
def layer_to_json(layer):
|
||||
dictionary = {'name': layer.name,
|
||||
'thickness': layer.thickness,
|
||||
'material': ToJson.material_to_json(layer.material),
|
||||
}
|
||||
return dictionary
|
||||
|
||||
@staticmethod
|
||||
def material_to_json(material):
|
||||
dictionary = {'id': material.id,
|
||||
'name': material.name,
|
||||
'solar_absorptance': material.solar_absorptance,
|
||||
'thermal_absorptance': material.thermal_absorptance,
|
||||
'visible_absorptance': material.visible_absorptance,
|
||||
'no_mass': str(material.no_mass),
|
||||
'thermal_resistance': '',
|
||||
'conductivity': '',
|
||||
'density': '',
|
||||
'specific_heat': ''
|
||||
}
|
||||
if material.no_mass:
|
||||
dictionary['thermal_resistance'] = material.thermal_resistance
|
||||
else:
|
||||
dictionary['conductivity'] = material.conductivity
|
||||
dictionary['density'] = material.density
|
||||
dictionary['specific_heat'] = material.specific_heat
|
||||
return dictionary
|
||||
|
||||
|
||||
class ConstructionCatalogEntry(Resource):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def post():
|
||||
session = refresh_session(request)
|
||||
if session is None:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
headers = session.headers
|
||||
catalog = session.construction_catalog
|
||||
name = None
|
||||
if request.data == b'' or request.json['name'] is None:
|
||||
response = {'error': 'Mandatory parameter "name" is missing'}
|
||||
return Response(json.dumps(response), headers=headers, status=400)
|
||||
try:
|
||||
name = request.json['name']
|
||||
entry = catalog.get_entry(name)
|
||||
output = {}
|
||||
if isinstance(entry, Archetype):
|
||||
output['archetypes'] = ToJson.archetype_to_json(entry)
|
||||
if isinstance(entry, Construction):
|
||||
output['constructions'] = ToJson.construction_to_json(entry)
|
||||
if isinstance(entry, Material):
|
||||
output['materials'] = ToJson.material_to_json(entry)
|
||||
if isinstance(entry, Window):
|
||||
output['windows'] = ToJson.window_to_json(entry)
|
||||
return Response(json.dumps(output), headers=headers)
|
||||
except IndexError:
|
||||
response = {'error': f'Name "{name}" unknown'}
|
||||
return Response(json.dumps(response), headers=headers, status=400)
|
||||
|
||||
|
||||
class ConstructionCatalogEntries(Resource):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def post():
|
||||
session = refresh_session(request)
|
||||
if session is None:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
headers = session.headers
|
||||
catalog = session.construction_catalog
|
||||
category = None
|
||||
if request.data != b'':
|
||||
category = request.json['category']
|
||||
output = {}
|
||||
if category is None:
|
||||
output = {'archetypes': [], 'constructions':[], 'materials':[], 'windows':[]}
|
||||
content = catalog.entries()
|
||||
for archetype in content.archetypes:
|
||||
output['archetypes'].append(ToJson.archetype_to_json(archetype))
|
||||
for construction in content.constructions:
|
||||
output['constructions'].append(ToJson.construction_to_json(construction))
|
||||
for material in content.materials:
|
||||
output['materials'].append(ToJson.material_to_json(material))
|
||||
for window in content.windows:
|
||||
output['windows'].append(ToJson.window_to_json(window))
|
||||
else:
|
||||
try:
|
||||
output[category] = []
|
||||
content = catalog.entries(category)
|
||||
for entry in content:
|
||||
if isinstance(entry, Archetype):
|
||||
output[category].append(ToJson.archetype_to_json(entry))
|
||||
if isinstance(entry, Construction):
|
||||
output[category].append(ToJson.construction_to_json(entry))
|
||||
if isinstance(entry, Material):
|
||||
output[category].append(ToJson.material_to_json(entry))
|
||||
if isinstance(entry, Window):
|
||||
output[category].append(ToJson.window_to_json(entry))
|
||||
except ValueError:
|
||||
output = {'error': f'Category "{category}" unknown'}
|
||||
return Response(json.dumps(output), headers=headers, status=400)
|
||||
return Response(json.dumps(output), headers=headers)
|
||||
|
||||
|
||||
class ConstructionCatalogNames(Resource):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def post():
|
||||
session = refresh_session(request)
|
||||
if session is None:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
headers = session.headers
|
||||
catalog = session.construction_catalog
|
||||
category = None
|
||||
if request.data != b'':
|
||||
category = request.json['category']
|
||||
if category is None:
|
||||
return Response(json.dumps(catalog.names()), headers=headers)
|
||||
else:
|
||||
try:
|
||||
return Response(json.dumps(catalog.names(category)), headers=headers)
|
||||
except ValueError:
|
||||
response = {'error': f'Category "{category}" unknown'}
|
||||
return Response(json.dumps(response), headers=headers, status=400)
|
394
hub_api/energy_demand.py
Normal file
394
hub_api/energy_demand.py
Normal file
@ -0,0 +1,394 @@
|
||||
import json
|
||||
from flask import request, Response
|
||||
from flask_restful import Resource
|
||||
from pathlib import Path
|
||||
from geomeppy import IDF
|
||||
import os
|
||||
import glob
|
||||
from hub_api.helpers.session_helper import refresh_session
|
||||
import hub_api.helpers.session_helper as sh
|
||||
import helpers.constants as cte
|
||||
import csv
|
||||
|
||||
|
||||
class EnergyDemand(Resource):
|
||||
_THERMOSTAT = 'HVACTEMPLATE:THERMOSTAT'
|
||||
_IDEAL_LOAD_AIR_SYSTEM = 'HVACTEMPLATE:ZONE:IDEALLOADSAIRSYSTEM'
|
||||
_SURFACE = 'BUILDINGSURFACE:DETAILED'
|
||||
_WINDOW_SURFACE = 'FENESTRATIONSURFACE:DETAILED'
|
||||
_CONSTRUCTION = 'CONSTRUCTION'
|
||||
_MATERIAL = 'MATERIAL'
|
||||
_MATERIAL_NOMASS = 'MATERIAL:NOMASS'
|
||||
_ROUGHNESS = 'MediumRough'
|
||||
_HOURLY_SCHEDULE = 'SCHEDULE:DAY:HOURLY'
|
||||
_COMPACT_SCHEDULE = 'SCHEDULE:COMPACT'
|
||||
_FILE_SCHEDULE = 'SCHEDULE:FILE'
|
||||
_ZONE = 'ZONE'
|
||||
_LIGHTS = 'LIGHTS'
|
||||
_PEOPLE = 'PEOPLE'
|
||||
_APPLIANCES = 'OTHEREQUIPMENT'
|
||||
_HEATING_COOLING = 'THERMOSTATSETPOINT:DUALSETPOINT'
|
||||
_INFILTRATION = 'ZONEINFILTRATION:DESIGNFLOWRATE'
|
||||
_BUILDING_SURFACE = 'BuildingSurfaceDetailed'
|
||||
_SCHEDULE_LIMIT = 'SCHEDULETYPELIMITS'
|
||||
_ON_OFF = 'On/Off'
|
||||
_FRACTION = 'Fraction'
|
||||
_ANY_NUMBER = 'Any Number'
|
||||
_CONTINUOUS = 'Continuous'
|
||||
_DISCRETE = 'Discrete'
|
||||
_BUILDING = 'BUILDING'
|
||||
_SIZING_PERIODS = 'SIZINGPERIOD:DESIGNDAY'
|
||||
_LOCATION = 'SITE:LOCATION'
|
||||
_WINDOW_MATERIAL_SIMPLE = 'WINDOWMATERIAL:SIMPLEGLAZINGSYSTEM'
|
||||
_WINDOW = 'WINDOW'
|
||||
_MATERIAL_ROOFVEGETATION = 'MATERIAL:ROOFVEGETATION'
|
||||
_SIMPLE = 'Simple'
|
||||
|
||||
idf_surfaces = {
|
||||
# todo: make an enum for all the surface types
|
||||
cte.WALL: 'wall',
|
||||
cte.GROUND: 'floor',
|
||||
cte.ROOF: 'roof'
|
||||
}
|
||||
idf_usage = {
|
||||
# todo: make an enum for all the usage types
|
||||
cte.RESIDENTIAL: 'residential_building'
|
||||
}
|
||||
idf_type_limits = {
|
||||
cte.ON_OFF: 'on/off',
|
||||
cte.FRACTION: 'Fraction',
|
||||
cte.ANY_NUMBER: 'Any Number',
|
||||
cte.CONTINUOUS: 'Continuous',
|
||||
cte.DISCRETE: 'Discrete',
|
||||
cte.TEMPERATURE: 'Any Number'
|
||||
}
|
||||
idf_day_types = {
|
||||
cte.MONDAY: 'Monday',
|
||||
cte.TUESDAY: 'Tuesday',
|
||||
cte.WEDNESDAY: 'Wednesday',
|
||||
cte.THURSDAY: 'Thursday',
|
||||
cte.FRIDAY: 'Friday',
|
||||
cte.SATURDAY: 'Saturday',
|
||||
cte.SUNDAY: 'Sunday',
|
||||
cte.HOLIDAY: 'Holidays',
|
||||
cte.WINTER_DESIGN_DAY: 'WinterDesignDay',
|
||||
cte.SUMMER_DESIGN_DAY: 'SummerDesignDay'
|
||||
}
|
||||
idf_schedule_types = {
|
||||
'compact': 'Compact',
|
||||
cte.DAY: 'Day',
|
||||
cte.WEEK: 'Week',
|
||||
cte.YEAR: 'Year',
|
||||
'file': 'File'
|
||||
}
|
||||
idf_schedule_data_type = {
|
||||
'compact': 'Compact',
|
||||
'hourly': 'Hourly',
|
||||
'daily': 'Daily',
|
||||
'interval': 'Interval',
|
||||
'list': 'List',
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
# this class is mostly hardcoded, as is intended to be used only for Dompark project,
|
||||
# other projects should use the normal idf workflow instead.
|
||||
self._output_path = Path(Path(__file__).parent.parent / 'tmp').resolve()
|
||||
self._data_path = Path(Path(__file__).parent.parent / 'data').resolve()
|
||||
self._city = None
|
||||
self._greenery_percentage = 0
|
||||
|
||||
def _set_layers(self, _idf, name, layers, vegetation=None):
|
||||
if vegetation is not None:
|
||||
_kwargs = {'Name': name, 'Outside_Layer': vegetation.name}
|
||||
for i in range(0, len(layers)):
|
||||
_kwargs[f'Layer_{i + 2}'] = layers[i].material.name
|
||||
else:
|
||||
_kwargs = {'Name': name, 'Outside_Layer': layers[0].material.name}
|
||||
for i in range(1, len(layers)):
|
||||
_kwargs[f'Layer_{i + 1}'] = layers[i].material.name
|
||||
_idf.newidfobject(self._CONSTRUCTION, **_kwargs)
|
||||
|
||||
def _update_constructions(self, _idf, ground, roof, wall, vegetation):
|
||||
for construction in _idf.idfobjects[self._CONSTRUCTION]:
|
||||
if construction.Name == 'Project ground floor':
|
||||
# floor
|
||||
self._set_layers(_idf, 'user_floor', ground)
|
||||
elif construction.Name == 'Dompark Roof':
|
||||
# roof
|
||||
self._set_layers(_idf, 'user_roof', roof)
|
||||
elif construction.Name == 'Dompark Roof Vegetation':
|
||||
# roof
|
||||
self._set_layers(_idf, 'user_roof_vegetation', roof, vegetation)
|
||||
elif construction.Name == 'Dompark Wall':
|
||||
# wall
|
||||
self._set_layers(_idf, 'user_wall', wall)
|
||||
else:
|
||||
continue
|
||||
for surface in _idf.idfobjects[self._SURFACE]:
|
||||
if surface.Construction_Name == 'Project ground floor':
|
||||
# floor
|
||||
surface.Construction_Name = 'user_floor'
|
||||
elif surface.Construction_Name == 'Dompark Wall':
|
||||
# wall
|
||||
surface.Construction_Name = 'user_wall'
|
||||
elif surface.Construction_Name == 'Dompark Roof' or surface.Construction_Name == 'Dompark Roof Vegetation':
|
||||
# roof
|
||||
surface.Construction_Name = 'user_roof'
|
||||
if self._greenery_percentage > 0:
|
||||
if surface.Name in sh.roofs_associated_to_percentage[str(self._greenery_percentage)]:
|
||||
surface.Construction_Name = 'user_roof_vegetation'
|
||||
else:
|
||||
continue
|
||||
|
||||
for window in _idf.idfobjects[self._WINDOW_SURFACE]:
|
||||
window.Construction_Name = 'window_construction_1'
|
||||
|
||||
def _add_material(self, _idf, layers):
|
||||
for layer in layers:
|
||||
for material in _idf.idfobjects[self._MATERIAL]:
|
||||
if material.Name == layer.material.name:
|
||||
return
|
||||
for material in _idf.idfobjects[self._MATERIAL_NOMASS]:
|
||||
if material.Name == layer.material.name:
|
||||
return
|
||||
if str(layer.material.no_mass) == 'True':
|
||||
_idf.newidfobject(self._MATERIAL_NOMASS,
|
||||
Name=layer.material.name,
|
||||
Roughness=self._ROUGHNESS,
|
||||
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:
|
||||
_idf.newidfobject(self._MATERIAL,
|
||||
Name=layer.material.name,
|
||||
Roughness=self._ROUGHNESS,
|
||||
Thickness=layer.thickness,
|
||||
Conductivity=layer.material.conductivity,
|
||||
Density=layer.material.density,
|
||||
Specific_Heat=layer.material.specific_heat,
|
||||
Thermal_Absorptance=layer.material.thermal_absorptance,
|
||||
Solar_Absorptance=layer.material.solar_absorptance,
|
||||
Visible_Absorptance=layer.material.visible_absorptance
|
||||
)
|
||||
|
||||
def _add_vegetation_material(self, _idf, vegetation):
|
||||
for vegetation_material in _idf.idfobjects[self._MATERIAL_ROOFVEGETATION]:
|
||||
if vegetation_material.Name == vegetation.name:
|
||||
return
|
||||
soil = vegetation.soil
|
||||
height = 0
|
||||
leaf_area_index = 0
|
||||
leaf_reflectivity = 0
|
||||
leaf_emissivity = 0
|
||||
minimal_stomatal_resistance = 0
|
||||
for plant in vegetation.plants:
|
||||
percentage = float(plant.percentage) / 100
|
||||
height += percentage * float(plant.height)
|
||||
leaf_area_index += percentage * float(plant.leaf_area_index)
|
||||
leaf_reflectivity += percentage * float(plant.leaf_reflectivity)
|
||||
leaf_emissivity += percentage * float(plant.leaf_emissivity)
|
||||
minimal_stomatal_resistance += percentage * float(plant.minimal_stomatal_resistance)
|
||||
_idf.newidfobject(self._MATERIAL_ROOFVEGETATION,
|
||||
Name=vegetation.name,
|
||||
Height_of_Plants=height,
|
||||
Leaf_Area_Index=leaf_area_index,
|
||||
Leaf_Reflectivity=leaf_reflectivity,
|
||||
Leaf_Emissivity=leaf_emissivity,
|
||||
Minimum_Stomatal_Resistance=minimal_stomatal_resistance,
|
||||
Soil_Layer_Name=soil.name,
|
||||
Roughness=soil.roughness,
|
||||
Thickness=vegetation.soil_thickness,
|
||||
Conductivity_of_Dry_Soil=soil.dry_conductivity,
|
||||
Density_of_Dry_Soil=soil.dry_density,
|
||||
Specific_Heat_of_Dry_Soil=soil.dry_specific_heat,
|
||||
Thermal_Absorptance=soil.thermal_absorptance,
|
||||
Solar_Absorptance=soil.solar_absorptance,
|
||||
Visible_Absorptance=soil.visible_absorptance,
|
||||
Saturation_Volumetric_Moisture_Content_of_the_Soil_Layer=
|
||||
soil.saturation_volumetric_moisture_content,
|
||||
Residual_Volumetric_Moisture_Content_of_the_Soil_Layer=
|
||||
soil.residual_volumetric_moisture_content,
|
||||
Initial_Volumetric_Moisture_Content_of_the_Soil_Layer=
|
||||
soil.initial_volumetric_moisture_content,
|
||||
Moisture_Diffusion_Calculation_Method=self._SIMPLE
|
||||
)
|
||||
|
||||
def _add_window_construction_and_material(self, _idf, window):
|
||||
name = 'glazing_1'
|
||||
_kwargs = {'Name': name, 'UFactor': window.overall_u_value,
|
||||
'Solar_Heat_Gain_Coefficient': window.g_value}
|
||||
_idf.newidfobject(self._WINDOW_MATERIAL_SIMPLE, **_kwargs)
|
||||
window_construction_name = 'window_construction_1'
|
||||
_kwargs = {'Name': window_construction_name, 'Outside_Layer': name}
|
||||
return _idf.newidfobject(self._CONSTRUCTION, **_kwargs)
|
||||
|
||||
def _add_materials(self, _idf):
|
||||
building = self._city.buildings[0]
|
||||
ground_surface = building.grounds[0]
|
||||
roof_surface = building.roofs[0]
|
||||
wall_surface = building.walls[0]
|
||||
internal_zone = building.internal_zones[0]
|
||||
thermal_zone = internal_zone.thermal_zones[0]
|
||||
ground = None
|
||||
roof = None
|
||||
roof_vegetation = None
|
||||
wall = None
|
||||
window = None
|
||||
for thermal_boundary in thermal_zone.thermal_boundaries:
|
||||
if thermal_boundary.parent_surface.id == wall_surface.id:
|
||||
wall = thermal_boundary.layers
|
||||
if thermal_boundary.parent_surface.id == roof_surface.id:
|
||||
roof = thermal_boundary.layers
|
||||
roof_vegetation = thermal_boundary.vegetation
|
||||
if thermal_boundary.parent_surface.id == ground_surface.id:
|
||||
ground = thermal_boundary.layers
|
||||
if thermal_boundary.thermal_openings is not None and len(thermal_boundary.thermal_openings) > 0:
|
||||
window = thermal_boundary.thermal_openings[0]
|
||||
if ground is not None and roof is not None and wall is not None and window is not None:
|
||||
# we have all the needed surfaces type
|
||||
break
|
||||
self._add_material(_idf, ground)
|
||||
self._add_material(_idf, roof)
|
||||
self._add_material(_idf, wall)
|
||||
if roof_vegetation is not None:
|
||||
self._add_vegetation_material(_idf, roof_vegetation)
|
||||
self._update_constructions(_idf, ground, roof, wall, roof_vegetation)
|
||||
self._add_window_construction_and_material(_idf, window)
|
||||
|
||||
def _add_standard_compact_hourly_schedule(self, _idf, schedule_name, schedules):
|
||||
_kwargs = {'Name': f'{schedule_name}',
|
||||
'Schedule_Type_Limits_Name': self.idf_type_limits[schedules[0].data_type],
|
||||
'Field_1': 'Through: 12/31'}
|
||||
for j, schedule in enumerate(schedules):
|
||||
_val = schedule.values
|
||||
_new_field = ''
|
||||
for day_type in schedule.day_types:
|
||||
_new_field += f' {self.idf_day_types[day_type]}'
|
||||
_kwargs[f'Field_{j * 25 + 2}'] = f'For:{_new_field}'
|
||||
for i in range(0, len(_val)):
|
||||
_kwargs[f'Field_{j * 25 + 3 + i}'] = f'Until: {i + 1:02d}:00,{_val[i]}'
|
||||
_idf.newidfobject(self._COMPACT_SCHEDULE, **_kwargs)
|
||||
|
||||
def _add_schedules(self, _idf, thermal_zone):
|
||||
self._add_standard_compact_hourly_schedule(_idf, 'user_occupancy_schedule',
|
||||
thermal_zone.occupancy.occupancy_schedules)
|
||||
self._add_standard_compact_hourly_schedule(_idf, 'user_lighting_schedule',
|
||||
thermal_zone.lighting.schedules)
|
||||
self._add_standard_compact_hourly_schedule(_idf, 'user_appliances_schedule',
|
||||
thermal_zone.appliances.schedules)
|
||||
self._add_standard_compact_hourly_schedule(_idf, 'user_heating_schedule',
|
||||
thermal_zone.thermal_control.heating_set_point_schedules)
|
||||
self._add_standard_compact_hourly_schedule(_idf, 'user_cooling_schedule',
|
||||
thermal_zone.thermal_control.cooling_set_point_schedules)
|
||||
|
||||
def _add_usage(self, _idf):
|
||||
_thermal_zone = None
|
||||
for building in self._city.buildings:
|
||||
for internal_zone in building.internal_zones:
|
||||
for thermal_zone in internal_zone.thermal_zones:
|
||||
_thermal_zone = thermal_zone
|
||||
# Dompark project share schedules and usages among all the buildings so we could add just the first one
|
||||
break
|
||||
break
|
||||
break
|
||||
self._add_schedules(_idf, _thermal_zone)
|
||||
fraction_radiant = _thermal_zone.occupancy.sensible_radiative_internal_gain / \
|
||||
(_thermal_zone.occupancy.sensible_radiative_internal_gain +
|
||||
_thermal_zone.occupancy.sensible_convective_internal_gain)
|
||||
for idf_object in _idf.idfobjects[self._PEOPLE]:
|
||||
idf_object['Number_of_People_Schedule_Name'] = 'user_occupancy_schedule'
|
||||
idf_object['People_per_Zone_Floor_Area'] = _thermal_zone.occupancy.occupancy_density
|
||||
idf_object['Fraction_Radiant'] = fraction_radiant
|
||||
|
||||
for idf_object in _idf.idfobjects[self._LIGHTS]:
|
||||
idf_object['Schedule_Name'] = 'user_lighting_schedule'
|
||||
idf_object['Watts_per_Zone_Floor_Area'] = _thermal_zone.lighting.density
|
||||
for idf_object in _idf.idfobjects[self._APPLIANCES]:
|
||||
idf_object['Schedule_Name'] = 'user_appliances_schedule'
|
||||
idf_object['Power_per_Zone_Floor_Area'] = _thermal_zone.appliances.density
|
||||
for idf_object in _idf.idfobjects[self._HEATING_COOLING]:
|
||||
idf_object['Heating_Setpoint_Temperature_Schedule_Name'] = 'user_heating_schedule'
|
||||
idf_object['Cooling_Setpoint_Temperature_Schedule_Name'] = 'user_cooling_schedule'
|
||||
|
||||
return
|
||||
|
||||
def get(self):
|
||||
session = refresh_session(request)
|
||||
if session is None:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
headers = session.headers
|
||||
self._city = session.city
|
||||
self._greenery_percentage = round(float(session.greenery_percentage) / 10) * 10
|
||||
output_file = str((self._output_path / 'dompark.idf').resolve())
|
||||
idd_file = str((self._data_path / 'energy+.idd').resolve())
|
||||
epw_file = str((self._data_path / 'CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').resolve())
|
||||
idf_file = str((self._data_path / 'dompark.idf').resolve())
|
||||
IDF.setiddname(idd_file)
|
||||
_idf = IDF(idf_file, epw_file)
|
||||
self._add_materials(_idf)
|
||||
self._add_usage(_idf)
|
||||
_idf.newidfobject(
|
||||
"OUTPUT:VARIABLE",
|
||||
Variable_Name="Zone Ideal Loads Supply Air Total Heating Energy",
|
||||
Reporting_Frequency="Hourly",
|
||||
)
|
||||
_idf.newidfobject(
|
||||
"OUTPUT:VARIABLE",
|
||||
Variable_Name="Zone Ideal Loads Supply Air Total Cooling Energy",
|
||||
Reporting_Frequency="Hourly",
|
||||
)
|
||||
# From EnergyPlus documentation: Lights Electric Energy [J]
|
||||
# The lighting electrical consumption including ballasts, if present. These will have the same value as Lights
|
||||
# Total Heating Energy (above).
|
||||
_idf.newidfobject(
|
||||
"OUTPUT:VARIABLE",
|
||||
Variable_Name="Lights Total Heating Energy",
|
||||
Reporting_Frequency="Hourly",
|
||||
)
|
||||
_idf.newidfobject(
|
||||
"OUTPUT:VARIABLE",
|
||||
Variable_Name="Other Equipment Total Heating Energy",
|
||||
Reporting_Frequency="Hourly",
|
||||
)
|
||||
|
||||
_idf.match()
|
||||
_idf.saveas(str(output_file))
|
||||
_idf.run(expandobjects=True, readvars=True, output_directory=self._output_path, output_prefix='dompark_')
|
||||
# Todo: set the heating and cooling
|
||||
heating = []
|
||||
cooling = []
|
||||
lighting = []
|
||||
appliances = []
|
||||
with open((self._output_path / f'dompark_out.csv').resolve()) as f:
|
||||
reader = csv.reader(f, delimiter=',')
|
||||
for row in reader:
|
||||
if '00:00' in row[0]:
|
||||
cooling_value = 0.0
|
||||
heating_value = 0.0
|
||||
lighting_value = 0.0
|
||||
appliances_value = 0.0
|
||||
for i in range(1, 38):
|
||||
lighting_value += float(row[i])
|
||||
for i in range(38, 73):
|
||||
appliances_value += float(row[i])
|
||||
for i in range(73, 133, 2):
|
||||
heating_value += float(row[i])
|
||||
cooling_value += float(row[i + 1])
|
||||
cooling.append(cooling_value)
|
||||
heating.append(heating_value)
|
||||
lighting.append(lighting_value)
|
||||
appliances.append(appliances_value)
|
||||
|
||||
files = glob.glob(f'{self._output_path}/dompark*')
|
||||
for file in files:
|
||||
os.remove(file)
|
||||
continue
|
||||
|
||||
response = {'heating_demand': heating,
|
||||
'cooling_demand': cooling,
|
||||
'lighting_demand': lighting,
|
||||
'appliances_demand': appliances
|
||||
}
|
||||
return Response(json.dumps(response), headers=headers)
|
29
hub_api/geometry.py
Normal file
29
hub_api/geometry.py
Normal file
@ -0,0 +1,29 @@
|
||||
"""
|
||||
Geometry
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Project Author name guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
|
||||
import json
|
||||
from flask import make_response, send_file, request, Response
|
||||
from flask_restful import Resource
|
||||
from pathlib import Path
|
||||
|
||||
from hub_api.helpers.session_helper import refresh_session
|
||||
|
||||
|
||||
class Geometry(Resource):
|
||||
def __init__(self):
|
||||
data_path = (Path(__file__).parent.parent / 'data').resolve()
|
||||
self._gtlf_path = (Path(data_path / 'DomparkBuilding.gltf')).resolve()
|
||||
|
||||
def get(self):
|
||||
session = refresh_session(request)
|
||||
if session is None:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
response = make_response(send_file(self._gtlf_path,
|
||||
as_attachment=True,
|
||||
mimetype='model/gltf+json, model/gltf-binary'))
|
||||
response.headers['session_id'] = session.id
|
||||
response.headers['token'] = session.token
|
||||
return response
|
85
hub_api/greenery.py
Normal file
85
hub_api/greenery.py
Normal file
@ -0,0 +1,85 @@
|
||||
"""
|
||||
Greenery
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from flask import Response, request
|
||||
from flask_restful import Resource
|
||||
|
||||
from city_model_structure.greenery.vegetation import Vegetation
|
||||
from city_model_structure.greenery.soil import Soil
|
||||
from city_model_structure.greenery.plant import Plant
|
||||
import helpers.constants as cte
|
||||
from hub_api.helpers.session_helper import refresh_session
|
||||
|
||||
|
||||
class Greenery(Resource):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def put():
|
||||
session = refresh_session(request)
|
||||
if session is None:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
headers = session.headers
|
||||
city = session.city
|
||||
try:
|
||||
session.greenery_percentage = request.json['greenery_percentage']
|
||||
if session.greenery_percentage == 0:
|
||||
response = {'result': 'succeed'}
|
||||
return Response(json.dumps(response), headers=headers)
|
||||
|
||||
building_names = request.json['building_names']
|
||||
vegetation_requested = request.json['vegetation']
|
||||
vegetation_name = vegetation_requested['vegetation_name']
|
||||
soil_thickness = vegetation_requested['soil_thickness']
|
||||
soil_name = vegetation_requested['soil_name']
|
||||
roughness = vegetation_requested['soil_roughness']
|
||||
dry_conductivity = vegetation_requested['dry_soil_conductivity']
|
||||
dry_density = vegetation_requested['dry_soil_density']
|
||||
dry_specific_heat = vegetation_requested['dry_soil_specific_heat']
|
||||
thermal_absorptance = vegetation_requested['soil_thermal_absorptance']
|
||||
solar_absorptance = vegetation_requested['soil_solar_absorptance']
|
||||
visible_absorptance = vegetation_requested['soil_visible_absorptance']
|
||||
saturation_volumetric_moisture_content = vegetation_requested['soil_saturation_volumetric_moisture_content']
|
||||
residual_volumetric_moisture_content = vegetation_requested['soil_residual_volumetric_moisture_content']
|
||||
soil = Soil(soil_name, roughness, dry_conductivity, dry_density, dry_specific_heat, thermal_absorptance,
|
||||
solar_absorptance, visible_absorptance, saturation_volumetric_moisture_content,
|
||||
residual_volumetric_moisture_content)
|
||||
soil.initial_volumetric_moisture_content = '0.1'
|
||||
plant_percentages = vegetation_requested['plant_percentages']
|
||||
plants = []
|
||||
for plant_percentage in plant_percentages:
|
||||
plant_name = plant_percentage['plant_name']
|
||||
height = plant_percentage['plant_height']
|
||||
leaf_area_index = plant_percentage['plant_leaf_area_index']
|
||||
leaf_reflectivity = plant_percentage['plant_leaf_reflectivity']
|
||||
leaf_emissivity = plant_percentage['plant_leaf_emissivity']
|
||||
minimal_stomatal_resistance = plant_percentage['plant_minimal_stomatal_resistance']
|
||||
co2_sequestration = plant_percentage['plant_co2_sequestration']
|
||||
grows_on_soils = plant_percentage['plant_grows_on']
|
||||
plant = Plant(plant_name, height, leaf_area_index, leaf_reflectivity, leaf_emissivity,
|
||||
minimal_stomatal_resistance, co2_sequestration, grows_on_soils)
|
||||
plant.percentage = plant_percentage['plant_percentage']
|
||||
plants.append(plant)
|
||||
vegetation = Vegetation(vegetation_name, soil, soil_thickness, plants)
|
||||
for building_name in building_names:
|
||||
for building in city.buildings:
|
||||
if building.human_readable_name != building_name:
|
||||
continue
|
||||
for internal_zone in building.internal_zones:
|
||||
for thermal_zone in internal_zone.thermal_zones:
|
||||
for thermal_boundary in thermal_zone.thermal_boundaries:
|
||||
if thermal_boundary.type == cte.ROOF:
|
||||
thermal_boundary.vegetation = vegetation
|
||||
|
||||
response = {'result': 'succeed'}
|
||||
except KeyError as ex:
|
||||
response = {'error': f'Mandatory parameter {ex} is missing'}
|
||||
return Response(json.dumps(response), headers=headers, status=400)
|
||||
|
||||
return Response(json.dumps(response), headers=headers)
|
180
hub_api/greenery_catalog.py
Normal file
180
hub_api/greenery_catalog.py
Normal file
@ -0,0 +1,180 @@
|
||||
"""
|
||||
Greenery catalog
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Project Author name guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from flask import request, Response
|
||||
from flask_restful import Resource
|
||||
|
||||
from catalog_factories.data_models.greenery.plant import Plant
|
||||
from catalog_factories.data_models.greenery.soil import Soil
|
||||
from catalog_factories.data_models.greenery.vegetation import Vegetation
|
||||
from hub_api.helpers.session_helper import refresh_session
|
||||
|
||||
|
||||
class ToJson:
|
||||
@staticmethod
|
||||
def plant_to_json(plant, percentage=None):
|
||||
dictionary = {
|
||||
'plant_percentage': percentage,
|
||||
'plant_name': plant.name,
|
||||
'plant_category': plant.category,
|
||||
'plant_height': plant.height,
|
||||
'plant_leaf_area_index': plant.leaf_area_index,
|
||||
'plant_leaf_reflectivity': plant.leaf_reflectivity,
|
||||
'plant_leaf_emissivity': plant.leaf_emissivity,
|
||||
'plant_minimal_stomatal_resistance': plant.minimal_stomatal_resistance,
|
||||
'plant_co2_sequestration': plant.co2_sequestration,
|
||||
'plant_grows_on': []
|
||||
}
|
||||
if percentage is None:
|
||||
dictionary.pop('plant_percentage')
|
||||
soils = []
|
||||
for soil in plant.grows_on:
|
||||
soil_dic = ToJson.soil_to_json(soil)
|
||||
soils.append(soil_dic)
|
||||
dictionary['plant_grows_on'] = soils
|
||||
return dictionary
|
||||
|
||||
@staticmethod
|
||||
def soil_to_json(soil):
|
||||
dictionary = {'soil_name': soil.name,
|
||||
'soil_roughness': str(soil.roughness),
|
||||
'soil_dry_conductivity': soil.dry_conductivity,
|
||||
'soil_dry_density': soil.dry_density,
|
||||
'soil_dry_specific_heat': soil.dry_specific_heat,
|
||||
'soil_thermal_absortance': soil.thermal_absorptance,
|
||||
'soil_solar_absortance': soil.solar_absorptance,
|
||||
'soil_visible_absortance': soil.visible_absorptance,
|
||||
'soil_saturation_volumetric_moisture_content': soil.saturation_volumetric_moisture_content,
|
||||
'soil_residual_volumetric_moisture_content': soil.residual_volumetric_moisture_content,
|
||||
'soil_initial_volumetric_moisture_content': soil.initial_volumetric_moisture_content
|
||||
}
|
||||
return dictionary
|
||||
|
||||
@staticmethod
|
||||
def vegetation_to_json(vegetation):
|
||||
|
||||
dictionary = {'vegetation_name': vegetation.name,
|
||||
'vegetation_category': vegetation.category,
|
||||
'soil_thickness': vegetation.soil_thickness,
|
||||
'management': str(vegetation.management),
|
||||
'air_gap': vegetation.air_gap,
|
||||
'soil_name': vegetation.soil_name,
|
||||
'soil_roughness': str(vegetation.soil_roughness),
|
||||
'dry_soil_conductivity': vegetation.dry_soil_conductivity,
|
||||
'dry_soil_density': vegetation.dry_soil_density,
|
||||
'dry_soil_specific_heat': vegetation.dry_soil_specific_heat,
|
||||
'soil_thermal_absorptance': vegetation.soil_thermal_absorptance,
|
||||
'soil_solar_absorptance': vegetation.soil_solar_absorptance,
|
||||
'soil_visible_absorptance': vegetation.soil_visible_absorptance,
|
||||
'soil_saturation_volumetric_moisture_content': vegetation.soil_saturation_volumetric_moisture_content,
|
||||
'soil_residual_volumetric_moisture_content': vegetation.soil_residual_volumetric_moisture_content,
|
||||
'soil_initial_volumetric_moisture_content': vegetation.soil_initial_volumetric_moisture_content,
|
||||
'plant_percentages': []
|
||||
}
|
||||
percentages = []
|
||||
for percentage in vegetation.plant_percentages:
|
||||
percentage_dic = ToJson.plant_to_json(percentage, percentage.percentage)
|
||||
percentages.append(percentage_dic)
|
||||
dictionary['plant_percentages'] = percentages
|
||||
return dictionary
|
||||
|
||||
|
||||
class GreeneryCatalogEntry(Resource):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def post():
|
||||
session = refresh_session(request)
|
||||
if session is None:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
headers = session.headers
|
||||
catalog = session.greenery_catalog
|
||||
name = None
|
||||
if request.data == b'' or request.json['name'] is None:
|
||||
response = {'error': 'Mandatory parameter "name" is missing'}
|
||||
return Response(json.dumps(response), headers=headers, status=400)
|
||||
try:
|
||||
name = request.json['name']
|
||||
entry = catalog.get_entry(name)
|
||||
output = {}
|
||||
if isinstance(entry, Vegetation):
|
||||
output['vegetations'] = ToJson.vegetation_to_json(entry)
|
||||
if isinstance(entry, Plant):
|
||||
output['plants'] = ToJson.plant_to_json(entry)
|
||||
if isinstance(entry, Soil):
|
||||
output['soils'] = ToJson.soil_to_json(entry)
|
||||
return Response(json.dumps(output), headers=headers)
|
||||
except IndexError:
|
||||
response = {'error': f'Name "{name}" unknown'}
|
||||
return Response(json.dumps(response), headers=headers, status=400)
|
||||
|
||||
|
||||
class GreeneryCatalogEntries(Resource):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def post():
|
||||
session = refresh_session(request)
|
||||
if session is None:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
headers = session.headers
|
||||
catalog = session.greenery_catalog
|
||||
category = None
|
||||
if request.data != b'':
|
||||
category = request.json['category']
|
||||
output = {}
|
||||
if category is None:
|
||||
content = catalog.entries()
|
||||
output = {'vegetations': [], 'plants': [], 'soils': []}
|
||||
for vegetation in content.vegetations:
|
||||
output['vegetations'].append(ToJson.vegetation_to_json(vegetation))
|
||||
for plant in content.plants:
|
||||
output['plants'].append(ToJson.plant_to_json(plant))
|
||||
for soil in content.soils:
|
||||
output['soils'].append(ToJson.soil_to_json(soil))
|
||||
else:
|
||||
try:
|
||||
content = catalog.entries(category)
|
||||
output[category] = []
|
||||
for entry in content:
|
||||
if isinstance(entry, Vegetation):
|
||||
output[category].append(ToJson.vegetation_to_json(entry))
|
||||
if isinstance(entry, Plant):
|
||||
output[category].append(ToJson.plant_to_json(entry))
|
||||
if isinstance(entry, Soil):
|
||||
output[category].append(ToJson.soil_to_json(entry))
|
||||
except ValueError:
|
||||
output = {'error': f'Category "{category}" unknown'}
|
||||
return Response(json.dumps(output), headers=headers, status=400)
|
||||
return Response(json.dumps(output), headers=headers)
|
||||
|
||||
|
||||
class GreeneryCatalogNames(Resource):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def post():
|
||||
session = refresh_session(request)
|
||||
if session is None:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
headers = session.headers
|
||||
catalog = session.greenery_catalog
|
||||
category = None
|
||||
if request.data != b'':
|
||||
category = request.json['category']
|
||||
if category is None:
|
||||
return Response(json.dumps(catalog.names()), headers=headers)
|
||||
else:
|
||||
try:
|
||||
return Response(json.dumps(catalog.names(category)), headers=headers)
|
||||
except ValueError:
|
||||
response = {'error': f'Category "{category}" unknown'}
|
||||
return Response(json.dumps(response), headers=headers, status=400)
|
79
hub_api/heat_pump.py
Normal file
79
hub_api/heat_pump.py
Normal file
@ -0,0 +1,79 @@
|
||||
"""
|
||||
HeatPump Service
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Project Author Peter Yefi peteryefi@gmail.com
|
||||
"""
|
||||
import json
|
||||
|
||||
from flask import send_file, request, make_response, Response
|
||||
from flask_apispec import use_kwargs, doc
|
||||
from flask_apispec.views import MethodResource
|
||||
from flask_restful import Resource
|
||||
from marshmallow import Schema, fields
|
||||
|
||||
from hub_api.helpers.session_helper import refresh_session
|
||||
from utils import HeatPumpSimulator
|
||||
from utils import validate_hp_model
|
||||
|
||||
|
||||
class HeatPumpPostData(Schema):
|
||||
"""
|
||||
Defines post data for heat-pump simulation
|
||||
"""
|
||||
StartYear = fields.Integer(required=True, description='Start year for simulation data')
|
||||
EndYear = fields.Integer(required=True, description='End year for simulation data')
|
||||
MaximumHPEnergyInput = fields.Float(required=True, description='Maximum heat pump energy input')
|
||||
HoursOfStorageAtMaxDemand = fields.Integer(required=True, description='Hours of storage at maximum demand')
|
||||
BuildingSuppTemp = fields.Integer(required=True, description='Building supply temperature')
|
||||
TemperatureDifference = fields.Float(required=True, description='Temperature difference')
|
||||
FuelLHV = fields.Float(required=True, description='Fuel LHV')
|
||||
FuelPrice = fields.Float(required=True, description='Fuel price')
|
||||
FuelEF = fields.Integer(required=True, description='Fuel EF')
|
||||
FuelDensity = fields.Float(required=True, description='Fuel Density')
|
||||
HPSupTemp = fields.Float(required=True, description='Heat pump supply temperature')
|
||||
HeatPumpType = fields.String(required=True, description='Type of Heat pump',
|
||||
enum=['Water to Water HP', 'Air Source HP'])
|
||||
HeatPumpModel = fields.String(required=True, description='Model of heat pump to run simulation for',
|
||||
enum=['ClimateMaster 156 kW', 'ClimateMaster 256 kW', 'ClimateMaster 335 kW',
|
||||
'012', '015', '018', '023', '030', '033', '037', '044', '047', '057', '070',
|
||||
'087', '097', '102', '120', '130', '140'])
|
||||
SimType = fields.Integer(required=True, description='Series or Parallel simulation [0 for series, 1 for parallel',
|
||||
enum=[0, 1])
|
||||
|
||||
|
||||
class HeatPump(MethodResource, Resource):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@doc(description='Heat pump simulation run', tags=['HeatPump'])
|
||||
@use_kwargs(HeatPumpPostData)
|
||||
def post(self, **kwargs):
|
||||
session = refresh_session(request)
|
||||
if session is None:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
city = session.city
|
||||
if validate_hp_model(kwargs['HeatPumpType'], kwargs['HeatPumpModel']):
|
||||
try:
|
||||
# Run simulation and return output file here
|
||||
hp_simulator = HeatPumpSimulator(city, kwargs)
|
||||
result_file = hp_simulator.run_hp_simulation()
|
||||
response = self._send_response(result_file, session)
|
||||
return response
|
||||
except Exception as err:
|
||||
print(err)
|
||||
return Response(json.dumps({'error_message': 'Sorry an error occurred while running HP Simulation'}))
|
||||
else:
|
||||
return Response(json.dumps({'error_message': 'Wrong heat pump type/model combination'}), status=400)
|
||||
|
||||
@staticmethod
|
||||
def _send_response(result_file, session):
|
||||
"""
|
||||
Sends insel results file after simulation
|
||||
:param result_file: the insel output file
|
||||
:param session: session variable
|
||||
:return CSV output file
|
||||
"""
|
||||
response = make_response(send_file(result_file, as_attachment=True, mimetype='text/csv'))
|
||||
response.headers['session_id'] = session.id
|
||||
response.headers['token'] = session.token
|
||||
return response
|
189
hub_api/helpers/session_helper.py
Normal file
189
hub_api/helpers/session_helper.py
Normal file
@ -0,0 +1,189 @@
|
||||
"""
|
||||
Session helper
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Project Author name guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
import uuid
|
||||
import datetime
|
||||
|
||||
sessions = []
|
||||
begin_time = None
|
||||
city = None
|
||||
greenery_catalog = None
|
||||
construction_catalog = None
|
||||
usage_catalog = None
|
||||
|
||||
roofs_associated_to_percentage = {'10': ['BuildingAX5th:ZoneX56Xspace_Roof_1_0_0',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_1',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_2',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_3',
|
||||
'BuildingAX3rd:ZoneX76Xspace_Roof_1_0_0'],
|
||||
'20': ['BuildingBX2nd:Airbnb2_Roof_1_0_0',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_0',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_1'],
|
||||
'30': ['BuildingBX2nd:Airbnb_Roof_1_0_0',
|
||||
'BuildingAX5th:ZoneX53Xspace_Roof_1_0_0',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_0'],
|
||||
'40': ['AdditionalBuildings:ZoneX73Xspace_Roof_1_0_2',
|
||||
'BuildingBX2nd:Airbnb_Roof_1_0_0',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_4',
|
||||
'BuildingAX5th:ZoneX54Xspace_Roof_1_0_0'],
|
||||
'50': ['BuildingBX2nd:Airbnb2_Roof_1_0_0',
|
||||
'BuildingBX2nd:Airbnb_Roof_1_0_0',
|
||||
'BuildingAX5th:ZoneX53Xspace_Roof_1_0_0',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_0',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_1',
|
||||
'BuildingAX5th:ZoneX55Xspace_Roof_1_0_0'],
|
||||
'60': ['BuildingBX2nd:Airbnb2_Roof_1_0_0',
|
||||
'BuildingBX2nd:Airbnb_Roof_1_0_0',
|
||||
'BuildingAX5th:ZoneX53Xspace_Roof_1_0_0',
|
||||
'BuildingAX5th:ZoneX54Xspace_Roof_1_0_0'],
|
||||
'70': ['BuildingBX2nd:Airbnb2_Roof_1_0_0',
|
||||
'BuildingBX2nd:Airbnb_Roof_1_0_0',
|
||||
'BuildingAX5th:ZoneX56Xspace_Roof_1_0_0',
|
||||
'BuildingAX5th:ZoneX53Xspace_Roof_1_0_0',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_1',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_2',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_3',
|
||||
'BuildingAX5th:ZoneX54Xspace_Roof_1_0_0',
|
||||
'BuildingAX3rd:ZoneX76Xspace_Roof_1_0_0'],
|
||||
'80': ['BuildingCX2nd:ZoneX41Xspace_Roof_1_0_0',
|
||||
'BuildingBX2nd:Corridor_Roof_1_0_0',
|
||||
'BuildingBX2nd:Airbnb2_Roof_1_0_0',
|
||||
'BuildingBX2nd:Airbnb_Roof_1_0_0',
|
||||
'BuildingAX5th:ZoneX53Xspace_Roof_1_0_0',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_2',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_4',
|
||||
'BuildingAX5th:ZoneX54Xspace_Roof_1_0_0'],
|
||||
'90': ['AdditionalBuildings:ZoneX73Xspace_Roof_1_0_0',
|
||||
'AdditionalBuildings:ZoneX73Xspace_Roof_1_0_1',
|
||||
'AdditionalBuildings:ZoneX73Xspace_Roof_1_0_2',
|
||||
'BuildingCX2nd:ZoneX41Xspace_Roof_1_0_0',
|
||||
'BuildingBX2nd:Corridor_Roof_1_0_0',
|
||||
'BuildingBX2nd:Airbnb2_Roof_1_0_0',
|
||||
'BuildingBX2nd:Airbnb_Roof_1_0_0',
|
||||
'BuildingAX5th:ZoneX53Xspace_Roof_1_0_0',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_0',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_4',
|
||||
'BuildingAX5th:ZoneX54Xspace_Roof_1_0_0',
|
||||
'BuildingAX5th:ZoneX55Xspace_Roof_1_0_0'],
|
||||
'100': ['AdditionalBuildings:ZoneX73Xspace_Roof_1_0_0',
|
||||
'AdditionalBuildings:ZoneX73Xspace_Roof_1_0_1',
|
||||
'AdditionalBuildings:ZoneX73Xspace_Roof_1_0_2',
|
||||
'BuildingCX2nd:ZoneX41Xspace_Roof_1_0_0',
|
||||
'BuildingBX2nd:Corridor_Roof_1_0_0',
|
||||
'BuildingBX2nd:Airbnb2_Roof_1_0_0',
|
||||
'BuildingBX2nd:Airbnb_Roof_1_0_0',
|
||||
'BuildingAX5th:ZoneX56Xspace_Roof_1_0_0',
|
||||
'BuildingAX5th:ZoneX53Xspace_Roof_1_0_0',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_0',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_1',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_2',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_3',
|
||||
'BuildingAX5th:Corridor_Roof_1_0_4',
|
||||
'BuildingAX5th:ZoneX54Xspace_Roof_1_0_0',
|
||||
'BuildingAX5th:ZoneX55Xspace_Roof_1_0_0',
|
||||
'BuildingAX3rd:ZoneX76Xspace_Roof_1_0_0'],
|
||||
}
|
||||
|
||||
|
||||
class SessionData:
|
||||
def __init__(self, session):
|
||||
self._session = session
|
||||
|
||||
@property
|
||||
def city(self):
|
||||
return self._session['city']
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._session['session_id']
|
||||
|
||||
@property
|
||||
def token(self):
|
||||
return self._session['token']
|
||||
|
||||
@property
|
||||
def headers(self):
|
||||
return {'session_id': str(self.id), 'token': str(self.token)}
|
||||
|
||||
@property
|
||||
def greenery_percentage(self):
|
||||
return self._session['greenery_percentage']
|
||||
|
||||
@greenery_percentage.setter
|
||||
def greenery_percentage(self, value):
|
||||
self._session['greenery_percentage'] = value
|
||||
|
||||
@property
|
||||
def greenery_catalog(self):
|
||||
return greenery_catalog
|
||||
|
||||
@property
|
||||
def construction_catalog(self):
|
||||
return construction_catalog
|
||||
|
||||
@property
|
||||
def usage_catalog(self):
|
||||
return usage_catalog
|
||||
|
||||
|
||||
def active_session(session_id):
|
||||
for i in range(len(sessions)):
|
||||
_session = sessions[i]
|
||||
_expire = datetime.datetime.strptime(_session['expire'], '%Y-%m-%d %H:%M:%S.%f')
|
||||
if _session['session_id'] == session_id:
|
||||
if _expire > datetime.datetime.now():
|
||||
return True, i
|
||||
else:
|
||||
# Ensure remove old sessions
|
||||
del sessions[i]
|
||||
return False, -1
|
||||
return False, -1
|
||||
|
||||
|
||||
def _valid_session(session_id, ip, token):
|
||||
active, i = active_session(session_id)
|
||||
if active:
|
||||
return sessions[i]['token'] == token and sessions[i]['ip'] == ip, i
|
||||
return False, -1
|
||||
|
||||
|
||||
def remove_session(request):
|
||||
"""
|
||||
Remove a session from the sessions array
|
||||
"""
|
||||
session = eval(request.headers.get('session'))
|
||||
session_id = session['session_id']
|
||||
token = session['token']
|
||||
ip = request.remote_addr
|
||||
valid, i = _valid_session(session_id, ip, token)
|
||||
if valid:
|
||||
del sessions[i]
|
||||
return valid
|
||||
|
||||
|
||||
def refresh_session(request):
|
||||
"""
|
||||
Validate and extend current session
|
||||
:return: valid, token, city
|
||||
"""
|
||||
session_header = request.headers.get('session')
|
||||
if session_header is None:
|
||||
return None
|
||||
session = eval(session_header)
|
||||
session_id = session['session_id']
|
||||
token = session['token']
|
||||
ip = request.remote_addr
|
||||
valid, i = _valid_session(session_id, ip, token)
|
||||
if valid:
|
||||
if session_id == 'debug':
|
||||
new_token = 'debug'
|
||||
else:
|
||||
new_token = str(uuid.uuid4())
|
||||
expire = str(datetime.datetime.now() + datetime.timedelta(minutes=5))
|
||||
sessions[i]['token'] = new_token
|
||||
sessions[i]['expire'] = expire
|
||||
return SessionData(sessions[i])
|
||||
else:
|
||||
return None
|
134
hub_api/lca.py
Normal file
134
hub_api/lca.py
Normal file
@ -0,0 +1,134 @@
|
||||
"""
|
||||
LCA
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2021 Project Author name Atiya
|
||||
"""
|
||||
import json
|
||||
from flask import request, Response
|
||||
from flask_restful import Resource
|
||||
from lca_calculations import LcaCalculations
|
||||
from hub_api.helpers.session_helper import refresh_session
|
||||
from itertools import groupby
|
||||
from operator import itemgetter
|
||||
|
||||
|
||||
class MaterialLCACatalog(Resource):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_lca_value(city, nrel_id = None):
|
||||
nrel_material = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "14", "15", "16", "18", "20", "21",
|
||||
"22", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", "112", "113", "114"]
|
||||
lca_material = ["12", "32", "21", "25", "11", "20", "40", "27", "29", "28", "19", "40", "41", "25", "32", "32", "32", "21", "41",
|
||||
"21", "11", "33", "28", "11", "40", "1", "28", "41", "0", "0", "0", "0", "0", ]
|
||||
keys = ['nrel_material_id', 'lca_material_id']
|
||||
material_mapping = [dict(zip(keys, i)) for i in zip(nrel_material, lca_material)]
|
||||
|
||||
if nrel_id is None:
|
||||
materials_lca = {"material_lca_catalog": []}
|
||||
for mat in material_mapping:
|
||||
|
||||
if mat["nrel_material_id"] not in ("110", "111", "112", "113", "114"):
|
||||
for material in city.lca_materials:
|
||||
if(int(mat["lca_material_id"]) == material.id):
|
||||
material_lca_catalog = {}
|
||||
material_lca_catalog['nrel_material_id'] = mat["nrel_material_id"]
|
||||
material_lca_catalog['embodied_carbon'] = material.embodied_carbon
|
||||
mat_end_of_life = LcaCalculations(city).end_life_carbon(material.type)
|
||||
material_lca_catalog['end_of_life_carbon'] = mat_end_of_life
|
||||
materials_lca["material_lca_catalog"].append(material_lca_catalog)
|
||||
|
||||
else:
|
||||
material_lca_catalog = {}
|
||||
material_lca_catalog['nrel_material_id'] = mat["nrel_material_id"]
|
||||
material_lca_catalog['embodied_carbon'] = 0.0
|
||||
material_lca_catalog['end_of_life_carbon'] = 0.0
|
||||
materials_lca["material_lca_catalog"].append(material_lca_catalog)
|
||||
|
||||
return materials_lca
|
||||
|
||||
else:
|
||||
for mat in material_mapping:
|
||||
if mat["nrel_material_id"] == str(nrel_id):
|
||||
for material in city.lca_materials:
|
||||
if (material.id == int(mat["lca_material_id"])):
|
||||
return material.embodied_carbon, material.id, material.type, material.name, material.density
|
||||
# return material.embodied_carbon
|
||||
|
||||
|
||||
|
||||
def get(self):
|
||||
session = refresh_session(request)
|
||||
if session is None:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
headers = session.headers
|
||||
city = session.city
|
||||
|
||||
try:
|
||||
return Response(json.dumps(self.get_lca_value(city)), headers=headers)
|
||||
except ValueError:
|
||||
response = {'error': f'No Catalog Available'}
|
||||
return Response(json.dumps(response), headers=headers, status=400)
|
||||
|
||||
|
||||
class MaterialLCACalculations(Resource):
|
||||
"""
|
||||
LCA class
|
||||
"""
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get():
|
||||
"""
|
||||
Auto-method for processing the lca request
|
||||
:return: lca demand
|
||||
"""
|
||||
session = refresh_session(request)
|
||||
if session is None:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
headers = session.headers
|
||||
city = session.city
|
||||
|
||||
materials_lca = {'Wall': [], 'Ground': [], 'Roof': []}
|
||||
for building in city.buildings:
|
||||
for internal_zone in building.internal_zones:
|
||||
for thermal_zone in internal_zone.thermal_zones:
|
||||
for thermal_boundary in thermal_zone.thermal_boundaries:
|
||||
for i, layer in enumerate(thermal_boundary.layers):
|
||||
material_layers_lca = {}
|
||||
if layer.material.no_mass == False:
|
||||
embodied_carbon, mat_id, mat_type, mat_name, mat_density = MaterialLCACatalog.get_lca_value(city, layer.material.id)
|
||||
material_layers_lca['layer_name'] = f'Layer {i+1}'
|
||||
material_layers_lca['nrel_material_id'] = layer.material.id
|
||||
material_layers_lca['lca_material_id'] = mat_id
|
||||
material_layers_lca['embodied_carbon'] = (layer.thickness * thermal_boundary.opaque_area) * mat_density * embodied_carbon
|
||||
mat_end_of_life = LcaCalculations(city).end_life_carbon(mat_type)
|
||||
material_end_of_life = mat_end_of_life * (layer.thickness * thermal_boundary.opaque_area) * mat_density
|
||||
material_layers_lca['end_of_life_per_layer'] = material_end_of_life
|
||||
materials_lca[thermal_boundary.type].append(material_layers_lca)
|
||||
else:
|
||||
material_layers_lca['layer_name'] = f'Layer {i+1}'
|
||||
material_layers_lca['nrel_material_id'] = layer.material.id
|
||||
material_layers_lca['lca_material_id'] = mat_id
|
||||
material_layers_lca['embodied_carbon'] = (layer.thickness * thermal_boundary.opaque_area) * mat_density * embodied_carbon
|
||||
material_layers_lca['end_of_life_per_layer'] = 0.0
|
||||
materials_lca[thermal_boundary.type].append(material_layers_lca)
|
||||
|
||||
materials_embodied_carbon = {'Wall': [], 'Ground': [], 'Roof': []}
|
||||
for key, value in materials_lca.items():
|
||||
boundary_layers = sorted(value, key=itemgetter('layer_name'))
|
||||
for b_layer, layer_properties in groupby(boundary_layers, key=itemgetter('layer_name')):
|
||||
sum_embodied = 0.0
|
||||
sum_end_of_life = 0.0
|
||||
total_embodied_carbon = {}
|
||||
for k in layer_properties:
|
||||
sum_embodied += k["embodied_carbon"]
|
||||
sum_end_of_life += k["end_of_life_per_layer"]
|
||||
total_embodied_carbon['layer_name'] = b_layer
|
||||
total_embodied_carbon['embodied_carbon'] = sum_embodied
|
||||
total_embodied_carbon['end_of_life_carbon'] = sum_end_of_life
|
||||
materials_embodied_carbon[key].append(total_embodied_carbon)
|
||||
|
||||
return Response(json.dumps(materials_embodied_carbon), headers=headers)
|
70
hub_api/session.py
Normal file
70
hub_api/session.py
Normal file
@ -0,0 +1,70 @@
|
||||
"""
|
||||
Session
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Project Author name guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
|
||||
import json
|
||||
import uuid
|
||||
import datetime
|
||||
|
||||
from hub_api.helpers.session_helper import remove_session, active_session, sessions, refresh_session
|
||||
import hub_api.helpers.session_helper as sh
|
||||
from flask import request, Response
|
||||
from flask_restful import Resource
|
||||
|
||||
|
||||
class SessionStart(Resource):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def put():
|
||||
ip = request.remote_addr
|
||||
session = eval(request.headers.get('Session', None))
|
||||
session_id = session['session_id']
|
||||
active, i = active_session(session_id)
|
||||
if active:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
if session_id == 'debug':
|
||||
token = 'debug'
|
||||
else:
|
||||
token = str(uuid.uuid4())
|
||||
expire = str(datetime.datetime.now() + datetime.timedelta(minutes=5))
|
||||
session = {'session_id': session_id,
|
||||
'token': token,
|
||||
'expire': expire,
|
||||
'ip': ip,
|
||||
'city': sh.city.copy,
|
||||
'greenery_catalog': sh.greenery_catalog,
|
||||
'construction_catalog': sh.construction_catalog,
|
||||
'usage_catalog': sh.usage_catalog,
|
||||
'greenery_percentage': 0
|
||||
}
|
||||
response = {'session_id': session_id, 'token': token}
|
||||
sessions.append(session)
|
||||
return Response(json.dumps(response))
|
||||
|
||||
|
||||
class SessionEnd(Resource):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def put():
|
||||
if remove_session(request):
|
||||
return Response(json.dumps({'result': 'succeed'}))
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
|
||||
class KeepSessionAlive(Resource):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def put():
|
||||
session = refresh_session(request)
|
||||
if session is None:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
headers = session.headers
|
||||
response = {'result': 'succeed'}
|
||||
return Response(json.dumps(response), headers=headers)
|
22
hub_api/uptime.py
Normal file
22
hub_api/uptime.py
Normal file
@ -0,0 +1,22 @@
|
||||
"""
|
||||
Uptime
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Project Author name guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import json
|
||||
from flask import Response
|
||||
from flask_restful import Resource
|
||||
|
||||
import hub_api.helpers.session_helper as sh
|
||||
|
||||
|
||||
class Uptime(Resource):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get():
|
||||
uptime = {"uptime": f"{datetime.datetime.now() - sh.begin_time}"}
|
||||
return Response(json.dumps(uptime))
|
153
hub_api/usage.py
Normal file
153
hub_api/usage.py
Normal file
@ -0,0 +1,153 @@
|
||||
"""
|
||||
Usage
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Project Author name guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
|
||||
import json
|
||||
from flask import Response, request
|
||||
from flask_restful import Resource
|
||||
|
||||
from city_model_structure.attributes.schedule import Schedule
|
||||
from city_model_structure.building_demand.appliances import Appliances
|
||||
from city_model_structure.building_demand.lighting import Lighting
|
||||
from city_model_structure.building_demand.occupancy import Occupancy
|
||||
from city_model_structure.building_demand.thermal_control import ThermalControl
|
||||
from city_model_structure.building_demand.usage_zone import UsageZone
|
||||
from hub_api.helpers.session_helper import refresh_session
|
||||
import helpers.constants as cte
|
||||
|
||||
|
||||
class Usage(Resource):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def put():
|
||||
session = refresh_session(request)
|
||||
if session is None:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
headers = session.headers
|
||||
city = session.city
|
||||
catalog = session.usage_catalog
|
||||
usage_name = None
|
||||
try:
|
||||
building_names = request.json['building_names']
|
||||
usages = request.json['usages']
|
||||
seconds_in_hour = cte.MINUTES_TO_SECONDS * cte.HOUR_TO_MINUTES
|
||||
for building_name in building_names:
|
||||
for building in city.buildings:
|
||||
if building.human_readable_name != building_name:
|
||||
continue
|
||||
for internal_zone in building.internal_zones:
|
||||
internal_zone.usage_zones = []
|
||||
for usage in usages:
|
||||
usage_entry = catalog.get_entry(usage['name'])
|
||||
occupancy_schedules = []
|
||||
for schedule in usage_entry.occupancy.schedules:
|
||||
occupancy_schedule = Schedule()
|
||||
occupancy_schedule.type = schedule.type
|
||||
occupancy_schedule.values = schedule.values
|
||||
occupancy_schedule.data_type = schedule.data_type
|
||||
occupancy_schedule.time_step = schedule.time_step
|
||||
occupancy_schedule.time_range = schedule.time_range
|
||||
occupancy_schedule.day_types = schedule.day_types
|
||||
occupancy_schedules.append(occupancy_schedule)
|
||||
occupancy = Occupancy()
|
||||
occupancy.occupancy_density = usage_entry.occupancy.occupancy_density
|
||||
occupancy.sensible_convective_internal_gain = usage_entry.occupancy.sensible_convective_internal_gain
|
||||
occupancy.sensible_radiative_internal_gain = usage_entry.occupancy.sensible_radiative_internal_gain
|
||||
occupancy.latent_internal_gain = usage_entry.occupancy.latent_internal_gain
|
||||
occupancy.occupancy_schedules = occupancy_schedules
|
||||
|
||||
lighting_schedules = []
|
||||
for schedule in usage_entry.lighting.schedules:
|
||||
lighting_schedule = Schedule()
|
||||
lighting_schedule.type = schedule.type
|
||||
lighting_schedule.values = schedule.values
|
||||
lighting_schedule.data_type = schedule.data_type
|
||||
lighting_schedule.time_step = schedule.time_step
|
||||
lighting_schedule.time_range = schedule.time_range
|
||||
lighting_schedule.day_types = schedule.day_types
|
||||
lighting_schedules.append(lighting_schedule)
|
||||
lighting = Lighting()
|
||||
lighting.density = usage_entry.lighting.density
|
||||
lighting.convective_fraction = usage_entry.lighting.convective_fraction
|
||||
lighting.radiative_fraction = usage_entry.lighting.radiative_fraction
|
||||
lighting.latent_fraction = usage_entry.lighting.latent_fraction
|
||||
lighting.schedules = lighting_schedules
|
||||
appliances_schedules = []
|
||||
for schedule in usage_entry.appliances.schedules:
|
||||
appliances_schedule = Schedule()
|
||||
appliances_schedule.type = schedule.type
|
||||
appliances_schedule.values = schedule.values
|
||||
appliances_schedule.data_type = schedule.data_type
|
||||
appliances_schedule.time_step = schedule.time_step
|
||||
appliances_schedule.time_range = schedule.time_range
|
||||
appliances_schedule.day_types = schedule.day_types
|
||||
appliances_schedules.append(appliances_schedule)
|
||||
appliances = Appliances()
|
||||
appliances.density = usage_entry.appliances.density
|
||||
appliances.convective_fraction = usage_entry.appliances.convective_fraction
|
||||
appliances.radiative_fraction = usage_entry.appliances.radiative_fraction
|
||||
appliances.latent_fraction = usage_entry.appliances.latent_fraction
|
||||
appliances.schedules = appliances_schedules
|
||||
hvac_schedules = []
|
||||
for schedule in usage_entry.thermal_control.hvac_availability_schedules:
|
||||
hvac_schedule = Schedule()
|
||||
hvac_schedule.type = schedule.type
|
||||
hvac_schedule.values = schedule.values
|
||||
hvac_schedule.data_type = schedule.data_type
|
||||
hvac_schedule.time_step = schedule.time_step
|
||||
hvac_schedule.time_range = schedule.time_range
|
||||
hvac_schedule.day_types = schedule.day_types
|
||||
hvac_schedules.append(hvac_schedule)
|
||||
heating_schedules = []
|
||||
for schedule in usage_entry.thermal_control.heating_set_point_schedules:
|
||||
heating_schedule = Schedule()
|
||||
heating_schedule.type = schedule.type
|
||||
heating_schedule.values = schedule.values
|
||||
heating_schedule.data_type = schedule.data_type
|
||||
heating_schedule.time_step = schedule.time_step
|
||||
heating_schedule.time_range = schedule.time_range
|
||||
heating_schedule.day_types = schedule.day_types
|
||||
heating_schedules.append(heating_schedule)
|
||||
cooling_schedules = []
|
||||
for schedule in usage_entry.thermal_control.cooling_set_point_schedules:
|
||||
cooling_schedule = Schedule()
|
||||
cooling_schedule.type = schedule.type
|
||||
cooling_schedule.values = schedule.values
|
||||
cooling_schedule.data_type = schedule.data_type
|
||||
cooling_schedule.time_step = schedule.time_step
|
||||
cooling_schedule.time_range = schedule.time_range
|
||||
cooling_schedule.day_types = schedule.day_types
|
||||
cooling_schedules.append(cooling_schedule)
|
||||
thermal_control = ThermalControl()
|
||||
thermal_control.mean_heating_set_point = usage_entry.thermal_control.mean_heating_set_point
|
||||
thermal_control.heating_set_back = usage_entry.thermal_control.heating_set_back
|
||||
thermal_control.mean_cooling_set_point = usage_entry.thermal_control.mean_cooling_set_point
|
||||
thermal_control.hvac_availability_schedules = hvac_schedules
|
||||
thermal_control.heating_set_point_schedules = heating_schedules
|
||||
thermal_control.cooling_set_point_schedules = cooling_schedules
|
||||
usage_zone = UsageZone()
|
||||
usage_zone.usage = usage_entry.usage
|
||||
usage_zone.percentage = usage['percentage']
|
||||
usage_zone.hours_day = usage_entry.hours_day
|
||||
usage_zone.days_year = usage_entry.days_year
|
||||
usage_zone.mechanical_air_change = usage_entry.mechanical_air_change
|
||||
if usage_entry.mechanical_air_change is None:
|
||||
usage_zone.mechanical_air_change = ((usage_entry.ventilation_rate * internal_zone.area) /
|
||||
internal_zone.volume) * seconds_in_hour
|
||||
usage_zone.occupancy = occupancy
|
||||
usage_zone.lighting = lighting
|
||||
usage_zone.appliances = appliances
|
||||
usage_zone.thermal_control = thermal_control
|
||||
internal_zone.usage_zones.append(usage_zone)
|
||||
response = {'result': 'succeed'}
|
||||
except KeyError as ex:
|
||||
response = {'error': f'Mandatory parameter {ex} is missing'}
|
||||
return Response(json.dumps(response), headers=headers, status=400)
|
||||
except IndexError:
|
||||
response = {'error': f'Name "{usage_name}" unknown'}
|
||||
return Response(json.dumps(response), headers=headers, status=400)
|
||||
return Response(json.dumps(response), headers=headers)
|
155
hub_api/usage_catalog.py
Normal file
155
hub_api/usage_catalog.py
Normal file
@ -0,0 +1,155 @@
|
||||
"""
|
||||
Construction catalog
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Project Author name guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from flask import request, Response
|
||||
from flask_restful import Resource
|
||||
|
||||
from hub_api.helpers.session_helper import refresh_session
|
||||
|
||||
|
||||
class ToJson:
|
||||
@staticmethod
|
||||
def usage_to_json(usage):
|
||||
dictionary = {
|
||||
'usage': usage.usage,
|
||||
'hours_day': usage.hours_day,
|
||||
'days_year': usage.days_year,
|
||||
'mechanical_air_change': usage.mechanical_air_change if usage.mechanical_air_change is not None else '',
|
||||
'ventilation_rate': usage.ventilation_rate if usage.ventilation_rate is not None else '',
|
||||
'occupancy': ToJson.occupancy_to_json(usage.occupancy),
|
||||
'lighting': ToJson.lighting_to_json(usage.lighting),
|
||||
'appliances': ToJson.appliances_to_json(usage.appliances),
|
||||
'thermal_control': ToJson.thermal_control_to_json(usage.thermal_control)
|
||||
}
|
||||
return dictionary
|
||||
|
||||
@staticmethod
|
||||
def occupancy_to_json(occupancy):
|
||||
dictionary = {
|
||||
'occupancy_density': occupancy.occupancy_density,
|
||||
'sensible_convective_internal_gain': occupancy.sensible_convective_internal_gain,
|
||||
'sensible_radiative_internal_gain': occupancy.sensible_radiative_internal_gain,
|
||||
'latent_internal_gain': occupancy.latent_internal_gain,
|
||||
'schedules': []
|
||||
}
|
||||
for schedule in occupancy.schedules:
|
||||
dictionary['schedules'].append(ToJson.schedule_to_json(schedule))
|
||||
return dictionary
|
||||
|
||||
@staticmethod
|
||||
def lighting_to_json(lighting):
|
||||
dictionary = {
|
||||
'density': lighting.density,
|
||||
'convective_fraction': lighting.convective_fraction,
|
||||
'radiative_fraction': lighting.radiative_fraction,
|
||||
'latent_fraction': lighting.latent_fraction,
|
||||
'schedules': []
|
||||
}
|
||||
for schedule in lighting.schedules:
|
||||
dictionary['schedules'].append(ToJson.schedule_to_json(schedule))
|
||||
return dictionary
|
||||
|
||||
@staticmethod
|
||||
def appliances_to_json(appliances):
|
||||
dictionary = {
|
||||
'density': appliances.density,
|
||||
'convective_fraction': appliances.convective_fraction,
|
||||
'radiative_fraction': appliances.radiative_fraction,
|
||||
'latent_fraction': appliances.latent_fraction,
|
||||
'schedules': []
|
||||
}
|
||||
for schedule in appliances.schedules:
|
||||
dictionary['schedules'].append(ToJson.schedule_to_json(schedule))
|
||||
return dictionary
|
||||
|
||||
@staticmethod
|
||||
def thermal_control_to_json(thermal_control):
|
||||
dictionary = {
|
||||
'mean_heating_set_point': thermal_control.mean_heating_set_point,
|
||||
'heating_set_back': thermal_control.heating_set_back,
|
||||
'mean_cooling_set_point': thermal_control.mean_cooling_set_point,
|
||||
'hvac_availability_schedules': [],
|
||||
'heating_set_point_schedules': [],
|
||||
'cooling_set_point_schedules': [],
|
||||
}
|
||||
for schedule in thermal_control.hvac_availability_schedules:
|
||||
dictionary['hvac_availability_schedules'].append(ToJson.schedule_to_json(schedule))
|
||||
for schedule in thermal_control.heating_set_point_schedules:
|
||||
dictionary['heating_set_point_schedules'].append(ToJson.schedule_to_json(schedule))
|
||||
for schedule in thermal_control.cooling_set_point_schedules:
|
||||
dictionary['cooling_set_point_schedules'].append(ToJson.schedule_to_json(schedule))
|
||||
return dictionary
|
||||
|
||||
@staticmethod
|
||||
def schedule_to_json(schedule):
|
||||
schedule_dictionary = {
|
||||
'type': schedule.type,
|
||||
'data_type': schedule.data_type,
|
||||
'time_step': schedule.time_step,
|
||||
'time_range': schedule.time_range,
|
||||
'day_types': schedule.day_types,
|
||||
'values': schedule.values,
|
||||
}
|
||||
return schedule_dictionary
|
||||
|
||||
class UsageCatalogEntry(Resource):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def post():
|
||||
session = refresh_session(request)
|
||||
if session is None:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
headers = session.headers
|
||||
catalog = session.usage_catalog
|
||||
|
||||
name = None
|
||||
if request.data == b'' or request.json['name'] is None:
|
||||
response = {'error': 'Mandatory parameter "name" is missing'}
|
||||
return Response(json.dumps(response), headers=headers, status=400)
|
||||
try:
|
||||
name = request.json['name']
|
||||
entry = catalog.get_entry(name)
|
||||
output = {'usages': [ToJson.usage_to_json(entry)]}
|
||||
return Response(json.dumps(output), headers=headers)
|
||||
except IndexError:
|
||||
response = {'error': f'Name "{name}" unknown'}
|
||||
return Response(json.dumps(response), headers=headers, status=400)
|
||||
|
||||
|
||||
class UsageCatalogEntries(Resource):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def post():
|
||||
session = refresh_session(request)
|
||||
if session is None:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
headers = session.headers
|
||||
catalog = session.usage_catalog
|
||||
output = {'usages': []}
|
||||
content = catalog.entries()
|
||||
for usage in content.usages:
|
||||
output['usages'].append(ToJson.usage_to_json(usage))
|
||||
return Response(json.dumps(output), headers=headers)
|
||||
|
||||
|
||||
class UsageCatalogNames(Resource):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def post():
|
||||
session = refresh_session(request)
|
||||
if session is None:
|
||||
return Response(json.dumps({'error': 'invalid session'}), status=401)
|
||||
headers = session.headers
|
||||
catalog = session.usage_catalog
|
||||
return Response(json.dumps(catalog.names()), headers=headers)
|
23
requirements.txt
Normal file
23
requirements.txt
Normal file
@ -0,0 +1,23 @@
|
||||
flask
|
||||
flask_restful>0.3.8
|
||||
flask-apispec
|
||||
marshmallow
|
||||
apispec
|
||||
geomeppy
|
||||
xmltodict
|
||||
numpy
|
||||
trimesh
|
||||
pyproj
|
||||
pandas
|
||||
requests
|
||||
esoreader
|
||||
PyWavefront
|
||||
xlrd
|
||||
openpyxl
|
||||
networkx
|
||||
parseidf==1.0.0
|
||||
ply
|
||||
rhino3dm==7.7.0
|
||||
scipy
|
||||
PyYAML
|
||||
pyecore==0.12.2
|
125
tests/test_service.sh
Executable file
125
tests/test_service.sh
Executable file
@ -0,0 +1,125 @@
|
||||
#!/bin/bash
|
||||
clear
|
||||
printf 'Uptime:\n'
|
||||
curl -i -X GET http://127.0.0.1:15789/v1.3/uptime
|
||||
printf '\n\nSession start:\n'
|
||||
curl -i -X PUT -H "Session: {'session_id': 'debug'}" http://127.0.0.1:15789/v1.3/session/start
|
||||
|
||||
printf '\n\nRepeat session start:\n'
|
||||
curl -i -X PUT -H "Session: {'session_id': 'debug'}" http://127.0.0.1:15789/v1.3/session/start
|
||||
|
||||
printf '\n\nKeep session alive:\n'
|
||||
curl -i -X PUT -H "Session: {'session_id': 'debug', 'token': 'debug'}" http://127.0.0.1:15789/v1.3/session/keep_alive
|
||||
|
||||
printf '\n\nCity info:\n'
|
||||
curl -i -X GET -H "Session: {'session_id': 'debug', 'token': 'debug'}" http://127.0.0.1:15789/v1.3/city_info
|
||||
|
||||
printf '\n\nGreenery catalog entry without parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/greenery-catalog/entry
|
||||
|
||||
printf '\n\nGreenery catalog entry with wrong parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"name": "non_existing_name"}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/greenery-catalog/entry
|
||||
|
||||
printf '\n\nGreenery catalog entry with correct parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"name": "grass"}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/greenery-catalog/entry
|
||||
|
||||
printf '\n\nGreenery catalog names without parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/greenery-catalog/names
|
||||
|
||||
printf '\n\nGreenery catalog names with wrong parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"category": "non_existing_name"}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/greenery-catalog/names
|
||||
|
||||
printf '\n\nGreenery catalog names with correct parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"category": "soils"}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/greenery-catalog/names
|
||||
|
||||
printf '\n\nGreenery catalog entries without parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/greenery-catalog/entries
|
||||
|
||||
printf '\n\nGreenery catalog entries with wrong parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"category": "non_existing_category"}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/greenery-catalog/entries
|
||||
|
||||
printf '\n\nGreenery catalog entries with correct parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"category": "vegetations"}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/greenery-catalog/entries
|
||||
|
||||
printf '\n\nGeometry:\n'
|
||||
curl -i -X GET -H "Session: {'session_id': 'debug', 'token': 'debug'}" http://127.0.0.1:15789/v1.3/geometry
|
||||
|
||||
printf '\n\nHeat Pump:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"BuildingSuppTemp": 40, "EndYear": 2016, "FuelDensity": 0.717, "FuelEF": 1887, "FuelLHV": 47100, "FuelPrice": 0.3, "HPSupTemp": 60, "HeatPumpModel": "012", "HeatPumpType": "Air Source HP", "HoursOfStorageAtMaxDemand": 1, "MaximumHPEnergyInput": 8000, "SimType": 0, "StartYear": 2015, "TemperatureDifference": 15 }' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/heat-pump
|
||||
|
||||
printf '\n\Material LCA Catalog:\n'
|
||||
curl -i -X GET -H "Session: {'session_id': 'debug', 'token': 'debug'}" http://127.0.0.1:15789/v1.3/material_lca_catalog/entries
|
||||
|
||||
printf '\n\Material LCA Calculations:\n'
|
||||
curl -i -X GET -H "Session: {'session_id': 'debug', 'token': 'debug'}" http://127.0.0.1:15789/v1.3/material_lca_catalog/calculations
|
||||
|
||||
printf '\n\nConstruction catalog entry without parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/construction-catalog/entry
|
||||
|
||||
printf '\n\nConstruction catalog entry with wrong parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"name": "non_existing_name"}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/construction-catalog/entry
|
||||
|
||||
printf '\n\nConstruction catalog entry with correct parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"name": "189.1-2009 Nonres 4B Ext Wall Mass"}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/construction-catalog/entry
|
||||
|
||||
printf '\n\nConstruction catalog names without parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/construction-catalog/names
|
||||
|
||||
printf '\n\nConstruction catalog names with wrong parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"category": "non_existing_name"}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/construction-catalog/names
|
||||
|
||||
printf '\n\nConstruction catalog names with correct parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"category": "materials"}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/construction-catalog/names
|
||||
|
||||
printf '\n\nConstruction catalog entries without parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/construction-catalog/entries
|
||||
|
||||
printf '\n\nConstruction catalog entries with wrong parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"category": "non_existing_category"}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/construction-catalog/entries
|
||||
|
||||
printf '\n\nConstruction catalog entries with correct parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"category": "archetypes"}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/construction-catalog/entries
|
||||
|
||||
printf '\n\nSet Construction without building names:\n'
|
||||
curl -i -X PUT -H "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"extra_loses_due_to_thermal_bridges":"0.15","indirect_heated_ratio":"0.15","infiltration_rate_for_ventilation_system_off":"0.5","infiltration_rate_for_ventilation_system_on":"0","constructions":[{"type":"Wall","layers":[{"thickness":"0.0253","material":{"solar_absorptance":"0.92","thermal_absorptance":"0.9","visible_absorptance":"0.92","no_mass":"False","thermal_resistance":"","conductivity":"0.6918","density":"1858","specific_heat":"837"}},{"thickness":"0.2033","material":{"solar_absorptance":"0.65","thermal_absorptance":"0.9","visible_absorptance":"0.65","no_mass":"False","thermal_resistance":"","conductivity":"1.7296","density":"2243","specific_heat":"837"}},{"thickness":"0.0338606","material":{"solar_absorptance":"0.5","thermal_absorptance":"0.9","visible_absorptance":"0.5","no_mass":"False","thermal_resistance":"","conductivity":"0.0432","density":"91","specific_heat":"837"}},{"thickness":"0.01271","material":{"solar_absorptance":"0.92","thermal_absorptance":"0.9","visible_absorptance":"0.92","no_mass":"False","thermal_resistance":"","conductivity":"0.16","density":"784.9","specific_heat":"830"}}],"window":{"frame_ratio":"0","g_value":"0.36","overall_u_value":"4.491833333333333"}},{"type":"Ground","layers":[{"thickness":"0.1016","material":{"solar_absorptance":"0.7","thermal_absorptance":"0.9","visible_absorptance":"0.7","no_mass":"False","thermal_resistance":"","conductivity":"1.311","density":"2240","specific_heat":"836.8"}},{"thickness":0,"material":{"solar_absorptance":"0.7","thermal_absorptance":"0.9","visible_absorptance":"0.8","no_mass":"True","thermal_resistance":"0.21648","conductivity":"","density":"","specific_heat":""}}]},{"type":"Roof","layers":[{"thickness":"0.0178","material":{"solar_absorptance":"0.78","thermal_absorptance":"0.9","visible_absorptance":"0.78","no_mass":"False","thermal_resistance":"","conductivity":"0.115","density":"513","specific_heat":"1255"}},{"thickness":"0.0254","material":{"solar_absorptance":"0.78","thermal_absorptance":"0.9","visible_absorptance":"0.78","no_mass":"False","thermal_resistance":"","conductivity":"0.1211","density":"593","specific_heat":"2510"}},{"thickness":"0.221604","material":{"solar_absorptance":"0.7","thermal_absorptance":"0.9","visible_absorptance":"0.7","no_mass":"False","thermal_resistance":"","conductivity":"0.049","density":"265","specific_heat":"836.8"}},{"thickness":"0.01271","material":{"solar_absorptance":"0.92","thermal_absorptance":"0.9","visible_absorptance":"0.92","no_mass":"False","thermal_resistance":"","conductivity":"0.16","density":"784.9","specific_heat":"830"}}],"window":{"frame_ratio":"0","g_value":"0.36","overall_u_value":"4.491833333333333"}}]}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/construction
|
||||
|
||||
printf '\n\nSet Construction:\n'
|
||||
curl -i -X PUT -H "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"building_names":["Dompark"], "extra_loses_due_to_thermal_bridges":"0.15","indirect_heated_ratio":"0.15","infiltration_rate_for_ventilation_system_off":"0.5","infiltration_rate_for_ventilation_system_on":"0","constructions": [{"name": "189.1-2009 Res 4B Ext Wall Steel-Framed", "type": "Wall", "layers": [{"name": "Layer 1", "thickness": 0, "material": {"id": "12", "name": "MAT-SHEATH", "solar_absorptance": "0.7", "thermal_absorptance": "0.9", "visible_absorptance": "0.7", "no_mass": "True", "thermal_resistance": "0.36256", "conductivity": "", "density": "", "specific_heat": ""}}, {"name": "Layer 2", "thickness": "0.118387", "material": {"id": "3", "name": "Floor Insulation [4]", "solar_absorptance": "0.7", "thermal_absorptance": "0.9", "visible_absorptance": "0.7", "no_mass": "False", "thermal_resistance": "", "conductivity": "0.045", "density": "265", "specific_heat": "836.8"}}, {"name": "Layer 3", "thickness": "0.01271", "material": {"id": "7", "name": "1/2IN Gypsum", "solar_absorptance": "0.92", "thermal_absorptance": "0.9", "visible_absorptance": "0.92", "no_mass": "False", "thermal_resistance": "", "conductivity": "0.16", "density": "784.9", "specific_heat": "830"}}], "window_ratio": "0.3", "window": {"name": "189.1-2009 Nonres 4B Window Nonmetal framing", "frame_ratio": "0", "g_value": "0.35", "overall_u_value": "2.379966666666667"}}, {"name": "189.1-2009 Nonres 4B Ext Slab Unheated - 4in Slab with Carpet", "type": "Ground", "layers": [{"name": "Layer 1", "thickness": "0.1016", "material": {"id": "1", "name": "MAT-CC05 8 HW CONCRETE", "solar_absorptance": "0.7", "thermal_absorptance": "0.9", "visible_absorptance": "0.7", "no_mass": "False", "thermal_resistance": "", "conductivity": "1.311", "density": "2240", "specific_heat": "836.8"}}, {"name": "Layer 2", "thickness": 0, "material": {"id": "16", "name": "CP02 CARPET PAD", "solar_absorptance": "0.7", "thermal_absorptance": "0.9", "visible_absorptance": "0.8", "no_mass": "True", "thermal_resistance": "0.21648", "conductivity": "", "density": "", "specific_heat": ""}}]}, {"name": "189.1-2009 Nonres 4B Roof IEAD", "type": "Roof", "layers": [{"name": "Layer 1", "thickness": "0.0095", "material": {"id": "15", "name": "Roof Membrane", "solar_absorptance": "0.7", "thermal_absorptance": "0.9", "visible_absorptance": "0.7", "no_mass": "False", "thermal_resistance": "", "conductivity": "0.16", "density": "1121.29", "specific_heat": "1460"}}, {"name": "Layer 2", "thickness": "0.210538", "material": {"id": "11", "name": "Roof Insulation [23]", "solar_absorptance": "0.7", "thermal_absorptance": "0.9", "visible_absorptance": "0.7", "no_mass": "False", "thermal_resistance": "", "conductivity": "0.049", "density": "265", "specific_heat": "836.8"}}, {"name": "Layer 3", "thickness": "0.001524", "material": {"id": "14", "name": "Metal Decking", "solar_absorptance": "0.7", "thermal_absorptance": "0.9", "visible_absorptance": "0.3", "no_mass": "False", "thermal_resistance": "", "conductivity": "45.006", "density": "7680", "specific_heat": "418.4"}}]}]}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/construction
|
||||
|
||||
printf '\n\nEnergy Demand:\n'
|
||||
curl -i -X GET -H "Session: {'session_id': 'debug', 'token': 'debug'}" -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/energy-demand
|
||||
|
||||
printf '\n\nUsage catalog entry without parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/usage-catalog/entry
|
||||
|
||||
printf '\n\nUsage catalog entry with wrong parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"name": "non_existing_name"}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/usage-catalog/entry
|
||||
|
||||
printf '\n\nUsage catalog entry with correct parameter:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"name": "BA Automotive Facility"}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/usage-catalog/entry
|
||||
|
||||
printf '\n\nUsage catalog names:\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/usage-catalog/names
|
||||
|
||||
printf '\n\nUsage catalog entries :\n'
|
||||
curl -i -X POST -H "Session: {'session_id': 'debug', 'token': 'debug'}" -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/usage-catalog/entries
|
||||
|
||||
printf '\n\nSet Greenery with missing parameters:\n'
|
||||
curl -i -X PUT --header "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"vegetation": {"vegetation_name": "Grass", "soil_thickness": "0.2", "air_gap": "0.0", "soil_name": "Loamy sand", "soil_roughness": "MediumRough", "dry_soil_conductivity": "0.35", "dry_soil_density": "1100", "dry_soil_specific_heat": "1200", "soil_thermal_absorptance": "0.9", "soil_solar_absorptance": "0.7", "soil_visible_absorptance": "0.75", "soil_saturation_volumetric_moisture_content": "0.41", "soil_residual_volumetric_moisture_content": "0.057", "plant_percentages": [{"plant_percentage": "100", "plant_name": "Lawn", "plant_height": "0.06", "plant_leaf_area_index": "1.4", "plant_leaf_reflectivity": "0.1", "plant_leaf_emissivity": "0.9", "plant_minimal_stomatal_resistance": "50", "plant_co2_sequestration": "0", "plant_grows_on": [{"soil_name": "Loam", "soil_roughness": "MediumRough", "soil_dry_conductivity": "0.67", "soil_dry_density": "1100", "soil_dry_specific_heat": "900", "soil_thermal_absortance": "0.9", "soil_solar_absortance": "0.7", "soil_visible_absortance": "0.75", "soil_saturation_volumetric_moisture_content": "0.43", "soil_residual_volumetric_moisture_content": "0.078", "soil_initial_volumetric_moisture_content": "0.1"}]}]}}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/greenery
|
||||
|
||||
printf '\n\nSet Greenery:\n'
|
||||
curl -i -X PUT --header "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"building_names":["Dompark"], "greenery_percentage":"30", "vegetation": {"vegetation_name": "Grass", "soil_thickness": "0.2", "air_gap": "0.0", "soil_name": "Loamy sand", "soil_roughness": "MediumRough", "dry_soil_conductivity": "0.35", "dry_soil_density": "1100", "dry_soil_specific_heat": "1200", "soil_thermal_absorptance": "0.9", "soil_solar_absorptance": "0.7", "soil_visible_absorptance": "0.75", "soil_saturation_volumetric_moisture_content": "0.41", "soil_residual_volumetric_moisture_content": "0.057", "plant_percentages": [{"plant_percentage": "100", "plant_name": "Lawn", "plant_height": "0.06", "plant_leaf_area_index": "1.4", "plant_leaf_reflectivity": "0.1", "plant_leaf_emissivity": "0.9", "plant_minimal_stomatal_resistance": "50", "plant_co2_sequestration": "0", "plant_grows_on": [{"soil_name": "Loam", "soil_roughness": "MediumRough", "soil_dry_conductivity": "0.67", "soil_dry_density": "1100", "soil_dry_specific_heat": "900", "soil_thermal_absortance": "0.9", "soil_solar_absortance": "0.7", "soil_visible_absortance": "0.75", "soil_saturation_volumetric_moisture_content": "0.43", "soil_residual_volumetric_moisture_content": "0.078", "soil_initial_volumetric_moisture_content": "0.1"}]}]}}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/greenery
|
||||
|
||||
printf '\n\nSet Usages with missing parameters:\n'
|
||||
curl -i -X PUT --header "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"building_names":["Dompark"],"usages":[{"name": "BA Convention Center"}, {"name": "BA Multifamily", "percentage": "0.2"}, {"name": "BA Office", "percentage": "0.75"}]}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/usage
|
||||
|
||||
printf '\n\nSet Usages:\n'
|
||||
curl -i -X PUT --header "Session: {'session_id': 'debug', 'token': 'debug'}" -d '{"building_names":["Dompark"],"usages":[{"name": "BA Convention Center", "percentage": "0.05"}, {"name": "BA Multifamily", "percentage": "0.2"}, {"name": "BA Office", "percentage": "0.75"}]}' -H "Content-Type: application/json" http://127.0.0.1:15789/v1.3/usage
|
||||
|
||||
printf '\n\nSession end:\n'
|
||||
curl -i -X PUT -H "Session: {'session_id': 'debug', 'token': 'debug'}" http://127.0.0.1:15789/v1.3/session/end
|
||||
|
||||
printf '\n\nRepeat session end:\n'
|
||||
curl -i -X PUT -H "Session: {'session_id': 'debug', 'token': 'debug'}" http://127.0.0.1:15789/v1.3/session/end
|
||||
|
||||
printf '\n'
|
2
utils/__init__.py
Normal file
2
utils/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from .misc import validate_hp_model
|
||||
from .hp_simulator import HeatPumpSimulator
|
41
utils/hp_simulator.py
Normal file
41
utils/hp_simulator.py
Normal file
@ -0,0 +1,41 @@
|
||||
"""
|
||||
HeatPumpSimulator: Module for running simulations
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Project Author Peter Yefi peteryefi@gmail.com
|
||||
"""
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
||||
from exports.energy_systems_factory import EnergySystemsExportFactory
|
||||
from imports.energy_systems_factory import EnergySystemsFactory
|
||||
|
||||
|
||||
class HeatPumpSimulator:
|
||||
|
||||
def __init__(self, city, user_input: Dict):
|
||||
"""
|
||||
:param user_input: the user parameters for running simulation
|
||||
"""
|
||||
|
||||
self._user_input = user_input
|
||||
self._hp_type = user_input['HeatPumpType'].replace(" ", "_").lower()
|
||||
# file to have results after simulation is run
|
||||
self._output_path = Path(Path(__file__).parent.parent / "data/dompark_{}.csv".format(self._hp_type))
|
||||
self._city = city
|
||||
EnergySystemsFactory(user_input['HeatPumpType'].lower(), self._city).enrich()
|
||||
|
||||
def run_hp_simulation(self) -> str:
|
||||
"""
|
||||
Runs heat pump simulation and dumps output
|
||||
in self._output_path
|
||||
:return: None
|
||||
"""
|
||||
hp_type = 'water' if 'water' in self._hp_type else 'air'
|
||||
del self._user_input['HeatPumpType']
|
||||
model = self._user_input.pop('HeatPumpModel')
|
||||
EnergySystemsExportFactory(self._city,
|
||||
self._user_input,
|
||||
model,
|
||||
self._output_path,
|
||||
self._user_input['SimType']).export(hp_type)
|
||||
return str(self._output_path)
|
28
utils/misc.py
Normal file
28
utils/misc.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""
|
||||
Miscellaneous
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Project Author Peter Yefi peteryefi@gmail.com
|
||||
"""
|
||||
hp_models = {
|
||||
'air_source': ['012', '015', '018', '023', '030', '033', '037', '044', '047', '057', '070', '087', '097', '102',
|
||||
'120', '130', '140'],
|
||||
'water_to_water': ['ClimateMaster 156 kW', 'ClimateMaster 256 kW', 'ClimateMaster 335 kW']
|
||||
}
|
||||
|
||||
|
||||
def validate_hp_model(hp_type: str, model: str) -> bool:
|
||||
"""
|
||||
Validates the HP model parameter provided by users
|
||||
for running insel simulation
|
||||
:param hp_type: the type of heat pump: air source or
|
||||
water to water
|
||||
:param model: the model of heat pump
|
||||
:return: bool
|
||||
"""
|
||||
if 'air' in hp_type.lower():
|
||||
if model in hp_models['air_source']:
|
||||
return True
|
||||
elif 'water' in hp_type.lower():
|
||||
if model in hp_models['water_to_water']:
|
||||
return True
|
||||
return False
|
Loading…
Reference in New Issue
Block a user