Merge remote-tracking branch 'origin/main' into final_energy_system_model
# Conflicts: # .gitignore # hub/city_model_structure/building.py # hub/exports/building_energy/idf.py # hub/imports/geometry_factory.py
This commit is contained in:
commit
674393970c
13
.gitignore
vendored
13
.gitignore
vendored
@ -1 +1,12 @@
|
||||
.idea
|
||||
!.gitignore
|
||||
**/venv/
|
||||
.idea/
|
||||
/development_tests/
|
||||
/data/energy_systems/heat_pumps/*.csv
|
||||
/data/energy_systems/heat_pumps/*.insel
|
||||
.DS_Store
|
||||
**/.env
|
||||
**/hub/logs/
|
||||
**/__pycache__/
|
||||
**/.idea/
|
||||
cerc_hub.egg-info
|
||||
|
3
cerc_hub.egg-info/.gitignore
vendored
3
cerc_hub.egg-info/.gitignore
vendored
@ -1,3 +0,0 @@
|
||||
# Except this file
|
||||
*
|
||||
!.gitignore
|
@ -188,7 +188,7 @@ class EilatCatalog(Catalog):
|
||||
schedules_key = {}
|
||||
for j in range(0, number_usage_types):
|
||||
usage_parameters = _extracted_data.iloc[j]
|
||||
usage_type = usage_parameters[0]
|
||||
usage_type = usage_parameters.iloc[0]
|
||||
lighting_data[usage_type] = usage_parameters[1:6].values.tolist()
|
||||
plug_loads_data[usage_type] = usage_parameters[8:13].values.tolist()
|
||||
occupancy_data[usage_type] = usage_parameters[17:20].values.tolist()
|
||||
|
@ -8,6 +8,8 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
|
||||
|
||||
import json
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
import xmltodict
|
||||
|
||||
import hub.helpers.constants as cte
|
||||
@ -28,12 +30,11 @@ class NrcanCatalog(Catalog):
|
||||
Nrcan catalog class
|
||||
"""
|
||||
def __init__(self, path):
|
||||
path = str(path / 'nrcan.xml')
|
||||
self._schedules_path = Path(path / 'nrcan_schedules.json').resolve()
|
||||
self._space_types_path = Path(path / 'nrcan_space_types.json').resolve()
|
||||
self._space_compliance_path = Path(path / 'nrcan_space_compliance_2015.json').resolve()
|
||||
self._content = None
|
||||
self._schedules = {}
|
||||
with open(path, 'r', encoding='utf-8') as xml:
|
||||
self._metadata = xmltodict.parse(xml.read())
|
||||
self._base_url = self._metadata['nrcan']['@base_url']
|
||||
self._load_schedules()
|
||||
self._content = Content(self._load_archetypes())
|
||||
|
||||
@ -55,11 +56,9 @@ class NrcanCatalog(Catalog):
|
||||
return Schedule(hub_type, raw['values'], data_type, time_step, time_range, day_types)
|
||||
|
||||
def _load_schedules(self):
|
||||
usage = self._metadata['nrcan']
|
||||
url = f'{self._base_url}{usage["schedules"]}'
|
||||
_schedule_types = []
|
||||
with urllib.request.urlopen(url) as json_file:
|
||||
schedules_type = json.load(json_file)
|
||||
with open(self._schedules_path, 'r') as f:
|
||||
schedules_type = json.load(f)
|
||||
for schedule_type in schedules_type['tables']['schedules']['table']:
|
||||
schedule = NrcanCatalog._extract_schedule(schedule_type)
|
||||
if schedule_type['name'] not in _schedule_types:
|
||||
@ -80,14 +79,11 @@ class NrcanCatalog(Catalog):
|
||||
|
||||
def _load_archetypes(self):
|
||||
usages = []
|
||||
name = self._metadata['nrcan']
|
||||
url_1 = f'{self._base_url}{name["space_types"]}'
|
||||
url_2 = f'{self._base_url}{name["space_types_compliance"]}'
|
||||
with urllib.request.urlopen(url_1) as json_file:
|
||||
space_types = json.load(json_file)['tables']['space_types']['table']
|
||||
with open(self._space_types_path, 'r') as f:
|
||||
space_types = json.load(f)['tables']['space_types']['table']
|
||||
space_types = [st for st in space_types if st['space_type'] == 'WholeBuilding']
|
||||
with urllib.request.urlopen(url_2) as json_file:
|
||||
space_types_compliance = json.load(json_file)['tables']['space_compliance']['table']
|
||||
with open(self._space_compliance_path, 'r') as f:
|
||||
space_types_compliance = json.load(f)['tables']['space_compliance']['table']
|
||||
space_types_compliance = [st for st in space_types_compliance if st['space_type'] == 'WholeBuilding']
|
||||
space_types_dictionary = {}
|
||||
for space_type in space_types_compliance:
|
||||
|
@ -747,8 +747,13 @@ class Building(CityObject):
|
||||
|
||||
for key, item in self._distribution_systems_electrical_consumption.items():
|
||||
for i in range(0, len(item)):
|
||||
self._distribution_systems_electrical_consumption[key][i] += _peak_load * _consumption_fix_flow \
|
||||
* _working_hours[key] * cte.WATTS_HOUR_TO_JULES
|
||||
_working_hours_value = _working_hours[key]
|
||||
if len(item) == 12:
|
||||
_working_hours_value = _working_hours[key][i]
|
||||
self._distribution_systems_electrical_consumption[key][i] += (
|
||||
_peak_load * _consumption_fix_flow * _working_hours_value * cte.WATTS_HOUR_TO_JULES
|
||||
)
|
||||
|
||||
return self._distribution_systems_electrical_consumption
|
||||
|
||||
def _calculate_consumption(self, consumption_type, demand):
|
||||
@ -836,3 +841,17 @@ class Building(CityObject):
|
||||
"""
|
||||
self._heating_consumption_disaggregated = value
|
||||
|
||||
|
||||
@property
|
||||
def lower_corner(self):
|
||||
"""
|
||||
Get building lower corner.
|
||||
"""
|
||||
return [self._min_x, self._min_y, self._min_z]
|
||||
|
||||
@property
|
||||
def upper_corner(self):
|
||||
"""
|
||||
Get building upper corner.
|
||||
"""
|
||||
return [self._max_x, self._max_y, self._max_z]
|
||||
|
@ -16,7 +16,7 @@ class Layer:
|
||||
def __init__(self):
|
||||
self._thickness = None
|
||||
self._id = None
|
||||
self._name = None
|
||||
self._material_name = None
|
||||
self._conductivity = None
|
||||
self._specific_heat = None
|
||||
self._density = None
|
||||
@ -54,22 +54,20 @@ class Layer:
|
||||
self._thickness = float(value)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
def material_name(self):
|
||||
"""
|
||||
Get material name
|
||||
:return: str
|
||||
"""
|
||||
# todo: this should be named material_name instead
|
||||
return self._name
|
||||
return self._material_name
|
||||
|
||||
@name.setter
|
||||
def name(self, value):
|
||||
@material_name.setter
|
||||
def material_name(self, value):
|
||||
"""
|
||||
Set material name
|
||||
:param value: string
|
||||
"""
|
||||
# todo: this should be named material_name instead
|
||||
self._name = str(value)
|
||||
self._material_name = str(value)
|
||||
|
||||
@property
|
||||
def conductivity(self) -> Union[None, float]:
|
||||
|
@ -18,6 +18,7 @@ from hub.city_model_structure.attributes.point import Point
|
||||
from hub.city_model_structure.greenery.vegetation import Vegetation
|
||||
from hub.city_model_structure.building_demand.thermal_boundary import ThermalBoundary
|
||||
import hub.helpers.constants as cte
|
||||
from hub.helpers.configuration_helper import ConfigurationHelper
|
||||
|
||||
|
||||
class Surface:
|
||||
|
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<nrcan base_url="https://raw.githubusercontent.com/NREL/openstudio-standards/master/lib/openstudio-standards/standards/necb/">
|
||||
<space_types>NECB2015/data/space_types.json</space_types>
|
||||
<space_types_compliance>NECB2015/qaqc/qaqc_data/space_compliance_2015.json</space_types_compliance>>
|
||||
<schedules>NECB2015/data/schedules.json</schedules>
|
||||
</nrcan>
|
8685
hub/data/usage/nrcan_schedules.json
Normal file
8685
hub/data/usage/nrcan_schedules.json
Normal file
File diff suppressed because it is too large
Load Diff
3211
hub/data/usage/nrcan_space_compliance_2015.json
Normal file
3211
hub/data/usage/nrcan_space_compliance_2015.json
Normal file
File diff suppressed because it is too large
Load Diff
26170
hub/data/usage/nrcan_space_types.json
Normal file
26170
hub/data/usage/nrcan_space_types.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -12,6 +12,7 @@ from geomeppy import IDF
|
||||
import hub.helpers.constants as cte
|
||||
from hub.city_model_structure.attributes.schedule import Schedule
|
||||
from hub.city_model_structure.building_demand.thermal_zone import ThermalZone
|
||||
from hub.helpers.configuration_helper import ConfigurationHelper
|
||||
|
||||
|
||||
class Idf:
|
||||
@ -53,9 +54,16 @@ class Idf:
|
||||
_SIZING_PERIODS = 'SIZINGPERIOD:DESIGNDAY'
|
||||
_LOCATION = 'SITE:LOCATION'
|
||||
_SIMPLE = 'Simple'
|
||||
_EQUIPMENT_CONNECTIONS = 'ZONEHVAC:EQUIPMENTCONNECTIONS'
|
||||
_NODE_LIST = 'NODELIST'
|
||||
_BASEBOARD = 'ZONEHVAC:BASEBOARD:CONVECTIVE:ELECTRIC'
|
||||
_AIR_TERMINAL_NO_REHEAT = 'AIRTERMINAL:SINGLEDUCT:CONSTANTVOLUME:NOREHEAT'
|
||||
_AIR_DISTRIBUTION = 'ZONEHVAC:AIRDISTRIBUTIONUNIT'
|
||||
_EQUIPMENT_LIST = 'ZONEHVAC:EQUIPMENTLIST'
|
||||
_SIZING_ZONE = 'SIZING:ZONE'
|
||||
_DESIGN_SPECIFICATION_OUTDOOR_AIR = 'DESIGNSPECIFICATION:OUTDOORAIR'
|
||||
|
||||
idf_surfaces = {
|
||||
# todo: make an enum for all the surface types
|
||||
cte.WALL: 'wall',
|
||||
cte.GROUND: 'floor',
|
||||
cte.ROOF: 'roof'
|
||||
@ -117,7 +125,8 @@ class Idf:
|
||||
if levels_of_detail.construction is None:
|
||||
raise AttributeError('Level of detail of construction not assigned')
|
||||
if levels_of_detail.construction < 2:
|
||||
raise AttributeError(f'Level of detail of construction = {levels_of_detail.construction}. Required minimum level 2')
|
||||
raise AttributeError(
|
||||
f'Level of detail of construction = {levels_of_detail.construction}. Required minimum level 2')
|
||||
if levels_of_detail.usage is None:
|
||||
raise AttributeError('Level of detail of usage not assigned')
|
||||
if levels_of_detail.usage < 2:
|
||||
@ -148,20 +157,20 @@ class Idf:
|
||||
|
||||
def _add_material(self, layer):
|
||||
for material in self._idf.idfobjects[self._MATERIAL]:
|
||||
if material.Name == layer.name:
|
||||
if material.Name == layer.material_name:
|
||||
return
|
||||
for material in self._idf.idfobjects[self._MATERIAL_NOMASS]:
|
||||
if material.Name == layer.name:
|
||||
if material.Name == layer.material_name:
|
||||
return
|
||||
if layer.no_mass:
|
||||
self._idf.newidfobject(self._MATERIAL_NOMASS,
|
||||
Name=layer.name,
|
||||
Name=layer.material_name,
|
||||
Roughness=self._ROUGHNESS,
|
||||
Thermal_Resistance=layer.thermal_resistance
|
||||
)
|
||||
else:
|
||||
self._idf.newidfobject(self._MATERIAL,
|
||||
Name=layer.name,
|
||||
Name=layer.material_name,
|
||||
Roughness=self._ROUGHNESS,
|
||||
Thickness=layer.thickness,
|
||||
Conductivity=layer.conductivity,
|
||||
@ -276,11 +285,11 @@ class Idf:
|
||||
self._idf.newidfobject(self._COMPACT_SCHEDULE, **_kwargs)
|
||||
|
||||
def _write_schedules_file(self, usage, schedule):
|
||||
file_name = str((Path(self._output_path) / f'{schedule.type} schedules {usage}.dat').resolve())
|
||||
file_name = str((Path(self._output_path) / f'{schedule.type} schedules {usage}.csv').resolve())
|
||||
with open(file_name, 'w', encoding='utf8') as file:
|
||||
for value in schedule.values:
|
||||
file.write(f'{str(value)},\n')
|
||||
return file_name
|
||||
return Path(file_name).name
|
||||
|
||||
def _add_file_schedule(self, usage, schedule, file_name):
|
||||
_schedule = self._idf.newidfobject(self._FILE_SCHEDULE, Name=f'{schedule.type} schedules {usage}')
|
||||
@ -338,17 +347,17 @@ class Idf:
|
||||
_kwargs = {'Name': vegetation_name,
|
||||
'Outside_Layer': thermal_boundary.parent_surface.vegetation.name}
|
||||
for i in range(0, len(layers) - 1):
|
||||
_kwargs[f'Layer_{i + 2}'] = layers[i].name
|
||||
_kwargs[f'Layer_{i + 2}'] = layers[i].material_name
|
||||
else:
|
||||
_kwargs = {'Name': thermal_boundary.construction_name, 'Outside_Layer': layers[0].name}
|
||||
_kwargs = {'Name': thermal_boundary.construction_name, 'Outside_Layer': layers[0].material_name}
|
||||
for i in range(1, len(layers) - 1):
|
||||
_kwargs[f'Layer_{i + 1}'] = layers[i].name
|
||||
_kwargs[f'Layer_{i + 1}'] = layers[i].material_name
|
||||
self._idf.newidfobject(self._CONSTRUCTION, **_kwargs)
|
||||
|
||||
def _add_window_construction_and_material(self, thermal_opening):
|
||||
for window_material in self._idf.idfobjects[self._WINDOW_MATERIAL_SIMPLE]:
|
||||
if window_material['UFactor'] == thermal_opening.overall_u_value and \
|
||||
window_material['Solar_Heat_Gain_Coefficient'] == thermal_opening.g_value:
|
||||
window_material['Solar_Heat_Gain_Coefficient'] == thermal_opening.g_value:
|
||||
return
|
||||
|
||||
order = str(len(self._idf.idfobjects[self._WINDOW_MATERIAL_SIMPLE]) + 1)
|
||||
@ -381,29 +390,93 @@ class Idf:
|
||||
)
|
||||
|
||||
def _add_heating_system(self, thermal_zone, zone_name):
|
||||
for air_system in self._idf.idfobjects[self._IDEAL_LOAD_AIR_SYSTEM]:
|
||||
for air_system in self._idf.idfobjects[self._EQUIPMENT_CONNECTIONS]:
|
||||
if air_system.Zone_Name == zone_name:
|
||||
return
|
||||
thermostat = self._add_thermostat(thermal_zone)
|
||||
self._idf.newidfobject(self._IDEAL_LOAD_AIR_SYSTEM,
|
||||
self._idf.newidfobject(self._EQUIPMENT_CONNECTIONS,
|
||||
Zone_Name=zone_name,
|
||||
System_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {thermal_zone.usage_name}',
|
||||
Heating_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {thermal_zone.usage_name}',
|
||||
Cooling_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {thermal_zone.usage_name}',
|
||||
Template_Thermostat_Name=thermostat.Name)
|
||||
Zone_Conditioning_Equipment_List_Name=f'{zone_name} Equipment List',
|
||||
Zone_Air_Inlet_Node_or_NodeList_Name=f'{zone_name} Inlet Node List',
|
||||
Zone_Air_Node_Name=f'Node 1',
|
||||
Zone_Return_Air_Node_or_NodeList_Name=f'{zone_name} Return Node List')
|
||||
|
||||
def _add_nodelist_system(self, thermal_zone, zone_name):
|
||||
self._idf.newidfobject(self._NODE_LIST,
|
||||
Name=f'{zone_name} Inlet Node List',
|
||||
Node_1_Name='Node 2')
|
||||
self._idf.newidfobject(self._NODE_LIST,
|
||||
Name=f'{zone_name} Return Node List',
|
||||
Node_1_Name='Node 3')
|
||||
|
||||
def _add_baseboard_system(self, thermal_zone, zone_name):
|
||||
for baseboard in self._idf.idfobjects[self._BASEBOARD]:
|
||||
if baseboard.Zone_Name == zone_name:
|
||||
return
|
||||
self._idf.newidfobject(self._BASEBOARD, Name=f'Elec Baseboard',Availability_Schedule_Name='HVAC AVAIL')
|
||||
|
||||
def _add_air_terminal_system(self, thermal_zone, zone_name):
|
||||
"""for air_terminal in self._idf.idfobjects[self._AIR_TERMINAL_NO_REHEAT]:
|
||||
if air_terminal.Zone_Name == zone_name:
|
||||
return"""
|
||||
self._idf.newidfobject(self._AIR_TERMINAL_NO_REHEAT, Name=f'Diffuser',
|
||||
Availability_Schedule_Name='HVAC AVAIL',
|
||||
Air_Inlet_Node_Name='Node 4',
|
||||
Air_Outlet_Node_Name='Node 2',
|
||||
Maximum_Air_Flow_Rate='AutoSize')
|
||||
|
||||
def _add_air_distribution_system(self, thermal_zone, zone_name):
|
||||
for air_distribution in self._idf.idfobjects[self._AIR_DISTRIBUTION]:
|
||||
if air_distribution.Zone_Name == zone_name:
|
||||
return
|
||||
self._idf.newidfobject(self._AIR_DISTRIBUTION,
|
||||
Name='ADU Diffuser',
|
||||
Air_Distribution_Unit_Outlet_Node_Name='Node 2',
|
||||
Air_Terminal_Object_Type='AirTerminal:SingleDuct:ConstantVolume:NoReheat',
|
||||
Air_Terminal_Name='Diffuser')
|
||||
|
||||
def _add_equipment_list_system(self, thermal_zone, zone_name):
|
||||
for air_distribution in self._idf.idfobjects[self._EQUIPMENT_LIST]:
|
||||
if air_distribution.Zone_Name == zone_name:
|
||||
return
|
||||
self._idf.newidfobject(self._EQUIPMENT_LIST,
|
||||
Name=f'{zone_name} Equipment List',
|
||||
Load_Distribution_Scheme='SequentialLoad',
|
||||
Zone_Equipment_1_Object_Type='ZoneHVAC:Baseboard:Convective:Electric',
|
||||
Zone_Equipment_1_Name='Elec Baseboard',
|
||||
Zone_Equipment_1_Cooling_Sequence='1',
|
||||
Zone_Equipment_1_Heating_or_NoLoad_Sequence='1',
|
||||
Zone_Equipment_2_Object_Type='ZoneHVAC:AirDistributionUnit',
|
||||
Zone_Equipment_2_Name='ADU Diffuser',
|
||||
Zone_Equipment_2_Cooling_Sequence='2',
|
||||
Zone_Equipment_2_Heating_or_NoLoad_Sequence='2'
|
||||
)
|
||||
|
||||
def _add_sizing_zone(self, thermal_zone, zone_name):
|
||||
koa=self._idf.newidfobject(self._SIZING_ZONE,
|
||||
Zone_or_ZoneList_Name=f'{zone_name}',
|
||||
Zone_Cooling_Design_Supply_Air_Humidity_Ratio='0.0085',
|
||||
Zone_Heating_Design_Supply_Air_Humidity_Ratio='0.008'
|
||||
)
|
||||
|
||||
def _add_outdoor_air_design_specification(self, thermal_zone, zone_name):
|
||||
self._idf.newidfobject(self._DESIGN_SPECIFICATION_OUTDOOR_AIR,
|
||||
Name='MidriseApartment Apartment Ventilation',
|
||||
Outdoor_Air_Method='Sum',
|
||||
Outdoor_Air_Flow_per_Person='0.0169901079552')
|
||||
|
||||
def _add_occupancy(self, thermal_zone, zone_name):
|
||||
number_of_people = thermal_zone.occupancy.occupancy_density * thermal_zone.total_floor_area
|
||||
fraction_radiant = 0
|
||||
total_sensible = (
|
||||
thermal_zone.occupancy.sensible_radiative_internal_gain + thermal_zone.occupancy.sensible_convective_internal_gain
|
||||
thermal_zone.occupancy.sensible_radiative_internal_gain + thermal_zone.occupancy.sensible_convective_internal_gain
|
||||
)
|
||||
if total_sensible != 0:
|
||||
fraction_radiant = thermal_zone.occupancy.sensible_radiative_internal_gain / total_sensible
|
||||
|
||||
self._idf.newidfobject(self._PEOPLE,
|
||||
Name=f'{zone_name}_occupancy',
|
||||
Zone_or_ZoneList_Name=zone_name,
|
||||
Zone_or_ZoneList_or_Space_or_SpaceList_Name=zone_name,
|
||||
Number_of_People_Schedule_Name=f'Occupancy schedules {thermal_zone.usage_name}',
|
||||
Number_of_People_Calculation_Method="People",
|
||||
Number_of_People=number_of_people,
|
||||
@ -420,7 +493,7 @@ class Idf:
|
||||
|
||||
self._idf.newidfobject(self._LIGHTS,
|
||||
Name=f'{zone_name}_lights',
|
||||
Zone_or_ZoneList_Name=zone_name,
|
||||
Zone_or_ZoneList_or_Space_or_SpaceList_Name=zone_name,
|
||||
Schedule_Name=f'Lighting schedules {thermal_zone.usage_name}',
|
||||
Design_Level_Calculation_Method=method,
|
||||
Watts_per_Zone_Floor_Area=watts_per_zone_floor_area,
|
||||
@ -439,7 +512,7 @@ class Idf:
|
||||
self._idf.newidfobject(self._APPLIANCES,
|
||||
Fuel_Type=fuel_type,
|
||||
Name=f'{zone_name}_appliance',
|
||||
Zone_or_ZoneList_Name=zone_name,
|
||||
Zone_or_ZoneList_or_Space_or_SpaceList_Name=zone_name,
|
||||
Schedule_Name=f'Appliance schedules {thermal_zone.usage_name}',
|
||||
Design_Level_Calculation_Method=method,
|
||||
Power_per_Zone_Floor_Area=watts_per_zone_floor_area,
|
||||
@ -453,7 +526,7 @@ class Idf:
|
||||
_infiltration = thermal_zone.infiltration_rate_system_off * cte.HOUR_TO_SECONDS
|
||||
self._idf.newidfobject(self._INFILTRATION,
|
||||
Name=f'{zone_name}_infiltration',
|
||||
Zone_or_ZoneList_Name=zone_name,
|
||||
Zone_or_ZoneList_or_Space_or_SpaceList_Name=zone_name,
|
||||
Schedule_Name=schedule,
|
||||
Design_Flow_Rate_Calculation_Method='AirChanges/Hour',
|
||||
Air_Changes_per_Hour=_infiltration
|
||||
@ -464,7 +537,7 @@ class Idf:
|
||||
_air_change = thermal_zone.mechanical_air_change * cte.HOUR_TO_SECONDS
|
||||
self._idf.newidfobject(self._VENTILATION,
|
||||
Name=f'{zone_name}_ventilation',
|
||||
Zone_or_ZoneList_Name=zone_name,
|
||||
Zone_or_ZoneList_or_Space_or_SpaceList_Name=zone_name,
|
||||
Schedule_Name=schedule,
|
||||
Design_Flow_Rate_Calculation_Method='AirChanges/Hour',
|
||||
Air_Changes_per_Hour=_air_change
|
||||
@ -553,12 +626,7 @@ class Idf:
|
||||
self._add_zone(thermal_zone, building.name)
|
||||
self._add_heating_system(thermal_zone, building.name)
|
||||
self._add_infiltration(thermal_zone, building.name)
|
||||
self._add_ventilation(thermal_zone, building.name)
|
||||
self._add_occupancy(thermal_zone, building.name)
|
||||
self._add_lighting(thermal_zone, building.name)
|
||||
self._add_appliances(thermal_zone, building.name)
|
||||
self._add_dhw(thermal_zone, building.name)
|
||||
# todo: @Guille: if export_type is not surfaces, we don't differentiate between shadows and buildings?
|
||||
if self._export_type == "Surfaces":
|
||||
if building.name in self._target_buildings or building.name in self._adjacent_buildings:
|
||||
if building.thermal_zones_from_internal_zones is not None:
|
||||
@ -611,7 +679,6 @@ class Idf:
|
||||
windows_list.append(window)
|
||||
for window in windows_list:
|
||||
self._idf.removeidfobject(window)
|
||||
|
||||
self._idf.saveas(str(self._output_file))
|
||||
return self._idf
|
||||
|
||||
@ -640,24 +707,26 @@ class Idf:
|
||||
self._idf.intersect_match()
|
||||
|
||||
def _add_shading(self, building):
|
||||
for surface in building.surfaces:
|
||||
shading = self._idf.newidfobject(self._SHADING, Name=f'{surface.name}')
|
||||
for i, surface in enumerate(building.surfaces):
|
||||
shading = self._idf.newidfobject(self._SHADING, Name=f'{building.name}_{i}')
|
||||
coordinates = self._matrix_to_list(surface.solid_polygon.coordinates,
|
||||
self._city.lower_corner)
|
||||
shading.setcoords(coordinates)
|
||||
solar_reflectance = surface.short_wave_reflectance
|
||||
if solar_reflectance is None:
|
||||
solar_reflectance = ConfigurationHelper().short_wave_reflectance
|
||||
self._idf.newidfobject(self._SHADING_PROPERTY,
|
||||
Shading_Surface_Name=f'{surface.name}',
|
||||
Shading_Surface_Name=f'{building.name}_{i}',
|
||||
Diffuse_Solar_Reflectance_of_Unglazed_Part_of_Shading_Surface=solar_reflectance,
|
||||
Fraction_of_Shading_Surface_That_Is_Glazed=0)
|
||||
|
||||
def _add_pure_geometry(self, building, zone_name):
|
||||
for surface in building.surfaces:
|
||||
for index, surface in enumerate(building.surfaces):
|
||||
outside_boundary_condition = 'Outdoors'
|
||||
sun_exposure = 'SunExposed'
|
||||
wind_exposure = 'WindExposed'
|
||||
idf_surface_type = self.idf_surfaces[surface.type]
|
||||
_kwargs = {'Name': f'{surface.name}',
|
||||
_kwargs = {'Name': f'Building_{building.name}_surface_{index}',
|
||||
'Surface_Type': idf_surface_type,
|
||||
'Zone_Name': zone_name}
|
||||
if surface.type == cte.GROUND:
|
||||
@ -666,7 +735,7 @@ class Idf:
|
||||
wind_exposure = 'NoWind'
|
||||
if surface.percentage_shared is not None and surface.percentage_shared > 0.5:
|
||||
outside_boundary_condition = 'Surface'
|
||||
outside_boundary_condition_object = surface.name
|
||||
outside_boundary_condition_object = f'Building_{building.name}_surface_{index}'
|
||||
sun_exposure = 'NoSun'
|
||||
wind_exposure = 'NoWind'
|
||||
_kwargs['Outside_Boundary_Condition_Object'] = outside_boundary_condition_object
|
||||
@ -691,12 +760,12 @@ class Idf:
|
||||
|
||||
def _add_surfaces(self, building, zone_name):
|
||||
for thermal_zone in building.thermal_zones_from_internal_zones:
|
||||
for boundary in thermal_zone.thermal_boundaries:
|
||||
for index, boundary in enumerate(thermal_zone.thermal_boundaries):
|
||||
idf_surface_type = self.idf_surfaces[boundary.parent_surface.type]
|
||||
outside_boundary_condition = 'Outdoors'
|
||||
sun_exposure = 'SunExposed'
|
||||
wind_exposure = 'WindExposed'
|
||||
_kwargs = {'Name': f'{boundary.parent_surface.name}',
|
||||
_kwargs = {'Name': f'Building_{building.name}_surface_{index}',
|
||||
'Surface_Type': idf_surface_type,
|
||||
'Zone_Name': zone_name}
|
||||
if boundary.parent_surface.type == cte.GROUND:
|
||||
@ -705,7 +774,7 @@ class Idf:
|
||||
wind_exposure = 'NoWind'
|
||||
if boundary.parent_surface.percentage_shared is not None and boundary.parent_surface.percentage_shared > 0.5:
|
||||
outside_boundary_condition = 'Surface'
|
||||
outside_boundary_condition_object = boundary.parent_surface.name
|
||||
outside_boundary_condition_object = f'Building_{building.name}_surface_{index}'
|
||||
sun_exposure = 'NoSun'
|
||||
wind_exposure = 'NoWind'
|
||||
_kwargs['Outside_Boundary_Condition_Object'] = outside_boundary_condition_object
|
||||
|
File diff suppressed because it is too large
Load Diff
103532
hub/exports/building_energy/idf_files/Energy+9.5.idd
Normal file
103532
hub/exports/building_energy/idf_files/Energy+9.5.idd
Normal file
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,7 @@
|
||||
! HVAC: None.
|
||||
!
|
||||
|
||||
Version,9.5;
|
||||
Version,24.1;
|
||||
|
||||
Timestep,4;
|
||||
|
||||
@ -122,36 +122,30 @@
|
||||
No, !- Do Zone Sizing Calculation
|
||||
No, !- Do System Sizing Calculation
|
||||
No, !- Do Plant Sizing Calculation
|
||||
No, !- Run Simulation for Sizing Periods
|
||||
Yes, !- Run Simulation for Weather File Run Periods
|
||||
Yes, !- Run Simulation for Sizing Periods
|
||||
No, !- Run Simulation for Weather File Run Periods
|
||||
No, !- Do HVAC Sizing Simulation for Sizing Periods
|
||||
1; !- Maximum Number of HVAC Sizing Simulation Passes
|
||||
|
||||
Output:Table:SummaryReports, AnnualBuildingUtilityPerformanceSummary,
|
||||
DemandEndUseComponentsSummary,
|
||||
SensibleHeatGainSummary,
|
||||
InputVerificationandResultsSummary,
|
||||
AdaptiveComfortSummary,
|
||||
Standard62.1Summary,
|
||||
ClimaticDataSummary,
|
||||
EquipmentSummary,
|
||||
EnvelopeSummary,
|
||||
LightingSummary,
|
||||
HVACSizingSummary,
|
||||
SystemSummary,
|
||||
ComponentSizingSummary,
|
||||
OutdoorAirSummary,
|
||||
ObjectCountSummary,
|
||||
EndUseEnergyConsumptionOtherFuelsMonthly,
|
||||
PeakEnergyEndUseOtherFuelsMonthly;
|
||||
Output:VariableDictionary,Regular;
|
||||
|
||||
Output:Variable,*,Site Outdoor Air Drybulb Temperature,Timestep;
|
||||
|
||||
OutputControl:Table:Style, CommaAndHTML,JtoKWH;
|
||||
Output:Variable,*,Site Outdoor Air Wetbulb Temperature,Timestep;
|
||||
|
||||
Output:Meter,DISTRICTHEATING:Facility,hourly;
|
||||
Output:Meter,DISTRICTCOOLING:Facility,hourly;
|
||||
Output:Meter,InteriorEquipment:Electricity,hourly;
|
||||
Output:Meter,InteriorLights:Electricity,hourly;
|
||||
Output:Variable,*,Site Outdoor Air Dewpoint Temperature,Timestep;
|
||||
|
||||
Output:Variable,*,Site Solar Azimuth Angle,Timestep;
|
||||
|
||||
Output:Variable,*,Site Solar Altitude Angle,Timestep;
|
||||
|
||||
Output:Variable,*,Site Direct Solar Radiation Rate per Area,Timestep;
|
||||
|
||||
Output:Variable,*,Site Diffuse Solar Radiation Rate per Area,Timestep;
|
||||
|
||||
OutputControl:Table:Style,
|
||||
HTML; !- Column Separator
|
||||
|
||||
Output:Table:SummaryReports,
|
||||
AllSummary; !- Report 1 Name
|
||||
|
||||
OutputControl:IlluminanceMap:Style,
|
||||
Comma; !- Column separator
|
||||
|
157
hub/exports/building_energy/idf_files/Minimal9.5.idf
Normal file
157
hub/exports/building_energy/idf_files/Minimal9.5.idf
Normal file
@ -0,0 +1,157 @@
|
||||
! Minimal.idf
|
||||
! Basic file description: This is a minimal configuration necessary to run.
|
||||
! Highlights: Illustrates minimal items necessary to perform run.
|
||||
! BUILDING, SURFACEGEOMETRY, LOCATION and DESIGNDAY (or RUNPERIOD) are the absolute minimal required input objects.
|
||||
! TIME STEP IN HOUR is included so as to not get warning error.
|
||||
! Including two design days, Run Control object and RunPeriod to facilitate use.
|
||||
! Although not incredibly useful, this could be used as a weather/solar calculator.
|
||||
! Simulation Location/Run: Denver is included. Any could be used.
|
||||
! Building: None.
|
||||
!
|
||||
! Internal gains description: None.
|
||||
!
|
||||
! HVAC: None.
|
||||
!
|
||||
|
||||
Version,9.5;
|
||||
|
||||
Timestep,4;
|
||||
|
||||
Building,
|
||||
None, !- Name
|
||||
0.0000000E+00, !- North Axis {deg}
|
||||
Suburbs, !- Terrain
|
||||
0.04, !- Loads Convergence Tolerance Value {W}
|
||||
0.40, !- Temperature Convergence Tolerance Value {deltaC}
|
||||
FullInteriorAndExterior, !- Solar Distribution
|
||||
25, !- Maximum Number of Warmup Days
|
||||
6; !- Minimum Number of Warmup Days
|
||||
|
||||
GlobalGeometryRules,
|
||||
UpperLeftCorner, !- Starting Vertex Position
|
||||
CounterClockWise, !- Vertex Entry Direction
|
||||
World; !- Coordinate System
|
||||
|
||||
Site:Location,
|
||||
DENVER_STAPLETON_CO_USA_WMO_724690, !- Name
|
||||
39.77, !- Latitude {deg}
|
||||
-104.87, !- Longitude {deg}
|
||||
-7.00, !- Time Zone {hr}
|
||||
1611.00; !- Elevation {m}
|
||||
|
||||
! DENVER_STAPLETON_CO_USA Annual Heating Design Conditions Wind Speed=2.3m/s Wind Dir=180
|
||||
! Coldest Month=December
|
||||
! DENVER_STAPLETON_CO_USA Annual Heating 99.6%, MaxDB=-20°C
|
||||
|
||||
SizingPeriod:DesignDay,
|
||||
DENVER_STAPLETON Ann Htg 99.6% Condns DB, !- Name
|
||||
12, !- Month
|
||||
21, !- Day of Month
|
||||
WinterDesignDay, !- Day Type
|
||||
-20, !- Maximum Dry-Bulb Temperature {C}
|
||||
0.0, !- Daily Dry-Bulb Temperature Range {deltaC}
|
||||
, !- Dry-Bulb Temperature Range Modifier Type
|
||||
, !- Dry-Bulb Temperature Range Modifier Day Schedule Name
|
||||
Wetbulb, !- Humidity Condition Type
|
||||
-20, !- Wetbulb or DewPoint at Maximum Dry-Bulb {C}
|
||||
, !- Humidity Condition Day Schedule Name
|
||||
, !- Humidity Ratio at Maximum Dry-Bulb {kgWater/kgDryAir}
|
||||
, !- Enthalpy at Maximum Dry-Bulb {J/kg}
|
||||
, !- Daily Wet-Bulb Temperature Range {deltaC}
|
||||
83411., !- Barometric Pressure {Pa}
|
||||
2.3, !- Wind Speed {m/s}
|
||||
180, !- Wind Direction {deg}
|
||||
No, !- Rain Indicator
|
||||
No, !- Snow Indicator
|
||||
No, !- Daylight Saving Time Indicator
|
||||
ASHRAEClearSky, !- Solar Model Indicator
|
||||
, !- Beam Solar Day Schedule Name
|
||||
, !- Diffuse Solar Day Schedule Name
|
||||
, !- ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub) {dimensionless}
|
||||
, !- ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud) {dimensionless}
|
||||
0.00; !- Sky Clearness
|
||||
|
||||
! DENVER_STAPLETON Annual Cooling Design Conditions Wind Speed=4m/s Wind Dir=120
|
||||
! Hottest Month=July
|
||||
! DENVER_STAPLETON_CO_USA Annual Cooling (DB=>MWB) .4%, MaxDB=34.1°C MWB=15.8°C
|
||||
|
||||
SizingPeriod:DesignDay,
|
||||
DENVER_STAPLETON Ann Clg .4% Condns DB=>MWB, !- Name
|
||||
7, !- Month
|
||||
21, !- Day of Month
|
||||
SummerDesignDay, !- Day Type
|
||||
34.1, !- Maximum Dry-Bulb Temperature {C}
|
||||
15.2, !- Daily Dry-Bulb Temperature Range {deltaC}
|
||||
, !- Dry-Bulb Temperature Range Modifier Type
|
||||
, !- Dry-Bulb Temperature Range Modifier Day Schedule Name
|
||||
Wetbulb, !- Humidity Condition Type
|
||||
15.8, !- Wetbulb or DewPoint at Maximum Dry-Bulb {C}
|
||||
, !- Humidity Condition Day Schedule Name
|
||||
, !- Humidity Ratio at Maximum Dry-Bulb {kgWater/kgDryAir}
|
||||
, !- Enthalpy at Maximum Dry-Bulb {J/kg}
|
||||
, !- Daily Wet-Bulb Temperature Range {deltaC}
|
||||
83411., !- Barometric Pressure {Pa}
|
||||
4, !- Wind Speed {m/s}
|
||||
120, !- Wind Direction {deg}
|
||||
No, !- Rain Indicator
|
||||
No, !- Snow Indicator
|
||||
No, !- Daylight Saving Time Indicator
|
||||
ASHRAEClearSky, !- Solar Model Indicator
|
||||
, !- Beam Solar Day Schedule Name
|
||||
, !- Diffuse Solar Day Schedule Name
|
||||
, !- ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub) {dimensionless}
|
||||
, !- ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud) {dimensionless}
|
||||
1.00; !- Sky Clearness
|
||||
|
||||
RunPeriod,
|
||||
Run Period 1, !- Name
|
||||
1, !- Begin Month
|
||||
1, !- Begin Day of Month
|
||||
, !- Begin Year
|
||||
12, !- End Month
|
||||
31, !- End Day of Month
|
||||
, !- End Year
|
||||
Tuesday, !- Day of Week for Start Day
|
||||
Yes, !- Use Weather File Holidays and Special Days
|
||||
Yes, !- Use Weather File Daylight Saving Period
|
||||
No, !- Apply Weekend Holiday Rule
|
||||
Yes, !- Use Weather File Rain Indicators
|
||||
Yes; !- Use Weather File Snow Indicators
|
||||
|
||||
SimulationControl,
|
||||
No, !- Do Zone Sizing Calculation
|
||||
No, !- Do System Sizing Calculation
|
||||
No, !- Do Plant Sizing Calculation
|
||||
No, !- Run Simulation for Sizing Periods
|
||||
Yes, !- Run Simulation for Weather File Run Periods
|
||||
No, !- Do HVAC Sizing Simulation for Sizing Periods
|
||||
1; !- Maximum Number of HVAC Sizing Simulation Passes
|
||||
|
||||
Output:Table:SummaryReports, AnnualBuildingUtilityPerformanceSummary,
|
||||
DemandEndUseComponentsSummary,
|
||||
SensibleHeatGainSummary,
|
||||
InputVerificationandResultsSummary,
|
||||
AdaptiveComfortSummary,
|
||||
Standard62.1Summary,
|
||||
ClimaticDataSummary,
|
||||
EquipmentSummary,
|
||||
EnvelopeSummary,
|
||||
LightingSummary,
|
||||
HVACSizingSummary,
|
||||
SystemSummary,
|
||||
ComponentSizingSummary,
|
||||
OutdoorAirSummary,
|
||||
ObjectCountSummary,
|
||||
EndUseEnergyConsumptionOtherFuelsMonthly,
|
||||
PeakEnergyEndUseOtherFuelsMonthly;
|
||||
|
||||
|
||||
OutputControl:Table:Style, CommaAndHTML,JtoKWH;
|
||||
|
||||
Output:Meter,DISTRICTHEATING:Facility,hourly;
|
||||
Output:Meter,DISTRICTCOOLING:Facility,hourly;
|
||||
Output:Meter,InteriorEquipment:Electricity,hourly;
|
||||
Output:Meter,InteriorLights:Electricity,hourly;
|
||||
|
||||
OutputControl:IlluminanceMap:Style,
|
||||
Comma; !- Column separator
|
@ -7,9 +7,12 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from hub.exports.formats.glb import Glb
|
||||
from hub.exports.formats.obj import Obj
|
||||
from hub.exports.formats.geojson import Geojson
|
||||
from hub.exports.formats.simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm
|
||||
from hub.exports.formats.stl import Stl
|
||||
from hub.exports.formats.cesiumjs_tileset import CesiumjsTileset
|
||||
from hub.helpers.utils import validate_import_export_type
|
||||
|
||||
|
||||
@ -17,7 +20,7 @@ class ExportsFactory:
|
||||
"""
|
||||
Exports factory class
|
||||
"""
|
||||
def __init__(self, handler, city, path, target_buildings=None, adjacent_buildings=None):
|
||||
def __init__(self, handler, city, path, target_buildings=None, adjacent_buildings=None, base_uri=None):
|
||||
self._city = city
|
||||
self._handler = '_' + handler.lower()
|
||||
validate_import_export_type(ExportsFactory, handler)
|
||||
@ -26,18 +29,7 @@ class ExportsFactory:
|
||||
self._path = path
|
||||
self._target_buildings = target_buildings
|
||||
self._adjacent_buildings = adjacent_buildings
|
||||
|
||||
@property
|
||||
def _citygml(self):
|
||||
"""
|
||||
Export to citygml
|
||||
:return: None
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def _collada(self):
|
||||
raise NotImplementedError
|
||||
self._base_uri = base_uri
|
||||
|
||||
@property
|
||||
def _stl(self):
|
||||
@ -61,9 +53,30 @@ class ExportsFactory:
|
||||
Export the city to Simplified Radiosity Algorithm xml format
|
||||
:return: None
|
||||
"""
|
||||
return SimplifiedRadiosityAlgorithm(self._city,
|
||||
(self._path / f'{self._city.name}_sra.xml'),
|
||||
target_buildings=self._target_buildings)
|
||||
return SimplifiedRadiosityAlgorithm(
|
||||
self._city, (self._path / f'{self._city.name}_sra.xml'), target_buildings=self._target_buildings
|
||||
)
|
||||
|
||||
@property
|
||||
def _cesiumjs_tileset(self):
|
||||
"""
|
||||
Export the city to a cesiumJs tileset format
|
||||
:return: None
|
||||
"""
|
||||
return CesiumjsTileset(
|
||||
self._city,
|
||||
(self._path / f'{self._city.name}.json'),
|
||||
target_buildings=self._target_buildings,
|
||||
base_uri=self._base_uri
|
||||
)
|
||||
|
||||
@property
|
||||
def _glb(self):
|
||||
return Glb(self._city, self._path, target_buildings=self._target_buildings)
|
||||
|
||||
@property
|
||||
def _geojson(self):
|
||||
return Geojson(self._city, self._path, target_buildings=self._target_buildings)
|
||||
|
||||
def export(self):
|
||||
"""
|
||||
|
159
hub/exports/formats/cesiumjs_tileset.py
Normal file
159
hub/exports/formats/cesiumjs_tileset.py
Normal file
@ -0,0 +1,159 @@
|
||||
"""
|
||||
export a city into Cesium tileset format
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Concordia CERC group
|
||||
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
import json
|
||||
import math
|
||||
|
||||
import pyproj
|
||||
from pyproj import Transformer
|
||||
|
||||
from hub.helpers.geometry_helper import GeometryHelper
|
||||
|
||||
|
||||
class CesiumjsTileset:
|
||||
def __init__(self, city, file_name, target_buildings=None, base_uri=None):
|
||||
self._city = city
|
||||
self._file_name = file_name
|
||||
self._target_buildings = target_buildings
|
||||
if base_uri is None:
|
||||
base_uri = '.'
|
||||
self._base_uri = base_uri
|
||||
try:
|
||||
srs_name = self._city.srs_name
|
||||
if self._city.srs_name in GeometryHelper.srs_transformations:
|
||||
srs_name = GeometryHelper.srs_transformations[self._city.srs_name]
|
||||
input_reference = pyproj.CRS(srs_name) # Projected coordinate system from input data
|
||||
except pyproj.exceptions.CRSError as err:
|
||||
raise pyproj.exceptions.CRSError from err
|
||||
self._to_gps = Transformer.from_crs(input_reference, pyproj.CRS('EPSG:4326'))
|
||||
city_upper_corner = [
|
||||
self._city.upper_corner[0] - self._city.lower_corner[0],
|
||||
self._city.upper_corner[1] - self._city.lower_corner[1],
|
||||
self._city.upper_corner[2] - self._city.lower_corner[2]
|
||||
]
|
||||
city_lower_corner = [0, 0, 0]
|
||||
self._tile_set = {
|
||||
'asset': {
|
||||
'version': '1.1',
|
||||
"tilesetVersion": "1.2.3"
|
||||
},
|
||||
'position': self._to_gps.transform(self._city.lower_corner[0], self._city.lower_corner[1]),
|
||||
'schema': {
|
||||
'id': "building",
|
||||
'classes': {
|
||||
'building': {
|
||||
"properties": {
|
||||
'name': {
|
||||
'type': 'STRING'
|
||||
},
|
||||
'position': {
|
||||
'type': 'SCALAR',
|
||||
'array': True,
|
||||
'componentType': 'FLOAT32'
|
||||
},
|
||||
'aliases': {
|
||||
'type': 'STRING',
|
||||
'array': True,
|
||||
},
|
||||
'volume': {
|
||||
'type': 'SCALAR',
|
||||
'componentType': 'FLOAT32'
|
||||
},
|
||||
'floor_area': {
|
||||
'type': 'SCALAR',
|
||||
'componentType': 'FLOAT32'
|
||||
},
|
||||
'max_height': {
|
||||
'type': 'SCALAR',
|
||||
'componentType': 'INT32'
|
||||
},
|
||||
'year_of_construction': {
|
||||
'type': 'SCALAR',
|
||||
'componentType': 'INT32'
|
||||
},
|
||||
'function': {
|
||||
'type': 'STRING'
|
||||
},
|
||||
'usages_percentage': {
|
||||
'type': 'STRING'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'geometricError': 500,
|
||||
'root': {
|
||||
'boundingVolume': {
|
||||
'box': CesiumjsTileset._box_values(city_upper_corner, city_lower_corner)
|
||||
},
|
||||
'geometricError': 70,
|
||||
'refine': 'ADD',
|
||||
'children': []
|
||||
}
|
||||
}
|
||||
|
||||
self._export()
|
||||
|
||||
@staticmethod
|
||||
def _box_values(upper_corner, lower_corner):
|
||||
|
||||
x = (upper_corner[0] - lower_corner[0]) / 2
|
||||
x_center = ((upper_corner[0] - lower_corner[0]) / 2) + lower_corner[0]
|
||||
y = (upper_corner[1] - lower_corner[1]) / 2
|
||||
y_center = ((upper_corner[1] - lower_corner[1]) / 2) + lower_corner[1]
|
||||
z = (upper_corner[2] - lower_corner[2]) / 2
|
||||
return [x_center, y_center, z, x, 0, 0, 0, y, 0, 0, 0, z]
|
||||
|
||||
def _ground_coordinates(self, coordinates):
|
||||
ground_coordinates = []
|
||||
for coordinate in coordinates:
|
||||
ground_coordinates.append(
|
||||
(coordinate[0] - self._city.lower_corner[0], coordinate[1] - self._city.lower_corner[1])
|
||||
)
|
||||
return ground_coordinates
|
||||
|
||||
def _export(self):
|
||||
for building in self._city.buildings:
|
||||
upper_corner = [-math.inf, -math.inf, 0]
|
||||
lower_corner = [math.inf, math.inf, 0]
|
||||
lower_corner_coordinates = lower_corner
|
||||
for surface in building.grounds: # todo: maybe we should add the terrain?
|
||||
coordinates = self._ground_coordinates(surface.solid_polygon.coordinates)
|
||||
lower_corner = [min([c[0] for c in coordinates]), min([c[1] for c in coordinates]), 0]
|
||||
lower_corner_coordinates = [
|
||||
min([c[0] for c in surface.solid_polygon.coordinates]),
|
||||
min([c[1] for c in surface.solid_polygon.coordinates]),
|
||||
0
|
||||
]
|
||||
upper_corner = [max([c[0] for c in coordinates]), max([c[1] for c in coordinates]), building.max_height]
|
||||
|
||||
tile = {
|
||||
'boundingVolume': {
|
||||
'box': CesiumjsTileset._box_values(upper_corner, lower_corner)
|
||||
},
|
||||
'geometricError': 250,
|
||||
'metadata': {
|
||||
'class': 'building',
|
||||
'properties': {
|
||||
'name': building.name,
|
||||
'position': self._to_gps.transform(lower_corner_coordinates[0], lower_corner_coordinates[1]),
|
||||
'aliases': building.aliases,
|
||||
'volume': building.volume,
|
||||
'floor_area': building.floor_area,
|
||||
'max_height': building.max_height,
|
||||
'year_of_construction': building.year_of_construction,
|
||||
'function': building.function,
|
||||
'usages_percentage': building.usages_percentage
|
||||
}
|
||||
},
|
||||
'content': {
|
||||
'uri': f'{self._base_uri}/{building.name}.glb'
|
||||
}
|
||||
}
|
||||
self._tile_set['root']['children'].append(tile)
|
||||
|
||||
with open(self._file_name, 'w') as f:
|
||||
json.dump(self._tile_set, f, indent=2)
|
112
hub/exports/formats/geojson.py
Normal file
112
hub/exports/formats/geojson.py
Normal file
@ -0,0 +1,112 @@
|
||||
"""
|
||||
export a city into Geojson format
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Concordia CERC group
|
||||
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pyproj
|
||||
from pyproj import Transformer
|
||||
|
||||
from hub.helpers.geometry_helper import GeometryHelper
|
||||
|
||||
|
||||
class Geojson:
|
||||
"""
|
||||
Export to geojson format
|
||||
"""
|
||||
def __init__(self, city, path, target_buildings):
|
||||
self._city = city
|
||||
self._file_path = Path(path / f'{self._city.name}.geojson').resolve()
|
||||
try:
|
||||
srs_name = self._city.srs_name
|
||||
if self._city.srs_name in GeometryHelper.srs_transformations:
|
||||
srs_name = GeometryHelper.srs_transformations[self._city.srs_name]
|
||||
input_reference = pyproj.CRS(srs_name) # Projected coordinate system from input data
|
||||
except pyproj.exceptions.CRSError as err:
|
||||
raise pyproj.exceptions.CRSError from err
|
||||
self._to_gps = Transformer.from_crs(input_reference, pyproj.CRS('EPSG:4326'))
|
||||
if target_buildings is None:
|
||||
target_buildings = [b.name for b in self._city.buildings]
|
||||
self._geojson_skeleton = {
|
||||
'type': 'FeatureCollection',
|
||||
'features': []
|
||||
}
|
||||
self._feature_skeleton = {
|
||||
'type': 'Feature',
|
||||
'geometry': {
|
||||
'type': 'Polygon',
|
||||
'coordinates': []
|
||||
},
|
||||
'properties': {}
|
||||
}
|
||||
self._export()
|
||||
|
||||
def _export(self):
|
||||
for building in self._city.buildings:
|
||||
if len(building.grounds) == 1:
|
||||
ground = building.grounds[0]
|
||||
feature = self._polygon(ground)
|
||||
else:
|
||||
feature = self._multipolygon(building.grounds)
|
||||
feature['id'] = building.name
|
||||
feature['properties']['height'] = f'{building.max_height - building.lower_corner[2]}'
|
||||
feature['properties']['function'] = f'{building.function}'
|
||||
feature['properties']['year_of_construction'] = f'{building.year_of_construction}'
|
||||
feature['properties']['aliases'] = building.aliases
|
||||
feature['properties']['elevation'] = f'{building.lower_corner[2]}'
|
||||
self._geojson_skeleton['features'].append(feature)
|
||||
with open(self._file_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(self._geojson_skeleton, f, indent=2)
|
||||
|
||||
def _polygon(self, ground):
|
||||
feature = {
|
||||
'type': 'Feature',
|
||||
'geometry': {
|
||||
'type': 'Polygon',
|
||||
'coordinates': []
|
||||
},
|
||||
'properties': {}
|
||||
}
|
||||
ground_coordinates = []
|
||||
for coordinate in ground.solid_polygon.coordinates:
|
||||
gps_coordinate = self._to_gps.transform(coordinate[0], coordinate[1])
|
||||
ground_coordinates.insert(0, [gps_coordinate[1], gps_coordinate[0]])
|
||||
|
||||
first_gps_coordinate = self._to_gps.transform(
|
||||
ground.solid_polygon.coordinates[0][0],
|
||||
ground.solid_polygon.coordinates[0][1]
|
||||
)
|
||||
ground_coordinates.insert(0, [first_gps_coordinate[1], first_gps_coordinate[0]])
|
||||
feature['geometry']['coordinates'].append(ground_coordinates)
|
||||
return feature
|
||||
|
||||
def _multipolygon(self, grounds):
|
||||
feature = {
|
||||
'type': 'Feature',
|
||||
'geometry': {
|
||||
'type': 'MultiPolygon',
|
||||
'coordinates': []
|
||||
},
|
||||
'properties': {}
|
||||
}
|
||||
polygons = []
|
||||
for ground in grounds:
|
||||
ground_coordinates = []
|
||||
for coordinate in ground.solid_polygon.coordinates:
|
||||
gps_coordinate = self._to_gps.transform(coordinate[0], coordinate[1])
|
||||
ground_coordinates.insert(0, [gps_coordinate[1], gps_coordinate[0]])
|
||||
|
||||
first_gps_coordinate = self._to_gps.transform(
|
||||
ground.solid_polygon.coordinates[0][0],
|
||||
ground.solid_polygon.coordinates[0][1]
|
||||
)
|
||||
ground_coordinates.insert(0, [first_gps_coordinate[1], first_gps_coordinate[0]])
|
||||
polygons.append(ground_coordinates)
|
||||
feature['geometry']['coordinates'].append(polygons)
|
||||
return feature
|
||||
|
||||
|
54
hub/exports/formats/glb.py
Normal file
54
hub/exports/formats/glb.py
Normal file
@ -0,0 +1,54 @@
|
||||
"""
|
||||
export a city into Glb format
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Concordia CERC group
|
||||
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
from hub.city_model_structure.city import City
|
||||
from hub.exports.formats.obj import Obj
|
||||
|
||||
|
||||
class GltExceptionError(Exception):
|
||||
"""
|
||||
Glt execution error
|
||||
"""
|
||||
|
||||
|
||||
class Glb:
|
||||
"""
|
||||
Glb class
|
||||
"""
|
||||
def __init__(self, city, path, target_buildings=None):
|
||||
self._city = city
|
||||
self._path = path
|
||||
if target_buildings is None:
|
||||
target_buildings = [b.name for b in self._city.buildings]
|
||||
self._target_buildings = target_buildings
|
||||
self._export()
|
||||
|
||||
@property
|
||||
def _obj2gltf(self):
|
||||
return shutil.which('obj2gltf')
|
||||
|
||||
def _export(self):
|
||||
try:
|
||||
for building in self._city.buildings:
|
||||
city = City(self._city.lower_corner, self._city.upper_corner, self._city.srs_name)
|
||||
city.add_city_object(building)
|
||||
city.name = building.name
|
||||
Obj(city, self._path)
|
||||
glb = f'{self._path}/{building.name}.glb'
|
||||
subprocess.run([
|
||||
self._obj2gltf,
|
||||
'-i', f'{self._path}/{building.name}.obj',
|
||||
'-b',
|
||||
'-o', f'{glb}'
|
||||
])
|
||||
os.unlink(f'{self._path}/{building.name}.obj')
|
||||
os.unlink(f'{self._path}/{building.name}.mtl')
|
||||
except (subprocess.SubprocessError, subprocess.TimeoutExpired, subprocess.CalledProcessError) as err:
|
||||
raise GltExceptionError from err
|
@ -26,7 +26,7 @@ class Obj:
|
||||
|
||||
def _to_vertex(self, coordinate):
|
||||
x, y, z = self._ground(coordinate)
|
||||
return f'v {x} {z} {y}\n'
|
||||
return f'v {x} {z} -{y}\n' # to match opengl expectations
|
||||
|
||||
def _to_texture_vertex(self, coordinate):
|
||||
u, v, _ = self._ground(coordinate)
|
||||
@ -54,7 +54,7 @@ class Obj:
|
||||
with open(mtl_file_path, 'w', encoding='utf-8') as mtl:
|
||||
mtl.write("newmtl cerc_base_material\n")
|
||||
mtl.write("Ka 1.0 1.0 1.0 # Ambient color (white)\n")
|
||||
mtl.write("Kd 0.3 0.8 0.3 # Diffuse color (greenish)\n")
|
||||
mtl.write("Kd 0.1 0.3 0.1 # Diffuse color (greenish)\n")
|
||||
mtl.write("Ks 1.0 1.0 1.0 # Specular color (white)\n")
|
||||
mtl.write("Ns 400.0 # Specular exponent (defines shininess)\n")
|
||||
vertices = {}
|
||||
@ -63,12 +63,13 @@ class Obj:
|
||||
normal_index = 0
|
||||
with open(obj_file_path, 'w', encoding='utf-8') as obj:
|
||||
obj.write("# cerc-hub export\n")
|
||||
obj.write(f'mtllib {mtl_name}')
|
||||
obj.write(f'mtllib {mtl_name}\n')
|
||||
|
||||
for building in self._city.buildings:
|
||||
obj.write(f'# building {building.name}\n')
|
||||
obj.write(f'g {building.name}\n')
|
||||
obj.write('s off\n')
|
||||
|
||||
for surface in building.surfaces:
|
||||
obj.write(f'# surface {surface.name}\n')
|
||||
face = []
|
||||
@ -77,7 +78,6 @@ class Obj:
|
||||
textures = []
|
||||
for coordinate in surface.perimeter_polygon.coordinates:
|
||||
vertex = self._to_vertex(coordinate)
|
||||
|
||||
if vertex not in vertices:
|
||||
vertex_index += 1
|
||||
vertices[vertex] = vertex_index
|
||||
@ -86,8 +86,7 @@ class Obj:
|
||||
textures.append(self._to_texture_vertex(coordinate)) # only append if non-existing
|
||||
else:
|
||||
current = vertices[vertex]
|
||||
|
||||
face.insert(0, f'{current}/{current}/{normal_index}') # insert counterclockwise
|
||||
face.append(f'{current}/{current}/{normal_index}') # insert clockwise
|
||||
obj.writelines(normal) # add the normal
|
||||
obj.writelines(textures) # add the texture vertex
|
||||
|
||||
|
@ -84,7 +84,7 @@ class EilatPhysicsParameters:
|
||||
layer.thickness = layer_archetype.thickness
|
||||
total_thickness += layer_archetype.thickness
|
||||
archetype_material = layer_archetype.material
|
||||
layer.name = archetype_material.name
|
||||
layer.material_name = archetype_material.name
|
||||
layer.no_mass = archetype_material.no_mass
|
||||
if archetype_material.no_mass:
|
||||
layer.thermal_resistance = archetype_material.thermal_resistance
|
||||
|
@ -82,7 +82,7 @@ class NrcanPhysicsParameters:
|
||||
layer = Layer()
|
||||
layer.thickness = layer_archetype.thickness
|
||||
archetype_material = layer_archetype.material
|
||||
layer.name = archetype_material.name
|
||||
layer.material_name = archetype_material.name
|
||||
layer.no_mass = archetype_material.no_mass
|
||||
if archetype_material.no_mass:
|
||||
layer.thermal_resistance = archetype_material.thermal_resistance
|
||||
|
@ -85,7 +85,7 @@ class NrelPhysicsParameters:
|
||||
layer = Layer()
|
||||
layer.thickness = layer_archetype.thickness
|
||||
archetype_material = layer_archetype.material
|
||||
layer.name = archetype_material.name
|
||||
layer.material_name = archetype_material.name
|
||||
layer.no_mass = archetype_material.no_mass
|
||||
if archetype_material.no_mass:
|
||||
layer.thermal_resistance = archetype_material.thermal_resistance
|
||||
|
@ -4,6 +4,7 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Concordia CERC group
|
||||
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
import logging
|
||||
|
||||
import numpy as np
|
||||
import xmltodict
|
||||
@ -22,16 +23,17 @@ class CityGml:
|
||||
|
||||
def __init__(self,
|
||||
path,
|
||||
extrusion_height_field=None,
|
||||
year_of_construction_field=None,
|
||||
function_field=None,
|
||||
function_to_hub=None):
|
||||
function_to_hub=None,
|
||||
hub_crs=None):
|
||||
self._city = None
|
||||
self._lod = None
|
||||
self._lod1_tags = ['lod1Solid', 'lod1MultiSurface']
|
||||
self._lod2_tags = ['lod2Solid', 'lod2MultiSurface', 'lod2MultiCurve']
|
||||
self._extrusion_height_field = extrusion_height_field
|
||||
self._function_to_hub = function_to_hub
|
||||
if hub_crs is None:
|
||||
hub_crs = 'EPSG:26911'
|
||||
if function_field is None:
|
||||
function_field = 'function'
|
||||
if year_of_construction_field is None:
|
||||
@ -79,7 +81,8 @@ class CityGml:
|
||||
self._srs_name = envelope['@srsName']
|
||||
else:
|
||||
# If not coordinate system given assuming hub standard
|
||||
self._srs_name = "EPSG:26911"
|
||||
logging.warning(f'gml file contains no coordinate system assuming {hub_crs}')
|
||||
self._srs_name = hub_crs
|
||||
else:
|
||||
# get the boundary from the city objects instead
|
||||
for city_object_member in self._gml['CityModel']['cityObjectMember']:
|
||||
|
@ -49,6 +49,8 @@ class CityGmlLod2(CityGmlBase):
|
||||
surface_encoding, surface_subtype = cls._surface_encoding(bounded[surface_type])
|
||||
except NotImplementedError:
|
||||
continue
|
||||
if 'surfaceMember' not in bounded[surface_type][surface_encoding][surface_subtype]:
|
||||
continue
|
||||
for member in bounded[surface_type][surface_encoding][surface_subtype]['surfaceMember']:
|
||||
if 'CompositeSurface' in member:
|
||||
for composite_members in member['CompositeSurface']['surfaceMember']:
|
||||
|
@ -34,9 +34,13 @@ class Geojson:
|
||||
extrusion_height_field=None,
|
||||
year_of_construction_field=None,
|
||||
function_field=None,
|
||||
function_to_hub=None):
|
||||
# todo: destination epsg should change according actual the location
|
||||
self._transformer = Transformer.from_crs('epsg:4326', 'epsg:26911')
|
||||
function_to_hub=None,
|
||||
hub_crs=None
|
||||
):
|
||||
self._hub_crs = hub_crs
|
||||
if hub_crs is None :
|
||||
self._hub_crs = 'epsg:26911'
|
||||
self._transformer = Transformer.from_crs('epsg:4326', self._hub_crs)
|
||||
self._min_x = cte.MAX_FLOAT
|
||||
self._min_y = cte.MAX_FLOAT
|
||||
self._max_x = cte.MIN_FLOAT
|
||||
@ -155,7 +159,7 @@ class Geojson:
|
||||
extrusion_height))
|
||||
else:
|
||||
raise NotImplementedError(f'Geojson geometry type [{geometry["type"]}] unknown')
|
||||
self._city = City([self._min_x, self._min_y, 0.0], [self._max_x, self._max_y, self._max_z], 'epsg:26911')
|
||||
self._city = City([self._min_x, self._min_y, 0.0], [self._max_x, self._max_y, self._max_z], self._hub_crs)
|
||||
for building in buildings:
|
||||
# Do not include "small building-like structures" to buildings
|
||||
if building.floor_area >= 25:
|
||||
@ -166,7 +170,6 @@ class Geojson:
|
||||
if lod > 0:
|
||||
lines_information = GeometryHelper.city_mapping(self._city, plot=False)
|
||||
self._store_shared_percentage_to_walls(self._city, lines_information)
|
||||
|
||||
return self._city
|
||||
|
||||
def _polygon_coordinates_to_3d(self, polygon_coordinates):
|
||||
@ -210,8 +213,6 @@ class Geojson:
|
||||
polygon = Polygon(coordinates)
|
||||
polygon.area = igh.ground_area(coordinates)
|
||||
surfaces[-1] = Surface(polygon, polygon)
|
||||
if len(surfaces) > 1:
|
||||
raise ValueError('too many surfaces!!!!')
|
||||
building = Building(f'{building_name}', surfaces, year_of_construction, function)
|
||||
for alias in building_aliases:
|
||||
building.add_alias(alias)
|
||||
|
@ -17,12 +17,13 @@ class GeometryFactory:
|
||||
GeometryFactory class
|
||||
"""
|
||||
def __init__(self, file_type,
|
||||
path,
|
||||
path=None,
|
||||
aliases_field=None,
|
||||
height_field=None,
|
||||
year_of_construction_field=None,
|
||||
function_field=None,
|
||||
function_to_hub=None):
|
||||
function_to_hub=None,
|
||||
hub_crs=None):
|
||||
self._file_type = '_' + file_type.lower()
|
||||
validate_import_export_type(GeometryFactory, file_type)
|
||||
self._path = path
|
||||
@ -31,6 +32,7 @@ class GeometryFactory:
|
||||
self._year_of_construction_field = year_of_construction_field
|
||||
self._function_field = function_field
|
||||
self._function_to_hub = function_to_hub
|
||||
self._hub_crs = hub_crs
|
||||
|
||||
@property
|
||||
def _citygml(self) -> City:
|
||||
@ -39,10 +41,10 @@ class GeometryFactory:
|
||||
:return: City
|
||||
"""
|
||||
return CityGml(self._path,
|
||||
self._height_field,
|
||||
self._year_of_construction_field,
|
||||
self._function_field,
|
||||
self._function_to_hub).city
|
||||
self._function_to_hub,
|
||||
self._hub_crs).city
|
||||
|
||||
@property
|
||||
def _obj(self) -> City:
|
||||
@ -63,7 +65,8 @@ class GeometryFactory:
|
||||
self._height_field,
|
||||
self._year_of_construction_field,
|
||||
self._function_field,
|
||||
self._function_to_hub).city
|
||||
self._function_to_hub,
|
||||
self._hub_crs).city
|
||||
|
||||
@property
|
||||
def city(self) -> City:
|
||||
|
@ -70,7 +70,7 @@ class InselMonthlyEnergyBalance:
|
||||
total_day += value
|
||||
for day_type in schedule.day_types:
|
||||
total_lighting += total_day * cte.WEEK_DAYS_A_MONTH[month][day_type] \
|
||||
* lighting_density / cte.WATTS_HOUR_TO_JULES
|
||||
* lighting_density * cte.WATTS_HOUR_TO_JULES
|
||||
lighting_demand.append(total_lighting * area)
|
||||
|
||||
for schedule in thermal_zone.appliances.schedules:
|
||||
@ -79,7 +79,7 @@ class InselMonthlyEnergyBalance:
|
||||
total_day += value
|
||||
for day_type in schedule.day_types:
|
||||
total_appliances += total_day * cte.WEEK_DAYS_A_MONTH[month][day_type] \
|
||||
* appliances_density / cte.WATTS_HOUR_TO_JULES
|
||||
* appliances_density * cte.WATTS_HOUR_TO_JULES
|
||||
appliances_demand.append(total_appliances * area)
|
||||
|
||||
for schedule in thermal_zone.domestic_hot_water.schedules:
|
||||
@ -89,7 +89,7 @@ class InselMonthlyEnergyBalance:
|
||||
for day_type in schedule.day_types:
|
||||
demand = (
|
||||
peak_flow * cte.WATER_DENSITY * cte.WATER_HEAT_CAPACITY
|
||||
* (service_temperature - cold_water[i_month]) / cte.WATTS_HOUR_TO_JULES
|
||||
* (service_temperature - cold_water[i_month]) * cte.WATTS_HOUR_TO_JULES
|
||||
)
|
||||
total_dhw_demand += total_day * cte.WEEK_DAYS_A_MONTH[month][day_type] * demand
|
||||
domestic_hot_water_demand.append(total_dhw_demand * area)
|
||||
|
@ -24,7 +24,8 @@ class Weather:
|
||||
'DE.01.082': 'https://energyplus-weather.s3.amazonaws.com/europe_wmo_region_6/DEU/DEU_Stuttgart.107380_IWEC/DEU_Stuttgart.107380_IWEC.epw',
|
||||
'US.NY.047': 'https://energyplus-weather.s3.amazonaws.com/north_and_central_america_wmo_region_4/USA/NY/USA_NY_New.York.City-Central.Park.94728_TMY/USA_NY_New.York.City-Central.Park.94728_TMY.epw',
|
||||
'CA.10.12': 'https://energyplus-weather.s3.amazonaws.com/north_and_central_america_wmo_region_4/CAN/PQ/CAN_PQ_Quebec.717140_CWEC/CAN_PQ_Quebec.717140_CWEC.epw',
|
||||
'IL.01.': 'https://energyplus-weather.s3.amazonaws.com/europe_wmo_region_6/ISR/ISR_Eilat.401990_MSI/ISR_Eilat.401990_MSI.epw'
|
||||
'IL.01.': 'https://energyplus-weather.s3.amazonaws.com/europe_wmo_region_6/ISR/ISR_Eilat.401990_MSI/ISR_Eilat.401990_MSI.epw',
|
||||
'ES.07.PM': 'https://energyplus-weather.s3.amazonaws.com/europe_wmo_region_6/ESP/ESP_Palma.083060_SWEC/ESP_Palma.083060_SWEC.epw'
|
||||
}
|
||||
# todo: this dictionary need to be completed, a data science student task?
|
||||
|
||||
|
@ -4,10 +4,8 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Concordia CERC group
|
||||
Project CoderPeter Yefi peteryefi@gmail.com
|
||||
"""
|
||||
import json
|
||||
from typing import Dict
|
||||
|
||||
|
||||
from hub.persistence.repositories.application import Application
|
||||
from hub.persistence.repositories.city import City
|
||||
from hub.persistence.repositories.city_object import CityObject
|
||||
@ -75,10 +73,10 @@ class DBControl:
|
||||
:
|
||||
"""
|
||||
cities = self._city.get_by_user_id_application_id_and_scenario(user_id, application_id, scenario)
|
||||
for city in cities:
|
||||
result = self.building_info(name, city[0].id)
|
||||
if result is not None:
|
||||
return result
|
||||
c = [c[0].id for c in cities]
|
||||
result = self._city_object.building_in_cities_info(name, c)
|
||||
if result is not None:
|
||||
return result
|
||||
return None
|
||||
|
||||
def building_info(self, name, city_id) -> CityObject:
|
||||
@ -90,17 +88,27 @@ class DBControl:
|
||||
"""
|
||||
return self._city_object.get_by_name_or_alias_and_city(name, city_id)
|
||||
|
||||
def buildings_info(self, request_values, city_id) -> [CityObject]:
|
||||
def building_info_in_cities(self, name, cities) -> CityObject:
|
||||
"""
|
||||
Retrieve the building info from the database
|
||||
:param name: Building name
|
||||
:param cities: [City ID]
|
||||
:return: CityObject
|
||||
"""
|
||||
return self._city_object.get_by_name_or_alias_in_cities(name, cities)
|
||||
|
||||
def buildings_info(self, user_id, application_id, names_or_aliases) -> [CityObject]:
|
||||
"""
|
||||
Retrieve the buildings info from the database
|
||||
:param request_values: Building names
|
||||
:param city_id: City ID
|
||||
:param user_id: User ID
|
||||
:param application_id: Application ID
|
||||
:param names_or_aliases: A list of names or alias for the buildings
|
||||
:return: [CityObject]
|
||||
"""
|
||||
buildings = []
|
||||
for name in request_values['names']:
|
||||
buildings.append(self.building_info(name, city_id))
|
||||
return buildings
|
||||
results = self._city_object.get_by_name_or_alias_for_user_app(user_id, application_id, names_or_aliases)
|
||||
if results is None:
|
||||
return []
|
||||
return results
|
||||
|
||||
def results(self, user_id, application_id, request_values, result_names=None) -> Dict:
|
||||
"""
|
||||
@ -114,10 +122,7 @@ class DBControl:
|
||||
result_names = []
|
||||
results = {}
|
||||
for scenario in request_values['scenarios']:
|
||||
print('scenario', scenario, results)
|
||||
for scenario_name in scenario.keys():
|
||||
print('scenario name', scenario_name)
|
||||
|
||||
result_sets = self._city.get_by_user_id_application_id_and_scenario(
|
||||
user_id,
|
||||
application_id,
|
||||
@ -125,25 +130,21 @@ class DBControl:
|
||||
)
|
||||
if result_sets is None:
|
||||
continue
|
||||
for result_set in result_sets:
|
||||
city_id = result_set[0].id
|
||||
results[scenario_name] = []
|
||||
city_ids = [r[0].id for r in result_sets]
|
||||
for building_name in scenario[scenario_name]:
|
||||
_building = self._city_object.get_by_name_or_alias_in_cities(building_name, city_ids)
|
||||
if _building is None:
|
||||
continue
|
||||
city_object_id = _building.id
|
||||
_ = self._simulation_results.get_simulation_results_by_city_object_id_and_names(
|
||||
city_object_id,
|
||||
result_names)
|
||||
|
||||
results[scenario_name] = []
|
||||
for building_name in scenario[scenario_name]:
|
||||
_building = self._city_object.get_by_name_or_alias_and_city(building_name, city_id)
|
||||
if _building is None:
|
||||
continue
|
||||
city_object_id = _building.id
|
||||
_ = self._simulation_results.get_simulation_results_by_city_id_city_object_id_and_names(
|
||||
city_id,
|
||||
city_object_id,
|
||||
result_names)
|
||||
|
||||
for value in _:
|
||||
values = json.loads(value.values)
|
||||
values["building"] = building_name
|
||||
results[scenario_name].append(values)
|
||||
print(scenario, results)
|
||||
for value in _:
|
||||
values = value.values
|
||||
values["building"] = building_name
|
||||
results[scenario_name].append(values)
|
||||
return results
|
||||
|
||||
def persist_city(self, city: City, pickle_path, scenario, application_id: int, user_id: int):
|
||||
|
@ -7,9 +7,10 @@ Project Coder Guille Gutierrez Guillermo.GutierrezMorote@concordia.ca
|
||||
|
||||
import datetime
|
||||
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy import Column, Integer, String, Sequence
|
||||
from sqlalchemy import DateTime
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
|
||||
from hub.persistence.configuration import Models
|
||||
|
||||
|
||||
|
@ -21,8 +21,8 @@ class City(Models):
|
||||
pickle_path = Column(String, nullable=False)
|
||||
name = Column(String, nullable=False)
|
||||
scenario = Column(String, nullable=False)
|
||||
application_id = Column(Integer, ForeignKey('application.id'), nullable=False)
|
||||
user_id = Column(Integer, ForeignKey('user.id'), nullable=True)
|
||||
application_id = Column(Integer, ForeignKey('application.id', ondelete='CASCADE'), nullable=False)
|
||||
user_id = Column(Integer, ForeignKey('user.id', ondelete='CASCADE'), nullable=True)
|
||||
hub_release = Column(String, nullable=False)
|
||||
created = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
updated = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
|
@ -21,7 +21,7 @@ class CityObject(Models):
|
||||
"""
|
||||
__tablename__ = 'city_object'
|
||||
id = Column(Integer, Sequence('city_object_id_seq'), primary_key=True)
|
||||
city_id = Column(Integer, ForeignKey('city.id'), nullable=False)
|
||||
city_id = Column(Integer, ForeignKey('city.id', ondelete='CASCADE'), nullable=False)
|
||||
name = Column(String, nullable=False)
|
||||
aliases = Column(String, nullable=True)
|
||||
type = Column(String, nullable=False)
|
||||
|
@ -19,8 +19,8 @@ class SimulationResults(Models):
|
||||
"""
|
||||
__tablename__ = 'simulation_results'
|
||||
id = Column(Integer, Sequence('simulation_results_id_seq'), primary_key=True)
|
||||
city_id = Column(Integer, ForeignKey('city.id'), nullable=True)
|
||||
city_object_id = Column(Integer, ForeignKey('city_object.id'), nullable=True)
|
||||
city_id = Column(Integer, ForeignKey('city.id', ondelete='CASCADE'), nullable=True)
|
||||
city_object_id = Column(Integer, ForeignKey('city_object.id', ondelete='CASCADE'), nullable=True)
|
||||
name = Column(String, nullable=False)
|
||||
values = Column(JSONB, nullable=False)
|
||||
created = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
|
@ -10,6 +10,7 @@ import logging
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from hub.persistence.repository import Repository
|
||||
from hub.persistence.models import Application as Model
|
||||
@ -48,10 +49,11 @@ class Application(Repository):
|
||||
pass
|
||||
try:
|
||||
application = Model(name=name, description=description, application_uuid=application_uuid)
|
||||
self.session.add(application)
|
||||
self.session.commit()
|
||||
self.session.refresh(application)
|
||||
return application.id
|
||||
with Session(self.engine) as session:
|
||||
session.add(application)
|
||||
session.commit()
|
||||
session.refresh(application)
|
||||
return application.id
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('An error occurred while creating application %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
@ -65,10 +67,11 @@ class Application(Repository):
|
||||
:return: None
|
||||
"""
|
||||
try:
|
||||
self.session.query(Model).filter(
|
||||
Model.application_uuid == application_uuid
|
||||
).update({'name': name, 'description': description, 'updated': datetime.datetime.utcnow()})
|
||||
self.session.commit()
|
||||
with Session(self.engine) as session:
|
||||
session.query(Model).filter(
|
||||
Model.application_uuid == application_uuid
|
||||
).update({'name': name, 'description': description, 'updated': datetime.datetime.utcnow()})
|
||||
session.commit()
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while updating application %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
@ -80,9 +83,10 @@ class Application(Repository):
|
||||
:return: None
|
||||
"""
|
||||
try:
|
||||
self.session.query(Model).filter(Model.application_uuid == application_uuid).delete()
|
||||
self.session.flush()
|
||||
self.session.commit()
|
||||
with Session(self.engine) as session:
|
||||
session.query(Model).filter(Model.application_uuid == application_uuid).delete()
|
||||
session.flush()
|
||||
session.commit()
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while deleting application %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
@ -94,10 +98,11 @@ class Application(Repository):
|
||||
:return: Application with the provided application_uuid
|
||||
"""
|
||||
try:
|
||||
result_set = self.session.execute(select(Model).where(
|
||||
Model.application_uuid == application_uuid)
|
||||
).first()
|
||||
return result_set[0]
|
||||
with Session(self.engine) as session:
|
||||
result_set = session.execute(select(Model).where(
|
||||
Model.application_uuid == application_uuid)
|
||||
).first()
|
||||
return result_set[0]
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while fetching application by application_uuid %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
|
@ -54,18 +54,18 @@ class City(Repository):
|
||||
application_id,
|
||||
user_id,
|
||||
__version__)
|
||||
|
||||
self.session.add(db_city)
|
||||
self.session.flush()
|
||||
self.session.commit()
|
||||
for building in city.buildings:
|
||||
db_city_object = CityObject(db_city.id,
|
||||
building)
|
||||
self.session.add(db_city_object)
|
||||
self.session.flush()
|
||||
self.session.commit()
|
||||
self.session.refresh(db_city)
|
||||
return db_city.id
|
||||
with Session(self.engine) as session:
|
||||
session.add(db_city)
|
||||
session.flush()
|
||||
session.commit()
|
||||
for building in city.buildings:
|
||||
db_city_object = CityObject(db_city.id,
|
||||
building)
|
||||
session.add(db_city_object)
|
||||
session.flush()
|
||||
session.commit()
|
||||
session.refresh(db_city)
|
||||
return db_city.id
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('An error occurred while creating a city %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
@ -79,8 +79,9 @@ class City(Repository):
|
||||
"""
|
||||
try:
|
||||
now = datetime.datetime.utcnow()
|
||||
self.session.query(Model).filter(Model.id == city_id).update({'name': city.name, 'updated': now})
|
||||
self.session.commit()
|
||||
with Session(self.engine) as session:
|
||||
session.query(Model).filter(Model.id == city_id).update({'name': city.name, 'updated': now})
|
||||
session.commit()
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while updating city %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
@ -92,9 +93,10 @@ class City(Repository):
|
||||
:return: None
|
||||
"""
|
||||
try:
|
||||
self.session.query(CityObject).filter(CityObject.city_id == city_id).delete()
|
||||
self.session.query(Model).filter(Model.id == city_id).delete()
|
||||
self.session.commit()
|
||||
with Session(self.engine) as session:
|
||||
session.query(CityObject).filter(CityObject.city_id == city_id).delete()
|
||||
session.query(Model).filter(Model.id == city_id).delete()
|
||||
session.commit()
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while fetching city %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
@ -108,13 +110,12 @@ class City(Repository):
|
||||
:return: [ModelCity]
|
||||
"""
|
||||
try:
|
||||
result_set = self.session.execute(select(Model).where(Model.user_id == user_id,
|
||||
Model.application_id == application_id,
|
||||
Model.scenario == scenario
|
||||
)).all()
|
||||
self.session.close()
|
||||
self.session = Session(self.engine)
|
||||
return result_set
|
||||
with Session(self.engine) as session:
|
||||
result_set = session.execute(select(Model).where(Model.user_id == user_id,
|
||||
Model.application_id == application_id,
|
||||
Model.scenario == scenario
|
||||
)).all()
|
||||
return result_set
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while fetching city by name %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
@ -127,11 +128,11 @@ class City(Repository):
|
||||
:return: ModelCity
|
||||
"""
|
||||
try:
|
||||
result_set = self.session.execute(
|
||||
select(Model).where(Model.user_id == user_id, Model.application_id == application_id)
|
||||
)
|
||||
return [r[0] for r in result_set]
|
||||
with Session(self.engine) as session:
|
||||
result_set = session.execute(
|
||||
select(Model).where(Model.user_id == user_id, Model.application_id == application_id)
|
||||
)
|
||||
return [r[0] for r in result_set]
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while fetching city by name %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
|
||||
|
@ -6,14 +6,16 @@ Project Coder Guille Gutierrez Guillermo.GutierrezMorote@concordia.ca
|
||||
"""
|
||||
import datetime
|
||||
import logging
|
||||
from typing import Union
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from hub.city_model_structure.building import Building
|
||||
from hub.persistence.repository import Repository
|
||||
from hub.persistence.models import CityObject as Model
|
||||
from hub.persistence.models import City as CityModel
|
||||
from hub.persistence.repository import Repository
|
||||
|
||||
|
||||
class CityObject(Repository):
|
||||
@ -46,10 +48,11 @@ class CityObject(Repository):
|
||||
try:
|
||||
city_object = Model(city_id=city_id,
|
||||
building=building)
|
||||
self.session.add(city_object)
|
||||
self.session.flush()
|
||||
self.session.commit()
|
||||
self.session.refresh(city_object)
|
||||
with Session(self.engine) as session:
|
||||
session.add(city_object)
|
||||
session.flush()
|
||||
session.commit()
|
||||
session.refresh(city_object)
|
||||
return city_object.id
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('An error occurred while creating city_object %s', err)
|
||||
@ -68,17 +71,18 @@ class CityObject(Repository):
|
||||
for usage in internal_zone.usages:
|
||||
object_usage = f'{object_usage}{usage.name}_{usage.percentage} '
|
||||
object_usage = object_usage.rstrip()
|
||||
self.session.query(Model).filter(Model.name == building.name, Model.city_id == city_id).update(
|
||||
{'name': building.name,
|
||||
'alias': building.alias,
|
||||
'object_type': building.type,
|
||||
'year_of_construction': building.year_of_construction,
|
||||
'function': building.function,
|
||||
'usage': object_usage,
|
||||
'volume': building.volume,
|
||||
'area': building.floor_area,
|
||||
'updated': datetime.datetime.utcnow()})
|
||||
self.session.commit()
|
||||
with Session(self.engine) as session:
|
||||
session.query(Model).filter(Model.name == building.name, Model.city_id == city_id).update(
|
||||
{'name': building.name,
|
||||
'aliases': building.aliases,
|
||||
'object_type': building.type,
|
||||
'year_of_construction': building.year_of_construction,
|
||||
'function': building.function,
|
||||
'usage': object_usage,
|
||||
'volume': building.volume,
|
||||
'area': building.floor_area,
|
||||
'updated': datetime.datetime.utcnow()})
|
||||
session.commit()
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while updating city object %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
@ -91,30 +95,116 @@ class CityObject(Repository):
|
||||
:return: None
|
||||
"""
|
||||
try:
|
||||
self.session.query(Model).filter(Model.city_id == city_id, Model.name == name).delete()
|
||||
self.session.commit()
|
||||
with Session(self.engine) as session:
|
||||
session.query(Model).filter(Model.city_id == city_id, Model.name == name).delete()
|
||||
session.commit()
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while deleting application %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
|
||||
def get_by_name_or_alias_and_city(self, name, city_id) -> Model:
|
||||
def building_in_cities_info(self, name, cities):
|
||||
"""
|
||||
Fetch a city object based on name and city id
|
||||
:param name: city object name
|
||||
:param city_id: a city identifier
|
||||
:param cities: city identifiers
|
||||
:return: [CityObject] with the provided name or alias belonging to the city with id city_id
|
||||
"""
|
||||
try:
|
||||
# search by name first
|
||||
city_object = self.session.execute(select(Model).where(Model.name == name, Model.city_id == city_id)).first()
|
||||
if city_object is not None:
|
||||
return city_object[0]
|
||||
# name not found, so search by alias instead
|
||||
city_objects = self.session.execute(
|
||||
select(Model).where(Model.aliases.contains(name), Model.city_id == city_id)
|
||||
).all()
|
||||
self.session.close()
|
||||
self.session = Session(self.engine)
|
||||
with Session(self.engine) as session:
|
||||
city_object = session.execute(select(Model).where(
|
||||
Model.name == name, Model.city_id.in_(cities))
|
||||
).first()
|
||||
if city_object is not None:
|
||||
return city_object[0]
|
||||
# name not found, so search by alias instead
|
||||
city_objects = session.execute(
|
||||
select(Model).where(Model.aliases.contains(name), Model.city_id.in_(cities))
|
||||
).all()
|
||||
for city_object in city_objects:
|
||||
aliases = city_object[0].aliases.replace('{', '').replace('}', '').split(',')
|
||||
for alias in aliases:
|
||||
if alias == name:
|
||||
# force the name as the alias
|
||||
city_object[0].name = name
|
||||
return city_object[0]
|
||||
return None
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while fetching city object by name and city: %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
except IndexError as err:
|
||||
logging.error('Error while fetching city object by name and city, empty result %s', err)
|
||||
raise IndexError from err
|
||||
|
||||
def get_by_name_or_alias_for_user_app(self, user_id, application_id, names) -> Union[Model, None]:
|
||||
"""
|
||||
Fetch city objects belonging to the user and application where the name or alias is in the names list
|
||||
:param user_id: User ID
|
||||
:param application_id: Application ID
|
||||
:param names: a list of building aliases or names
|
||||
:return [CityObject] or None
|
||||
"""
|
||||
with Session(self.engine) as session:
|
||||
cities = session.execute(select(CityModel).where(
|
||||
CityModel.user_id == user_id, CityModel.application_id == application_id
|
||||
)).all()
|
||||
ids = [c[0].id for c in cities]
|
||||
buildings = session.execute(select(Model).where(
|
||||
Model.city_id.in_(ids), Model.name.in_(names)
|
||||
))
|
||||
results = [r[0] for r in buildings]
|
||||
print(ids, buildings)
|
||||
return None
|
||||
|
||||
def get_by_name_or_alias_and_city(self, name, city_id) -> Union[Model, None]:
|
||||
"""
|
||||
Fetch a city object based on name and city id
|
||||
:param name: city object name
|
||||
:param city_id: a city identifier
|
||||
:return: [CityObject] with the provided name or alias belonging to the city with id city_id
|
||||
"""
|
||||
try:
|
||||
# search by name first
|
||||
with Session(self.engine) as session:
|
||||
city_object = session.execute(select(Model).where(Model.name == name, Model.city_id == city_id)).first()
|
||||
if city_object is not None:
|
||||
return city_object[0]
|
||||
# name not found, so search by alias instead
|
||||
city_objects = session.execute(
|
||||
select(Model).where(Model.aliases.contains(name), Model.city_id == city_id)
|
||||
).all()
|
||||
for city_object in city_objects:
|
||||
aliases = city_object[0].aliases.replace('{', '').replace('}', '').split(',')
|
||||
for alias in aliases:
|
||||
if alias == name:
|
||||
# force the name as the alias
|
||||
city_object[0].name = name
|
||||
return city_object[0]
|
||||
return None
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while fetching city object by name and city: %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
except IndexError as err:
|
||||
logging.error('Error while fetching city object by name and city, empty result %s', err)
|
||||
raise IndexError from err
|
||||
|
||||
def get_by_name_or_alias_in_cities(self, name, city_ids) -> Model:
|
||||
"""
|
||||
Fetch a city object based on name and city ids
|
||||
:param name: city object name
|
||||
:param city_ids: a list of city identifiers
|
||||
:return: [CityObject] with the provided name or alias belonging to the city with id city_id
|
||||
"""
|
||||
try:
|
||||
# search by name first
|
||||
with Session(self.engine) as session:
|
||||
city_object = session.execute(select(Model).where(Model.name == name, Model.city_id.in_(tuple(city_ids)))).first()
|
||||
if city_object is not None:
|
||||
return city_object[0]
|
||||
# name not found, so search by alias instead
|
||||
city_objects = session.execute(
|
||||
select(Model).where(Model.aliases.contains(name), Model.city_id.in_(tuple(city_ids)))
|
||||
).all()
|
||||
for city_object in city_objects:
|
||||
aliases = city_object[0].aliases.replace('{', '').replace('}', '').split(',')
|
||||
for alias in aliases:
|
||||
|
@ -10,6 +10,7 @@ import logging
|
||||
from sqlalchemy import or_
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from hub.persistence.repository import Repository
|
||||
from hub.persistence.models import City
|
||||
@ -52,11 +53,12 @@ class SimulationResults(Repository):
|
||||
values=values,
|
||||
city_id=city_id,
|
||||
city_object_id=city_object_id)
|
||||
self.session.add(simulation_result)
|
||||
self.session.flush()
|
||||
self.session.commit()
|
||||
self.session.refresh(simulation_result)
|
||||
return simulation_result.id
|
||||
with Session(self.engine) as session:
|
||||
session.add(simulation_result)
|
||||
session.flush()
|
||||
session.commit()
|
||||
session.refresh(simulation_result)
|
||||
return simulation_result.id
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('An error occurred while creating city_object %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
@ -71,22 +73,23 @@ class SimulationResults(Repository):
|
||||
:return: None
|
||||
"""
|
||||
try:
|
||||
if city_id is not None:
|
||||
self.session.query(Model).filter(Model.name == name, Model.city_id == city_id).update(
|
||||
with Session(self.engine) as session:
|
||||
if city_id is not None:
|
||||
session.query(Model).filter(Model.name == name, Model.city_id == city_id).update(
|
||||
{
|
||||
'values': values,
|
||||
'updated': datetime.datetime.utcnow()
|
||||
})
|
||||
self.session.commit()
|
||||
elif city_object_id is not None:
|
||||
self.session.query(Model).filter(Model.name == name, Model.city_object_id == city_object_id).update(
|
||||
{
|
||||
'values': values,
|
||||
'updated': datetime.datetime.utcnow()
|
||||
})
|
||||
self.session.commit()
|
||||
else:
|
||||
raise NotImplementedError('Missing either city_id or city_object_id')
|
||||
session.commit()
|
||||
elif city_object_id is not None:
|
||||
session.query(Model).filter(Model.name == name, Model.city_object_id == city_object_id).update(
|
||||
{
|
||||
'values': values,
|
||||
'updated': datetime.datetime.utcnow()
|
||||
})
|
||||
session.commit()
|
||||
else:
|
||||
raise NotImplementedError('Missing either city_id or city_object_id')
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while updating city object %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
@ -100,14 +103,15 @@ class SimulationResults(Repository):
|
||||
:return: None
|
||||
"""
|
||||
try:
|
||||
if city_id is not None:
|
||||
self.session.query(Model).filter(Model.name == name, Model.city_id == city_id).delete()
|
||||
self.session.commit()
|
||||
elif city_object_id is not None:
|
||||
self.session.query(Model).filter(Model.name == name, Model.city_object_id == city_object_id).delete()
|
||||
self.session.commit()
|
||||
else:
|
||||
raise NotImplementedError('Missing either city_id or city_object_id')
|
||||
with Session(self.engine) as session:
|
||||
if city_id is not None:
|
||||
session.query(Model).filter(Model.name == name, Model.city_id == city_id).delete()
|
||||
session.commit()
|
||||
elif city_object_id is not None:
|
||||
session.query(Model).filter(Model.name == name, Model.city_object_id == city_object_id).delete()
|
||||
session.commit()
|
||||
else:
|
||||
raise NotImplementedError('Missing either city_id or city_object_id')
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while deleting application: %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
@ -119,7 +123,8 @@ class SimulationResults(Repository):
|
||||
:return: [City] with the provided city_id
|
||||
"""
|
||||
try:
|
||||
return self.session.execute(select(City).where(City.id == city_id)).first()
|
||||
with Session(self.engine) as session:
|
||||
return session.execute(select(City).where(City.id == city_id)).first()
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while fetching city by city_id: %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
@ -131,7 +136,8 @@ class SimulationResults(Repository):
|
||||
:return: [CityObject] with the provided city_object_id
|
||||
"""
|
||||
try:
|
||||
return self.session.execute(select(CityObject).where(CityObject.id == city_object_id)).first()
|
||||
with Session(self.engine) as session:
|
||||
return session.execute(select(CityObject).where(CityObject.id == city_object_id)).first()
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while fetching city by city_id: %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
@ -145,18 +151,43 @@ class SimulationResults(Repository):
|
||||
:return: [SimulationResult]
|
||||
"""
|
||||
try:
|
||||
result_set = self.session.execute(select(Model).where(or_(
|
||||
Model.city_id == city_id,
|
||||
Model.city_object_id == city_object_id
|
||||
)))
|
||||
results = [r[0] for r in result_set]
|
||||
if not result_names:
|
||||
return results
|
||||
filtered_results = []
|
||||
for result in results:
|
||||
if result.name in result_names:
|
||||
filtered_results.append(result)
|
||||
return filtered_results
|
||||
with Session(self.engine) as session:
|
||||
result_set = session.execute(select(Model).where(or_(
|
||||
Model.city_id == city_id,
|
||||
Model.city_object_id == city_object_id
|
||||
)))
|
||||
results = [r[0] for r in result_set]
|
||||
if not result_names:
|
||||
return results
|
||||
filtered_results = []
|
||||
for result in results:
|
||||
if result.name in result_names:
|
||||
filtered_results.append(result)
|
||||
return filtered_results
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while fetching city by city_id: %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
|
||||
def get_simulation_results_by_city_object_id_and_names(self, city_object_id, result_names=None) -> [Model]:
|
||||
"""
|
||||
Fetch the simulation results based in the city_object_id with the given names or all
|
||||
:param city_object_id: the city object id
|
||||
:param result_names: if given filter the results
|
||||
:return: [SimulationResult]
|
||||
"""
|
||||
try:
|
||||
with Session(self.engine) as session:
|
||||
result_set = session.execute(select(Model).where(
|
||||
Model.city_object_id == city_object_id
|
||||
))
|
||||
results = [r[0] for r in result_set]
|
||||
if not result_names:
|
||||
return results
|
||||
filtered_results = []
|
||||
for result in results:
|
||||
if result.name in result_names:
|
||||
filtered_results.append(result)
|
||||
return filtered_results
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while fetching city by city_id: %s', err)
|
||||
raise SQLAlchemyError from err
|
@ -9,6 +9,7 @@ import logging
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from hub.helpers.auth import Auth
|
||||
from hub.persistence.repository import Repository
|
||||
@ -49,10 +50,11 @@ class User(Repository):
|
||||
pass
|
||||
try:
|
||||
user = Model(name=name, password=Auth.hash_password(password), role=role, application_id=application_id)
|
||||
self.session.add(user)
|
||||
self.session.flush()
|
||||
self.session.commit()
|
||||
self.session.refresh(user)
|
||||
with Session(self.engine) as session:
|
||||
session.add(user)
|
||||
session.flush()
|
||||
session.commit()
|
||||
session.refresh(user)
|
||||
return user.id
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('An error occurred while creating user %s', err)
|
||||
@ -68,13 +70,14 @@ class User(Repository):
|
||||
:return: None
|
||||
"""
|
||||
try:
|
||||
self.session.query(Model).filter(Model.id == user_id).update({
|
||||
'name': name,
|
||||
'password': Auth.hash_password(password),
|
||||
'role': role,
|
||||
'updated': datetime.datetime.utcnow()
|
||||
})
|
||||
self.session.commit()
|
||||
with Session(self.engine) as session:
|
||||
session.query(Model).filter(Model.id == user_id).update({
|
||||
'name': name,
|
||||
'password': Auth.hash_password(password),
|
||||
'role': role,
|
||||
'updated': datetime.datetime.utcnow()
|
||||
})
|
||||
session.commit()
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while updating user: %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
@ -86,8 +89,9 @@ class User(Repository):
|
||||
:return: None
|
||||
"""
|
||||
try:
|
||||
self.session.query(Model).filter(Model.id == user_id).delete()
|
||||
self.session.commit()
|
||||
with Session(self.engine) as session:
|
||||
session.query(Model).filter(Model.id == user_id).delete()
|
||||
session.commit()
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while fetching user: %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
@ -100,10 +104,12 @@ class User(Repository):
|
||||
:return: User matching the search criteria or None
|
||||
"""
|
||||
try:
|
||||
user = self.session.execute(
|
||||
select(Model).where(Model.name == name, Model.application_id == application_id)
|
||||
).first()
|
||||
return user[0]
|
||||
with Session(self.engine) as session:
|
||||
user = session.execute(
|
||||
select(Model).where(Model.name == name, Model.application_id == application_id)
|
||||
).first()
|
||||
session.commit()
|
||||
return user[0]
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while fetching user by name and application: %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
@ -120,12 +126,13 @@ class User(Repository):
|
||||
:return: User
|
||||
"""
|
||||
try:
|
||||
user = self.session.execute(
|
||||
select(Model).where(Model.name == name, Model.application_id == application_id)
|
||||
).first()
|
||||
if user:
|
||||
if Auth.check_password(password, user[0].password):
|
||||
return user[0]
|
||||
with Session(self.engine) as session:
|
||||
user = session.execute(
|
||||
select(Model).where(Model.name == name, Model.application_id == application_id)
|
||||
).first()
|
||||
if user:
|
||||
if Auth.check_password(password, user[0].password):
|
||||
return user[0]
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while fetching user by name: %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
@ -140,10 +147,11 @@ class User(Repository):
|
||||
:return: User
|
||||
"""
|
||||
try:
|
||||
application = self.session.execute(
|
||||
select(ApplicationModel).where(ApplicationModel.application_uuid == application_uuid)
|
||||
).first()
|
||||
return self.get_by_name_application_id_and_password(name, password, application[0].id)
|
||||
with Session(self.engine) as session:
|
||||
application = session.execute(
|
||||
select(ApplicationModel).where(ApplicationModel.application_uuid == application_uuid)
|
||||
).first()
|
||||
return self.get_by_name_application_id_and_password(name, password, application[0].id)
|
||||
except SQLAlchemyError as err:
|
||||
logging.error('Error while fetching user by name: %s', err)
|
||||
raise SQLAlchemyError from err
|
||||
|
@ -6,7 +6,6 @@ Project Coder Peter Yefi peteryefi@gmail.com
|
||||
"""
|
||||
import logging
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import Session
|
||||
from hub.persistence.configuration import Configuration
|
||||
|
||||
|
||||
@ -19,6 +18,5 @@ class Repository:
|
||||
try:
|
||||
self.configuration = Configuration(db_name, dotenv_path, app_env)
|
||||
self.engine = create_engine(self.configuration.connection_string)
|
||||
self.session = Session(self.engine)
|
||||
except ValueError as err:
|
||||
logging.error('Missing value for credentials: %s', err)
|
||||
|
@ -1,4 +1,4 @@
|
||||
"""
|
||||
Hub version number
|
||||
"""
|
||||
__version__ = '0.1.8.11'
|
||||
__version__ = '0.1.8.39'
|
||||
|
1
setup.py
1
setup.py
@ -100,6 +100,7 @@ setup(
|
||||
('hub/data/geolocation', glob.glob('hub/data/geolocation/*.txt')),
|
||||
('hub/data/greenery', glob.glob('hub/data/greenery/*.xml')),
|
||||
('hub/data/usage', glob.glob('hub/data/usage/*.xml')),
|
||||
('hub/data/usage', glob.glob('hub/data/usage/*.json')),
|
||||
('hub/data/usage', glob.glob('hub/data/usage/*.xlsx')),
|
||||
('hub/data/weather', glob.glob('hub/data/weather/*.dat')),
|
||||
('hub/data/weather/epw', glob.glob('hub/data/weather/epw/*.epw')),
|
||||
|
@ -6,7 +6,6 @@ Project Coder Peter Yefi peteryefi@gmail.com
|
||||
"""
|
||||
import distutils.spawn
|
||||
import glob
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
@ -103,16 +102,16 @@ class Control:
|
||||
app_env='TEST',
|
||||
dotenv_path=dotenv_path)
|
||||
|
||||
self._application_uuid = '60b7fc1b-f389-4254-9ffd-22a4cf32c7a3'
|
||||
self._application_uuid = 'b9e0ce80-1218-410c-8a64-9d9b7026aad8'
|
||||
self._application_id = 1
|
||||
self._user_id = 1
|
||||
|
||||
self._application_id = self._database.persist_application(
|
||||
'City_layers',
|
||||
'City layers test user',
|
||||
'test',
|
||||
'test',
|
||||
self.application_uuid
|
||||
)
|
||||
self._user_id = self._database.create_user('city_layers', self._application_id, 'city_layers', UserRoles.Admin)
|
||||
self._user_id = self._database.create_user('test', self._application_id, 'test', UserRoles.Admin)
|
||||
|
||||
self._pickle_path = Path('tests_data/pickle_path.bz2').resolve()
|
||||
|
||||
@ -248,36 +247,36 @@ TestDBFactory
|
||||
for x in building.onsite_electrical_production[cte.MONTH]]
|
||||
yearly_on_site_electrical_production = [x * cte.WATTS_HOUR_TO_JULES
|
||||
for x in building.onsite_electrical_production[cte.YEAR]]
|
||||
results = json.dumps({cte.INSEL_MEB: [
|
||||
{'monthly_cooling_peak_load': monthly_cooling_peak_load},
|
||||
{'yearly_cooling_peak_load': yearly_cooling_peak_load},
|
||||
{'monthly_heating_peak_load': monthly_heating_peak_load},
|
||||
{'yearly_heating_peak_load': yearly_heating_peak_load},
|
||||
{'monthly_lighting_peak_load': monthly_lighting_peak_load},
|
||||
{'yearly_lighting_peak_load': yearly_lighting_peak_load},
|
||||
{'monthly_appliances_peak_load': monthly_appliances_peak_load},
|
||||
{'yearly_appliances_peak_load': yearly_appliances_peak_load},
|
||||
{'monthly_cooling_demand': monthly_cooling_demand},
|
||||
{'yearly_cooling_demand': yearly_cooling_demand},
|
||||
{'monthly_heating_demand': monthly_heating_demand},
|
||||
{'yearly_heating_demand': yearly_heating_demand},
|
||||
{'monthly_lighting_electrical_demand': monthly_lighting_electrical_demand},
|
||||
{'yearly_lighting_electrical_demand': yearly_lighting_electrical_demand},
|
||||
{'monthly_appliances_electrical_demand': monthly_appliances_electrical_demand},
|
||||
{'yearly_appliances_electrical_demand': yearly_appliances_electrical_demand},
|
||||
{'monthly_domestic_hot_water_heat_demand': monthly_domestic_hot_water_heat_demand},
|
||||
{'yearly_domestic_hot_water_heat_demand': yearly_domestic_hot_water_heat_demand},
|
||||
{'monthly_heating_consumption': monthly_heating_consumption},
|
||||
{'yearly_heating_consumption': yearly_heating_consumption},
|
||||
{'monthly_cooling_consumption': monthly_cooling_consumption},
|
||||
{'yearly_cooling_consumption': yearly_cooling_consumption},
|
||||
{'monthly_domestic_hot_water_consumption': monthly_domestic_hot_water_consumption},
|
||||
{'yearly_domestic_hot_water_consumption': yearly_domestic_hot_water_consumption},
|
||||
{'monthly_distribution_systems_electrical_consumption': monthly_distribution_systems_electrical_consumption},
|
||||
{'yearly_distribution_systems_electrical_consumption': yearly_distribution_systems_electrical_consumption},
|
||||
{'monthly_on_site_electrical_production': monthly_on_site_electrical_production},
|
||||
{'yearly_on_site_electrical_production': yearly_on_site_electrical_production}
|
||||
]})
|
||||
results = {cte.INSEL_MEB: {
|
||||
'monthly_cooling_peak_load': monthly_cooling_peak_load,
|
||||
'yearly_cooling_peak_load': yearly_cooling_peak_load,
|
||||
'monthly_heating_peak_load': monthly_heating_peak_load,
|
||||
'yearly_heating_peak_load': yearly_heating_peak_load,
|
||||
'monthly_lighting_peak_load': monthly_lighting_peak_load,
|
||||
'yearly_lighting_peak_load': yearly_lighting_peak_load,
|
||||
'monthly_appliances_peak_load': monthly_appliances_peak_load,
|
||||
'yearly_appliances_peak_load': yearly_appliances_peak_load,
|
||||
'monthly_cooling_demand': monthly_cooling_demand,
|
||||
'yearly_cooling_demand': yearly_cooling_demand,
|
||||
'monthly_heating_demand': monthly_heating_demand,
|
||||
'yearly_heating_demand': yearly_heating_demand,
|
||||
'monthly_lighting_electrical_demand': monthly_lighting_electrical_demand,
|
||||
'yearly_lighting_electrical_demand': yearly_lighting_electrical_demand,
|
||||
'monthly_appliances_electrical_demand': monthly_appliances_electrical_demand,
|
||||
'yearly_appliances_electrical_demand': yearly_appliances_electrical_demand,
|
||||
'monthly_domestic_hot_water_heat_demand': monthly_domestic_hot_water_heat_demand,
|
||||
'yearly_domestic_hot_water_heat_demand': yearly_domestic_hot_water_heat_demand,
|
||||
'monthly_heating_consumption': monthly_heating_consumption,
|
||||
'yearly_heating_consumption': yearly_heating_consumption,
|
||||
'monthly_cooling_consumption': monthly_cooling_consumption,
|
||||
'yearly_cooling_consumption': yearly_cooling_consumption,
|
||||
'monthly_domestic_hot_water_consumption': monthly_domestic_hot_water_consumption,
|
||||
'yearly_domestic_hot_water_consumption': yearly_domestic_hot_water_consumption,
|
||||
'monthly_distribution_systems_electrical_consumption': monthly_distribution_systems_electrical_consumption,
|
||||
'yearly_distribution_systems_electrical_consumption': yearly_distribution_systems_electrical_consumption,
|
||||
'monthly_on_site_electrical_production': monthly_on_site_electrical_production,
|
||||
'yearly_on_site_electrical_production': yearly_on_site_electrical_production
|
||||
}}
|
||||
|
||||
db_building_id = _building.id
|
||||
city_objects_id.append(db_building_id)
|
||||
@ -286,6 +285,8 @@ TestDBFactory
|
||||
results, city_object_id=db_building_id)
|
||||
self.assertEqual(17, len(city_objects_id), 'wrong number of results')
|
||||
self.assertIsNotNone(city_objects_id[0], 'city_object_id is None')
|
||||
results = control.database.results(control.user_id, control.application_id, request_values)
|
||||
|
||||
for _id in city_objects_id:
|
||||
control.database.delete_results_by_name('insel meb', city_object_id=_id)
|
||||
control.database.delete_city(city_id)
|
||||
|
@ -5,11 +5,8 @@ Copyright © 2022 Concordia CERC group
|
||||
Project Coder Peter Yefi peteryefi@gmail.com
|
||||
"""
|
||||
import distutils.spawn
|
||||
import glob
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest import TestCase
|
||||
@ -18,19 +15,7 @@ import sqlalchemy.exc
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.exc import ProgrammingError
|
||||
|
||||
import hub.helpers.constants as cte
|
||||
from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
|
||||
from hub.exports.exports_factory import ExportsFactory
|
||||
from hub.helpers.data.montreal_function_to_hub_function import MontrealFunctionToHubFunction
|
||||
from hub.imports.construction_factory import ConstructionFactory
|
||||
from hub.imports.energy_systems_factory import EnergySystemsFactory
|
||||
from hub.imports.geometry_factory import GeometryFactory
|
||||
from hub.imports.results_factory import ResultFactory
|
||||
from hub.imports.usage_factory import UsageFactory
|
||||
from hub.imports.weather_factory import WeatherFactory
|
||||
from hub.persistence.db_control import DBControl
|
||||
from hub.persistence.models import City, Application, CityObject, SimulationResults
|
||||
from hub.persistence.models import User, UserRoles
|
||||
from hub.persistence.repository import Repository
|
||||
|
||||
|
||||
@ -129,24 +114,12 @@ TestDBFactory
|
||||
"""
|
||||
|
||||
@unittest.skipIf(control.skip_test, control.skip_reason)
|
||||
def test_retrieve_results(self):
|
||||
def test_buildings_info(self):
|
||||
request_values = {
|
||||
"scenarios": [
|
||||
{
|
||||
"current status": ["01002777", "01002773", "01036804"]
|
||||
},
|
||||
{
|
||||
"skin retrofit": ["01002777", "01002773", "01036804"]
|
||||
},
|
||||
{
|
||||
"system retrofit and pv": ["01002777", "01002773", "01036804"]
|
||||
},
|
||||
{
|
||||
"skin and system retrofit with pv": ["01002777", "01002773", "01036804"]
|
||||
}
|
||||
|
||||
|
||||
"buildings": [
|
||||
"01002777", "01002773", "01036804"
|
||||
]
|
||||
}
|
||||
results = control.database.results(control.user_id, control.application_id, request_values)
|
||||
results = control.database.buildings_info(control.user_id, control.application_id, request_values)
|
||||
self.assertEqual(12, len(results), 'wrong number of results')
|
||||
print(results)
|
||||
|
@ -5,19 +5,20 @@ Copyright © 2022 Concordia CERC group
|
||||
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||
"""
|
||||
|
||||
import logging.handlers
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from unittest import TestCase
|
||||
from hub.imports.geometry_factory import GeometryFactory
|
||||
from hub.helpers.dictionaries import Dictionaries
|
||||
from hub.imports.construction_factory import ConstructionFactory
|
||||
from hub.imports.usage_factory import UsageFactory
|
||||
from hub.imports.weather_factory import WeatherFactory
|
||||
from hub.exports.exports_factory import ExportsFactory
|
||||
from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
|
||||
|
||||
import hub.helpers.constants as cte
|
||||
from hub.city_model_structure.city import City
|
||||
from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
|
||||
from hub.exports.exports_factory import ExportsFactory
|
||||
from hub.helpers.dictionaries import Dictionaries
|
||||
from hub.imports.construction_factory import ConstructionFactory
|
||||
from hub.imports.geometry_factory import GeometryFactory
|
||||
from hub.imports.usage_factory import UsageFactory
|
||||
from hub.imports.weather_factory import WeatherFactory
|
||||
|
||||
|
||||
class TestExports(TestCase):
|
||||
@ -66,7 +67,7 @@ class TestExports(TestCase):
|
||||
|
||||
def _export(self, export_type, from_pickle=False):
|
||||
self._complete_city = self._get_complete_city(from_pickle)
|
||||
ExportsFactory(export_type, self._complete_city, self._output_path).export()
|
||||
ExportsFactory(export_type, self._complete_city, self._output_path, base_uri='../glb').export()
|
||||
|
||||
def _export_building_energy(self, export_type, from_pickle=False):
|
||||
self._complete_city = self._get_complete_city(from_pickle)
|
||||
@ -78,6 +79,39 @@ class TestExports(TestCase):
|
||||
"""
|
||||
self._export('obj', False)
|
||||
|
||||
def test_cesiumjs_tileset_export(self):
|
||||
"""
|
||||
export to cesiumjs tileset
|
||||
"""
|
||||
self._export('cesiumjs_tileset', False)
|
||||
tileset = Path(self._output_path / f'{self._city.name}.json')
|
||||
self.assertTrue(tileset.exists())
|
||||
with open(tileset, 'r') as f:
|
||||
json_tileset = json.load(f)
|
||||
self.assertEqual(1, len(json_tileset['root']['children']), "Wrong number of children")
|
||||
|
||||
def test_glb_export(self):
|
||||
"""
|
||||
export to glb format
|
||||
"""
|
||||
self._export('glb', False)
|
||||
for building in self._city.buildings:
|
||||
glb_file = Path(self._output_path / f'{building.name}.glb')
|
||||
self.assertTrue(glb_file.exists(), f'{building.name} Building glb wasn\'t correctly generated')
|
||||
|
||||
def test_geojson_export(self):
|
||||
self._export('geojson', False)
|
||||
geojson_file = Path(self._output_path / f'{self._city.name}.geojson')
|
||||
self.assertTrue(geojson_file.exists(), f'{geojson_file} doesn\'t exists')
|
||||
with open(geojson_file, 'r') as f:
|
||||
geojson = json.load(f)
|
||||
self.assertEqual(1, len(geojson['features']), 'Wrong number of buildings')
|
||||
geometry = geojson['features'][0]['geometry']
|
||||
self.assertEqual('Polygon', geometry['type'], 'Wrong geometry type')
|
||||
self.assertEqual(1, len(geometry['coordinates']), 'Wrong polygon structure')
|
||||
self.assertEqual(11, len(geometry['coordinates'][0]), 'Wrong number of vertices')
|
||||
os.unlink(geojson_file) # todo: this test need to cover a multipolygon example too
|
||||
|
||||
def test_energy_ade_export(self):
|
||||
"""
|
||||
export to energy ADE
|
||||
@ -109,9 +143,7 @@ class TestExports(TestCase):
|
||||
EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
|
||||
UsageFactory('nrcan', city).enrich()
|
||||
WeatherFactory('epw', city).enrich()
|
||||
print(self._output_path)
|
||||
try:
|
||||
EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
|
||||
except Exception:
|
||||
self.fail("Idf ExportsFactory raised ExceptionType unexpectedly!")
|
||||
|
||||
|
142
texttest
Normal file
142
texttest
Normal file
@ -0,0 +1,142 @@
|
||||
ZoneControl:Thermostat,
|
||||
Room_180_7ad8616b Thermostat, !- Name
|
||||
Room_180_7ad8616b, !- Zone or ZoneList Name
|
||||
Room_180_7ad8616b Thermostat Schedule, !- Control Type Schedule Name
|
||||
ThermostatSetpoint:DualSetpoint, !- Control 1 Object Type
|
||||
LargeOffice Building_Setpoint 26, !- Control 1 Name
|
||||
, !- Control 2 Object Type
|
||||
, !- Control 2 Name
|
||||
, !- Control 3 Object Type
|
||||
, !- Control 3 Name
|
||||
, !- Control 4 Object Type
|
||||
, !- Control 4 Name
|
||||
0; !- Temperature Difference Between Cutout And Setpoint {deltaC}
|
||||
|
||||
Schedule:Compact,
|
||||
Room_180_7ad8616b Thermostat Schedule, !- Name
|
||||
Room_180_7ad8616b Thermostat Schedule Type Limits, !- Schedule Type Limits Name
|
||||
Through: 12/31, !- Field 1
|
||||
For: AllDays, !- Field 2
|
||||
Until: 24:00, !- Field 3
|
||||
4; !- Field 4
|
||||
|
||||
ScheduleTypeLimits,
|
||||
Room_180_7ad8616b Thermostat Schedule Type Limits, !- Name
|
||||
0, !- Lower Limit Value {BasedOnField A3}
|
||||
4, !- Upper Limit Value {BasedOnField A3}
|
||||
DISCRETE; !- Numeric Type
|
||||
|
||||
ThermostatSetpoint:DualSetpoint,
|
||||
LargeOffice Building_Setpoint 26, !- Name
|
||||
LargeOffice Building_Setpoint_HtgSetp Schedule, !- Heating Setpoint Temperature Schedule Name
|
||||
LargeOffice Building_Setpoint_ClgSetp Schedule; !- Cooling Setpoint Temperature Schedule Name
|
||||
|
||||
ZoneHVAC:EquipmentConnections,
|
||||
Room_180_7ad8616b, !- Zone Name
|
||||
Room_180_7ad8616b Equipment List, !- Zone Conditioning Equipment List Name
|
||||
Room_180_7ad8616b Inlet Node List, !- Zone Air Inlet Node or NodeList Name
|
||||
, !- Zone Air Exhaust Node or NodeList Name
|
||||
Node 27, !- Zone Air Node Name
|
||||
Room_180_7ad8616b Return Node List; !- Zone Return Air Node or NodeList Name
|
||||
|
||||
NodeList,
|
||||
Room_180_7ad8616b Inlet Node List, !- Name
|
||||
Node 305; !- Node Name 1
|
||||
|
||||
NodeList,
|
||||
Room_180_7ad8616b Return Node List, !- Name
|
||||
Node 308; !- Node Name 1
|
||||
|
||||
ZoneHVAC:Baseboard:Convective:Electric,
|
||||
Elec Baseboard 1, !- Name
|
||||
Always On Discrete hvac_library, !- Availability Schedule Name
|
||||
, !- Heating Design Capacity Method
|
||||
Autosize, !- Heating Design Capacity {W}
|
||||
, !- Heating Design Capacity Per Floor Area {W/m2}
|
||||
, !- Fraction of Autosized Heating Design Capacity
|
||||
1; !- Efficiency
|
||||
|
||||
AirTerminal:SingleDuct:ConstantVolume:NoReheat,
|
||||
Diffuser 21, !- Name
|
||||
Always On Discrete hvac_library, !- Availability Schedule Name
|
||||
Node 307, !- Air Inlet Node Name
|
||||
Node 305, !- Air Outlet Node Name
|
||||
AutoSize; !- Maximum Air Flow Rate {m3/s}
|
||||
|
||||
ZoneHVAC:AirDistributionUnit,
|
||||
ADU Diffuser 21, !- Name
|
||||
Node 305, !- Air Distribution Unit Outlet Node Name
|
||||
AirTerminal:SingleDuct:ConstantVolume:NoReheat, !- Air Terminal Object Type
|
||||
Diffuser 21; !- Air Terminal Name
|
||||
|
||||
ZoneHVAC:EquipmentList,
|
||||
Room_180_7ad8616b Equipment List, !- Name
|
||||
SequentialLoad, !- Load Distribution Scheme
|
||||
ZoneHVAC:Baseboard:Convective:Electric, !- Zone Equipment Object Type 1
|
||||
Elec Baseboard 1, !- Zone Equipment Name 1
|
||||
1, !- Zone Equipment Cooling Sequence 1
|
||||
1, !- Zone Equipment Heating or No-Load Sequence 1
|
||||
, !- Zone Equipment Sequential Cooling Fraction Schedule Name 1
|
||||
, !- Zone Equipment Sequential Heating Fraction Schedule Name 1
|
||||
ZoneHVAC:AirDistributionUnit, !- Zone Equipment Object Type 2
|
||||
ADU Diffuser 21, !- Zone Equipment Name 2
|
||||
2, !- Zone Equipment Cooling Sequence 2
|
||||
2, !- Zone Equipment Heating or No-Load Sequence 2
|
||||
, !- Zone Equipment Sequential Cooling Fraction Schedule Name 2
|
||||
; !- Zone Equipment Sequential Heating Fraction Schedule Name 2
|
||||
|
||||
Sizing:Zone,
|
||||
Room_180_7ad8616b, !- Zone or ZoneList Name
|
||||
SupplyAirTemperature, !- Zone Cooling Design Supply Air Temperature Input Method
|
||||
14, !- Zone Cooling Design Supply Air Temperature {C}
|
||||
11.11, !- Zone Cooling Design Supply Air Temperature Difference {deltaC}
|
||||
SupplyAirTemperature, !- Zone Heating Design Supply Air Temperature Input Method
|
||||
40, !- Zone Heating Design Supply Air Temperature {C}
|
||||
11.11, !- Zone Heating Design Supply Air Temperature Difference {deltaC}
|
||||
0.0085, !- Zone Cooling Design Supply Air Humidity Ratio {kgWater/kgDryAir}
|
||||
0.008, !- Zone Heating Design Supply Air Humidity Ratio {kgWater/kgDryAir}
|
||||
Room_180_7ad8616b DSOA Space List, !- Design Specification Outdoor Air Object Name
|
||||
, !- Zone Heating Sizing Factor
|
||||
, !- Zone Cooling Sizing Factor
|
||||
DesignDay, !- Cooling Design Air Flow Method
|
||||
0, !- Cooling Design Air Flow Rate {m3/s}
|
||||
0.000762, !- Cooling Minimum Air Flow per Zone Floor Area {m3/s-m2}
|
||||
0, !- Cooling Minimum Air Flow {m3/s}
|
||||
0, !- Cooling Minimum Air Flow Fraction
|
||||
DesignDay, !- Heating Design Air Flow Method
|
||||
0, !- Heating Design Air Flow Rate {m3/s}
|
||||
0.002032, !- Heating Maximum Air Flow per Zone Floor Area {m3/s-m2}
|
||||
0.1415762, !- Heating Maximum Air Flow {m3/s}
|
||||
0.3, !- Heating Maximum Air Flow Fraction
|
||||
, !- Design Specification Zone Air Distribution Object Name
|
||||
No, !- Account for Dedicated Outdoor Air System
|
||||
, !- Dedicated Outdoor Air System Control Strategy
|
||||
, !- Dedicated Outdoor Air Low Setpoint Temperature for Design {C}
|
||||
, !- Dedicated Outdoor Air High Setpoint Temperature for Design {C}
|
||||
Sensible Load Only No Latent Load, !- Zone Load Sizing Method
|
||||
HumidityRatioDifference, !- Zone Latent Cooling Design Supply Air Humidity Ratio Input Method
|
||||
, !- Zone Dehumidification Design Supply Air Humidity Ratio {kgWater/kgDryAir}
|
||||
0.005, !- Zone Cooling Design Supply Air Humidity Ratio Difference {kgWater/kgDryAir}
|
||||
HumidityRatioDifference, !- Zone Latent Heating Design Supply Air Humidity Ratio Input Method
|
||||
, !- Zone Humidification Design Supply Air Humidity Ratio {kgWater/kgDryAir}
|
||||
0.005; !- Zone Humidification Design Supply Air Humidity Ratio Difference {kgWater/kgDryAir}
|
||||
|
||||
DesignSpecification:OutdoorAir:SpaceList,
|
||||
Room_180_7ad8616b DSOA Space List, !- Name
|
||||
Room_180_7ad8616b_Space, !- Space Name 1
|
||||
MidriseApartment Apartment Ventilation; !- Space Design Specification Outdoor Air Object Name 1
|
||||
|
||||
Zone,
|
||||
Room_181_3a411b5d, !- Name
|
||||
, !- Direction of Relative North {deg}
|
||||
0, !- X Origin {m}
|
||||
0, !- Y Origin {m}
|
||||
0, !- Z Origin {m}
|
||||
, !- Type
|
||||
1, !- Multiplier
|
||||
4, !- Ceiling Height {m}
|
||||
291.62935408288, !- Volume {m3}
|
||||
, !- Floor Area {m2}
|
||||
, !- Zone Inside Convection Algorithm
|
||||
, !- Zone Outside Convection Algorithm
|
||||
Yes; !- Part of Total Floor Area
|
Loading…
Reference in New Issue
Block a user