Clean up and define session start message
This commit is contained in:
parent
5f6b7339c4
commit
f39ffedc87
@ -14,11 +14,12 @@ from flask import Response
|
|||||||
from flask_restful import Api
|
from flask_restful import Api
|
||||||
|
|
||||||
import hub_api.helpers.session_helper as sh
|
import hub_api.helpers.session_helper as sh
|
||||||
from hub_api.construction_catalog import ConstructionCatalogEntry, ConstructionCatalogEntries, ConstructionCatalogNames
|
from hub_api.catalogs.construction_catalog import ConstructionCatalogEntry, ConstructionCatalogEntries, ConstructionCatalogNames
|
||||||
from hub_api.greenery_catalog import GreeneryCatalogEntry, GreeneryCatalogEntries, GreeneryCatalogNames
|
from hub_api.catalogs.greenery_catalog import GreeneryCatalogEntry, GreeneryCatalogEntries, GreeneryCatalogNames
|
||||||
|
from hub_api.catalogs.usage_catalog import UsageCatalogEntry, UsageCatalogEntries, UsageCatalogNames
|
||||||
from hub_api.session import SessionStart, SessionEnd, KeepSessionAlive
|
from hub_api.session import SessionStart, SessionEnd, KeepSessionAlive
|
||||||
from hub_api.uptime import Uptime
|
from hub_api.uptime import Uptime
|
||||||
from hub_api.usage_catalog import UsageCatalogEntry, UsageCatalogEntries, UsageCatalogNames
|
|
||||||
|
|
||||||
sh.begin_time = datetime.datetime.now()
|
sh.begin_time = datetime.datetime.now()
|
||||||
app = flask.Flask('cerc_api')
|
app = flask.Flask('cerc_api')
|
||||||
|
1
cache/.gitignore
vendored
1
cache/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
!.gitignore
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
8764
data/Montreal.cli
8764
data/Montreal.cli
File diff suppressed because it is too large
Load Diff
8764
data/None.cli
8764
data/None.cli
File diff suppressed because it is too large
Load Diff
BIN
data/dompark.3dm
BIN
data/dompark.3dm
Binary file not shown.
15600
data/dompark.idf
15600
data/dompark.idf
File diff suppressed because it is too large
Load Diff
@ -1,14 +0,0 @@
|
|||||||
,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
103532
data/energy+.idd
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
156
hub_api/catalogs/usage_catalog.py
Normal file
156
hub_api/catalogs/usage_catalog.py
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
"""
|
||||||
|
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)
|
@ -24,13 +24,69 @@ paths:
|
|||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/Uptime'
|
$ref: '#/components/schemas/uptime'
|
||||||
|
/v1.4/session/start:
|
||||||
|
put:
|
||||||
|
parameters:
|
||||||
|
- in: header
|
||||||
|
name: username
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: the application username accessing this API
|
||||||
|
- in: header
|
||||||
|
name: password
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: the password for the user accessing this API
|
||||||
|
- in: header
|
||||||
|
name: application_id
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: the Id of the application accessing this API
|
||||||
|
tags:
|
||||||
|
- Session start
|
||||||
|
summary: Starts an user session
|
||||||
|
operationId: session_start
|
||||||
|
description: Authenticate and initialize an user session in the api
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Authorized
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/authorized'
|
||||||
|
'403':
|
||||||
|
description: Unauthorized
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/unauthorized'
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
Uptime:
|
uptime:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
uptime:
|
uptime:
|
||||||
type: string
|
type: string
|
||||||
format: hh:mm:ss.ms
|
format: hh:mm:ss.ms
|
||||||
example: "00:09:53.600281"
|
example: "00:09:53.600281"
|
||||||
|
authorized:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
session_id:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
example: "ad0720ed-0f31-4f3e-9686-1177d4624ec1"
|
||||||
|
token:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
example: "660d1aa0-d24f-4cb1-902d-13c7bd29793c"
|
||||||
|
unauthorized:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
|
example: "unauthorized"
|
@ -1,396 +0,0 @@
|
|||||||
import json
|
|
||||||
from flask import request, Response
|
|
||||||
from flask_restful import Resource
|
|
||||||
from pathlib import Path
|
|
||||||
from geomeppy import IDF
|
|
||||||
import os
|
|
||||||
import glob
|
|
||||||
import hub_api.helpers.session_helper as sh
|
|
||||||
import hub.helpers.constants as cte
|
|
||||||
import csv
|
|
||||||
from hub_api.helpers.auth import role_required
|
|
||||||
from hub.persistence.models import UserRoles
|
|
||||||
from hub_api.config import Config
|
|
||||||
|
|
||||||
|
|
||||||
class EnergyDemand(Resource, Config):
|
|
||||||
_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.
|
|
||||||
super().__init__()
|
|
||||||
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
|
|
||||||
|
|
||||||
@role_required([UserRoles.Admin.value, UserRoles.Hub_Reader.value])
|
|
||||||
def get(self, city_id):
|
|
||||||
payload = request.get_json()
|
|
||||||
self._city = self.get_city(city_id)
|
|
||||||
|
|
||||||
self._greenery_percentage = round(float(payload['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), status=200)
|
|
@ -1,76 +0,0 @@
|
|||||||
"""
|
|
||||||
HeatPump Service
|
|
||||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
|
||||||
Copyright © 2023 Project Author Peter Yefi peteryefi@gmail.com
|
|
||||||
"""
|
|
||||||
import json
|
|
||||||
from flask import Response, request
|
|
||||||
from flask_restful import Resource
|
|
||||||
from hub.hub_logger import logger
|
|
||||||
from hub_api.helpers.auth import generate_auth_token, role_required
|
|
||||||
from hub.persistence.models import UserRoles
|
|
||||||
from hub_api.config import Config
|
|
||||||
|
|
||||||
headers = {'Content-Type': 'application/json'}
|
|
||||||
|
|
||||||
|
|
||||||
class User(Resource, Config):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
@role_required([UserRoles.Admin.value])
|
|
||||||
def post(self):
|
|
||||||
try:
|
|
||||||
payload = request.get_json()
|
|
||||||
user = self.user_factory.create_user(name=payload["name"], email=payload["email"], password=payload["password"],
|
|
||||||
role=payload["role"])
|
|
||||||
if type(user) is dict:
|
|
||||||
return Response(response=json.dumps(user), status=400, headers=headers)
|
|
||||||
return Response(response=json.dumps({'user': {'id': user.id, 'name': user.name, 'email': user.email,
|
|
||||||
'password': user.password, 'role': user.role.value}}), status=201,
|
|
||||||
headers=headers)
|
|
||||||
except Exception as err:
|
|
||||||
logger.error(err)
|
|
||||||
return Response(response=json.dumps({'err_msg': 'Sorry an error occurred while creating user'}), status=400,
|
|
||||||
headers=headers)
|
|
||||||
|
|
||||||
@role_required([UserRoles.Admin.value])
|
|
||||||
def put(self):
|
|
||||||
try:
|
|
||||||
payload = request.get_json()
|
|
||||||
res = self.user_factory.update_user(user_id=payload['id'], name=payload['name'], password=payload['password'],
|
|
||||||
role=payload['role'], email=payload['email'])
|
|
||||||
if res:
|
|
||||||
return Response(response=json.dumps(res), status=400, headers=headers)
|
|
||||||
return Response(response=json.dumps({'success': 'user updated successfully'}), status=200, headers=headers)
|
|
||||||
except Exception as err:
|
|
||||||
logger.error(err)
|
|
||||||
return Response(response=json.dumps({'err_msg': 'Sorry, an error occurred while updating user'}),
|
|
||||||
status=400, headers=headers)
|
|
||||||
|
|
||||||
|
|
||||||
class UserLogin(Resource, Config):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
def post(self):
|
|
||||||
try:
|
|
||||||
payload = request.get_json()
|
|
||||||
user = self.ex_user_factory.login_user(email=payload["email"], password=payload["password"])
|
|
||||||
if type(user) is dict:
|
|
||||||
return Response(response=json.dumps(user), status=400, headers=headers)
|
|
||||||
user = user[0]
|
|
||||||
user_dict = {
|
|
||||||
'user': {
|
|
||||||
'id': user.id,
|
|
||||||
'name': user.name,
|
|
||||||
'email': user.email,
|
|
||||||
'password': user.password,
|
|
||||||
'role': user.role.value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
user_dict['token'] = generate_auth_token(user_dict)
|
|
||||||
return Response(response=json.dumps(user_dict), status=200, headers=headers)
|
|
||||||
except Exception as err:
|
|
||||||
logger.error(err)
|
|
||||||
return Response(response=json.dumps({'err_msg': 'An error occurred while authenticating user'}), status=400)
|
|
@ -25,3 +25,5 @@ jwt==1.3.1
|
|||||||
flagger==3.1.0
|
flagger==3.1.0
|
||||||
flasgger
|
flasgger
|
||||||
cerc-hub
|
cerc-hub
|
||||||
|
python-dotenv
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user