Quality improvements
This commit is contained in:
parent
58066a45cc
commit
312a9c34be
|
@ -554,7 +554,7 @@ class Polygon:
|
||||||
def _order_points(edges_list):
|
def _order_points(edges_list):
|
||||||
# todo: not sure that this method works for any case -> RECHECK
|
# todo: not sure that this method works for any case -> RECHECK
|
||||||
points = edges_list[0]
|
points = edges_list[0]
|
||||||
for j in range(0, len(points)):
|
for _ in range(0, len(points)):
|
||||||
for i in range(1, len(edges_list)):
|
for i in range(1, len(edges_list)):
|
||||||
point_1 = edges_list[i][0]
|
point_1 = edges_list[i][0]
|
||||||
point_2 = points[len(points)-1]
|
point_2 = points[len(points)-1]
|
||||||
|
|
|
@ -5,14 +5,11 @@ Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from os.path import exists
|
|
||||||
|
|
||||||
from exports.formats.stl import Stl
|
|
||||||
from exports.formats.obj import Obj
|
|
||||||
from exports.formats.energy_ade import EnergyAde
|
from exports.formats.energy_ade import EnergyAde
|
||||||
from exports.formats.simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm
|
|
||||||
from exports.formats.idf import Idf
|
from exports.formats.idf import Idf
|
||||||
|
from exports.formats.obj import Obj
|
||||||
|
from exports.formats.simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm
|
||||||
|
from exports.formats.stl import Stl
|
||||||
|
|
||||||
|
|
||||||
class ExportsFactory:
|
class ExportsFactory:
|
||||||
|
@ -73,7 +70,7 @@ class ExportsFactory:
|
||||||
idf_data_path = (Path(__file__).parent / './formats/idf_files/').resolve()
|
idf_data_path = (Path(__file__).parent / './formats/idf_files/').resolve()
|
||||||
# todo: create a get epw file function based on the city
|
# todo: create a get epw file function based on the city
|
||||||
weather_path = (Path(__file__).parent / '../data/weather/epw/CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').resolve()
|
weather_path = (Path(__file__).parent / '../data/weather/epw/CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').resolve()
|
||||||
Idf(self._city, self._path, (idf_data_path / f'Minimal.idf'), (idf_data_path / f'Energy+.idd'), weather_path)
|
return Idf(self._city, self._path, (idf_data_path / 'Minimal.idf'), (idf_data_path / 'Energy+.idd'), weather_path)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _sra(self):
|
def _sra(self):
|
||||||
|
@ -85,10 +82,3 @@ class ExportsFactory:
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
return getattr(self, self._export_type, lambda: None)
|
return getattr(self, self._export_type, lambda: None)
|
||||||
|
|
||||||
def _debug_export(self):
|
|
||||||
"""
|
|
||||||
Export the city model structure to the given export type
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
self._idf()
|
|
||||||
|
|
|
@ -3,47 +3,50 @@ ExportsFactory export a city into several formats
|
||||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
"""
|
"""
|
||||||
import xmltodict
|
|
||||||
import uuid
|
import uuid
|
||||||
import datetime
|
import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import xmltodict
|
||||||
import helpers.constants as cte
|
import helpers.constants as cte
|
||||||
|
|
||||||
|
|
||||||
class EnergyAde:
|
class EnergyAde:
|
||||||
|
"""
|
||||||
|
Export the city to citygml + energy ade
|
||||||
|
"""
|
||||||
def __init__(self, city, path):
|
def __init__(self, city, path):
|
||||||
self._city = city
|
self._city = city
|
||||||
self._path = path
|
self._path = path
|
||||||
self._surface_members = None
|
self._surface_members = None
|
||||||
self._export()
|
self._export()
|
||||||
|
|
||||||
|
|
||||||
def _export(self):
|
def _export(self):
|
||||||
energy_ade = {
|
energy_ade = {
|
||||||
'core:CityModel': {
|
'core:CityModel': {
|
||||||
'@xmlns:brid':'http://www.opengis.net/citygml/bridge/2.0',
|
'@xmlns:brid': 'http://www.opengis.net/citygml/bridge/2.0',
|
||||||
'@xmlns:tran':'http://www.opengis.net/citygml/transportation/2.0',
|
'@xmlns:tran': 'http://www.opengis.net/citygml/transportation/2.0',
|
||||||
'@xmlns:frn':'http://www.opengis.net/citygml/cityfurniture/2.0',
|
'@xmlns:frn': 'http://www.opengis.net/citygml/cityfurniture/2.0',
|
||||||
'@xmlns:wtr':'http://www.opengis.net/citygml/waterbody/2.0',
|
'@xmlns:wtr': 'http://www.opengis.net/citygml/waterbody/2.0',
|
||||||
'@xmlns:sch':'http://www.ascc.net/xml/schematron',
|
'@xmlns:sch': 'http://www.ascc.net/xml/schematron',
|
||||||
'@xmlns:veg':'http://www.opengis.net/citygml/vegetation/2.0',
|
'@xmlns:veg': 'http://www.opengis.net/citygml/vegetation/2.0',
|
||||||
'@xmlns:xlink':'http://www.w3.org/1999/xlink',
|
'@xmlns:xlink': 'http://www.w3.org/1999/xlink',
|
||||||
'@xmlns:tun':'http://www.opengis.net/citygml/tunnel/2.0',
|
'@xmlns:tun': 'http://www.opengis.net/citygml/tunnel/2.0',
|
||||||
'@xmlns:tex':'http://www.opengis.net/citygml/texturedsurface/2.0',
|
'@xmlns:tex': 'http://www.opengis.net/citygml/texturedsurface/2.0',
|
||||||
'@xmlns:gml':'http://www.opengis.net/gml',
|
'@xmlns:gml': 'http://www.opengis.net/gml',
|
||||||
'@xmlns:genobj':'http://www.opengis.net/citygml/generics/2.0',
|
'@xmlns:genobj': 'http://www.opengis.net/citygml/generics/2.0',
|
||||||
'@xmlns:dem':'http://www.opengis.net/citygml/relief/2.0',
|
'@xmlns:dem': 'http://www.opengis.net/citygml/relief/2.0',
|
||||||
'@xmlns:app':'http://www.opengis.net/citygml/appearance/2.0',
|
'@xmlns:app': 'http://www.opengis.net/citygml/appearance/2.0',
|
||||||
'@xmlns:luse':'http://www.opengis.net/citygml/landuse/2.0',
|
'@xmlns:luse': 'http://www.opengis.net/citygml/landuse/2.0',
|
||||||
'@xmlns:xAL':'urn:oasis:names:tc:ciq:xsdschema:xAL:2.0',
|
'@xmlns:xAL': 'urn:oasis:names:tc:ciq:xsdschema:xAL:2.0',
|
||||||
'@xmlns:xsi':'http://www.w3.org/2001/XMLSchema-instance',
|
'@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
|
||||||
'@xmlns:smil20lang':'http://www.w3.org/2001/SMIL20/Language',
|
'@xmlns:smil20lang': 'http://www.w3.org/2001/SMIL20/Language',
|
||||||
'@xmlns:pbase':'http://www.opengis.net/citygml/profiles/base/2.0',
|
'@xmlns:pbase': 'http://www.opengis.net/citygml/profiles/base/2.0',
|
||||||
'@xmlns:smil20': 'http://www.w3.org/2001/SMIL20/',
|
'@xmlns:smil20': 'http://www.w3.org/2001/SMIL20/',
|
||||||
'@xmlns:bldg':'http://www.opengis.net/citygml/building/2.0',
|
'@xmlns:bldg': 'http://www.opengis.net/citygml/building/2.0',
|
||||||
'@xmlns:energy': "http://www.sig3d.org/citygml/2.0/energy/1.0",
|
'@xmlns:energy': "http://www.sig3d.org/citygml/2.0/energy/1.0",
|
||||||
'@xmlns:core':'http://www.opengis.net/citygml/2.0',
|
'@xmlns:core': 'http://www.opengis.net/citygml/2.0',
|
||||||
'@xmlns:grp':'http://www.opengis.net/citygml/cityobjectgroup/2.0',
|
'@xmlns:grp': 'http://www.opengis.net/citygml/cityobjectgroup/2.0',
|
||||||
'gml:boundedBy': {
|
'gml:boundedBy': {
|
||||||
'gml:Envelope': {
|
'gml:Envelope': {
|
||||||
'@srsName': self._city.srs_name,
|
'@srsName': self._city.srs_name,
|
||||||
|
@ -68,7 +71,7 @@ class EnergyAde:
|
||||||
building_dic = self._building_geometry(building, building_dic, self._city)
|
building_dic = self._building_geometry(building, building_dic, self._city)
|
||||||
building_dic['bldg:Building']['energy:volume'] = {
|
building_dic['bldg:Building']['energy:volume'] = {
|
||||||
'energy:VolumeType': {
|
'energy:VolumeType': {
|
||||||
'energy:type':'grossVolume',
|
'energy:type': 'grossVolume',
|
||||||
'energy:value': {
|
'energy:value': {
|
||||||
'@uom': 'm3',
|
'@uom': 'm3',
|
||||||
'energy:value': building.volume
|
'energy:value': building.volume
|
||||||
|
@ -79,22 +82,22 @@ class EnergyAde:
|
||||||
'gml:Point': {
|
'gml:Point': {
|
||||||
'@srsName': self._city.srs_name,
|
'@srsName': self._city.srs_name,
|
||||||
'@gml:id': f'GML_{uuid.uuid4()}',
|
'@gml:id': f'GML_{uuid.uuid4()}',
|
||||||
'gml:Pos': f'{" ".join(map(str,building.centroid))}'
|
'gml:Pos': f'{" ".join(map(str, building.centroid))}'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
building_dic['bldg:Building']['energy:thermalZone'] = self._thermal_zones(building,self._city)
|
building_dic['bldg:Building']['energy:thermalZone'] = self._thermal_zones(building, self._city)
|
||||||
buildings.append(building_dic)
|
buildings.append(building_dic)
|
||||||
|
|
||||||
energy_ade['core:CityModel']['core:cityObjectMember'] = buildings
|
energy_ade['core:CityModel']['core:cityObjectMember'] = buildings
|
||||||
|
|
||||||
file_name = self._city.name + '_ade.gml'
|
file_name = self._city.name + '_ade.gml'
|
||||||
file_path = Path(self._path / file_name).resolve()
|
file_path = Path(self._path / file_name).resolve()
|
||||||
with open(file_path, 'w' ) as file:
|
with open(file_path, 'w') as file:
|
||||||
file.write(xmltodict.unparse(energy_ade,pretty=True, short_empty_elements=True))
|
file.write(xmltodict.unparse(energy_ade, pretty=True, short_empty_elements=True))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _measures(building, building_dic):
|
def _measures(building, building_dic):
|
||||||
#todo: this method is only for year and insel need to be generalized
|
# todo: this method is only for year and insel need to be generalized
|
||||||
measures = []
|
measures = []
|
||||||
measure = EnergyAde._measure(building.heating, cte.YEAR, 'Energy demand heating', 'INSEL')
|
measure = EnergyAde._measure(building.heating, cte.YEAR, 'Energy demand heating', 'INSEL')
|
||||||
if measure is not None:
|
if measure is not None:
|
||||||
|
@ -128,7 +131,7 @@ class EnergyAde:
|
||||||
'@name': name,
|
'@name': name,
|
||||||
'genobj:value': {
|
'genobj:value': {
|
||||||
'@uom': 'kWh',
|
'@uom': 'kWh',
|
||||||
'#text': ' '.join([str(e/1000) for e in measure_dict[key_value][source]])
|
'#text': ' '.join([str(e / 1000) for e in measure_dict[key_value][source]])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return measure
|
return measure
|
||||||
|
@ -162,7 +165,6 @@ class EnergyAde:
|
||||||
}
|
}
|
||||||
return demand
|
return demand
|
||||||
|
|
||||||
|
|
||||||
def _building_geometry(self, building, building_dic, city):
|
def _building_geometry(self, building, building_dic, city):
|
||||||
|
|
||||||
building_dic['bldg:Building']['bldg:function'] = building.function
|
building_dic['bldg:Building']['bldg:function'] = building.function
|
||||||
|
@ -183,7 +185,6 @@ class EnergyAde:
|
||||||
raise NotImplementedError('Only lod 1 and 2 can be exported')
|
raise NotImplementedError('Only lod 1 and 2 can be exported')
|
||||||
return building_dic
|
return building_dic
|
||||||
|
|
||||||
|
|
||||||
def _lod1(self, building, building_dic, city):
|
def _lod1(self, building, building_dic, city):
|
||||||
raise NotImplementedError('Only lod 1 and 2 can be exported')
|
raise NotImplementedError('Only lod 1 and 2 can be exported')
|
||||||
|
|
||||||
|
@ -197,7 +198,7 @@ class EnergyAde:
|
||||||
'gml:upperCorner': ' '.join([str(e) for e in city.upper_corner])
|
'gml:upperCorner': ' '.join([str(e) for e in city.upper_corner])
|
||||||
}}]
|
}}]
|
||||||
for surface in building.surfaces:
|
for surface in building.surfaces:
|
||||||
surface_member = { '@xlink:href': f'#PolyId{surface.name}'}
|
surface_member = {'@xlink:href': f'#PolyId{surface.name}'}
|
||||||
self._surface_members.append(surface_member)
|
self._surface_members.append(surface_member)
|
||||||
if surface.type == 'Wall':
|
if surface.type == 'Wall':
|
||||||
surface_type = 'bldg:WallSurface'
|
surface_type = 'bldg:WallSurface'
|
||||||
|
@ -272,7 +273,7 @@ class EnergyAde:
|
||||||
'gml:name': f'Thermal zone {index} in {building.name} building',
|
'gml:name': f'Thermal zone {index} in {building.name} building',
|
||||||
'energy:contains': [],
|
'energy:contains': [],
|
||||||
'energy:floorArea': {
|
'energy:floorArea': {
|
||||||
'energy:FloorArea' : {
|
'energy:FloorArea': {
|
||||||
'energy:type': 'grossFloorArea',
|
'energy:type': 'grossFloorArea',
|
||||||
'energy:value': {
|
'energy:value': {
|
||||||
'@uom': 'm2',
|
'@uom': 'm2',
|
||||||
|
@ -285,7 +286,7 @@ class EnergyAde:
|
||||||
'energy:type': 'grossVolume',
|
'energy:type': 'grossVolume',
|
||||||
'energy:value': {
|
'energy:value': {
|
||||||
'@uom': 'm3',
|
'@uom': 'm3',
|
||||||
#todo: for now we have just one thermal zone, therefore is the building volume, this need to be changed
|
# todo: for now we have just one thermal zone, therefore is the building volume, this need to be changed
|
||||||
'#text': f'{building.volume}'
|
'#text': f'{building.volume}'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -373,8 +374,5 @@ class EnergyAde:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
thermal_boundaries.append(thermal_boundary_dic)
|
thermal_boundaries.append(thermal_boundary_dic)
|
||||||
return thermal_boundaries
|
return thermal_boundaries
|
|
@ -4,10 +4,12 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
Copyright © 2020 Project Author Soroush Samareh Abolhassani - soroush.samarehabolhassani@mail.concordia.ca
|
Copyright © 2020 Project Author Soroush Samareh Abolhassani - soroush.samarehabolhassani@mail.concordia.ca
|
||||||
"""
|
"""
|
||||||
from geomeppy import IDF
|
from geomeppy import IDF
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
|
|
||||||
class Idf:
|
class Idf:
|
||||||
|
"""
|
||||||
|
Export city to IDF
|
||||||
|
"""
|
||||||
_THERMOSTAT = 'HVACTEMPLATE:THERMOSTAT'
|
_THERMOSTAT = 'HVACTEMPLATE:THERMOSTAT'
|
||||||
_IDEAL_LOAD_AIR_SYSTEM = 'HVACTEMPLATE:ZONE:IDEALLOADSAIRSYSTEM'
|
_IDEAL_LOAD_AIR_SYSTEM = 'HVACTEMPLATE:ZONE:IDEALLOADSAIRSYSTEM'
|
||||||
_SURFACE = 'BUILDINGSURFACE:DETAILED'
|
_SURFACE = 'BUILDINGSURFACE:DETAILED'
|
||||||
|
@ -269,10 +271,8 @@ class Idf:
|
||||||
for boundary in thermal_zone.thermal_boundaries:
|
for boundary in thermal_zone.thermal_boundaries:
|
||||||
idf_surface_type = self.idf_surfaces[boundary.surface.type]
|
idf_surface_type = self.idf_surfaces[boundary.surface.type]
|
||||||
for usage_zone in thermal_zone.usage_zones:
|
for usage_zone in thermal_zone.usage_zones:
|
||||||
|
|
||||||
surface = self._idf.newidfobject(self._SURFACE, Name=f'{boundary.surface.name}',
|
surface = self._idf.newidfobject(self._SURFACE, Name=f'{boundary.surface.name}',
|
||||||
Surface_Type=idf_surface_type, Zone_Name=usage_zone.id,
|
Surface_Type=idf_surface_type, Zone_Name=usage_zone.id,
|
||||||
Construction_Name=boundary.construction_name)
|
Construction_Name=boundary.construction_name)
|
||||||
coordinates = self._matrix_to_list(boundary.surface.solid_polygon.coordinates)
|
coordinates = self._matrix_to_list(boundary.surface.solid_polygon.coordinates)
|
||||||
surface.setcoords(coordinates)
|
surface.setcoords(coordinates)
|
||||||
|
|
||||||
|
|
|
@ -4,18 +4,23 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
from exports.formats.triangular import Triangular
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from imports.geometry_factory import GeometryFactory
|
|
||||||
import trimesh.exchange.obj
|
import trimesh.exchange.obj
|
||||||
|
from exports.formats.triangular import Triangular
|
||||||
|
from imports.geometry_factory import GeometryFactory
|
||||||
|
|
||||||
|
|
||||||
class Obj(Triangular):
|
class Obj(Triangular):
|
||||||
|
"""
|
||||||
|
Export to obj format
|
||||||
|
"""
|
||||||
def __init__(self, city, path):
|
def __init__(self, city, path):
|
||||||
super().__init__(city, path, 'obj')
|
super().__init__(city, path, 'obj')
|
||||||
|
|
||||||
def to_ground_points(self):
|
def to_ground_points(self):
|
||||||
|
"""
|
||||||
|
Move closer to the origin
|
||||||
|
"""
|
||||||
file_name_in = self._city.name + '.' + self._triangular_format
|
file_name_in = self._city.name + '.' + self._triangular_format
|
||||||
file_name_out = self._city.name + '_ground.' + self._triangular_format
|
file_name_out = self._city.name + '_ground.' + self._triangular_format
|
||||||
file_path_in = (Path(self._path).resolve() / file_name_in).resolve()
|
file_path_in = (Path(self._path).resolve() / file_name_in).resolve()
|
||||||
|
|
|
@ -3,12 +3,13 @@ Simplified Radiosity Algorithm
|
||||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
Copyright © 2020 Project Author Guillermo.GutierrezMorote@concordia.ca
|
Copyright © 2020 Project Author Guillermo.GutierrezMorote@concordia.ca
|
||||||
"""
|
"""
|
||||||
from pathlib import Path
|
|
||||||
import xmltodict
|
import xmltodict
|
||||||
|
|
||||||
|
|
||||||
class SimplifiedRadiosityAlgorithm:
|
class SimplifiedRadiosityAlgorithm:
|
||||||
|
"""
|
||||||
|
Export to SRA format
|
||||||
|
"""
|
||||||
def __init__(self, city, file_name, begin_month=1, begin_day=1, end_month=12, end_day=31):
|
def __init__(self, city, file_name, begin_month=1, begin_day=1, end_month=12, end_day=31):
|
||||||
self._file_name = file_name
|
self._file_name = file_name
|
||||||
self._begin_month = begin_month
|
self._begin_month = begin_month
|
||||||
|
@ -83,7 +84,5 @@ class SimplifiedRadiosityAlgorithm:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
with open(self._file_name, "w") as file:
|
with open(self._file_name, "w") as file:
|
||||||
file.write(xmltodict.unparse(sra, pretty=True, short_empty_elements=True))
|
file.write(xmltodict.unparse(sra, pretty=True, short_empty_elements=True))
|
||||||
return
|
|
||||||
|
|
|
@ -8,5 +8,8 @@ from exports.formats.triangular import Triangular
|
||||||
|
|
||||||
|
|
||||||
class Stl(Triangular):
|
class Stl(Triangular):
|
||||||
|
"""
|
||||||
|
Export to STL
|
||||||
|
"""
|
||||||
def __init__(self, city, path):
|
def __init__(self, city, path):
|
||||||
super().__init__(city, path, 'stl', write_mode='wb')
|
super().__init__(city, path, 'stl', write_mode='wb')
|
||||||
|
|
|
@ -8,6 +8,9 @@ from trimesh import Trimesh
|
||||||
|
|
||||||
|
|
||||||
class Triangular:
|
class Triangular:
|
||||||
|
"""
|
||||||
|
Superclass to export to triangular format (STL or OBJ)
|
||||||
|
"""
|
||||||
def __init__(self, city, path, triangular_format, write_mode='w'):
|
def __init__(self, city, path, triangular_format, write_mode='w'):
|
||||||
self._city = city
|
self._city = city
|
||||||
self._path = path
|
self._path = path
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
"""
|
||||||
|
Constant module
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
# universal constants
|
# universal constants
|
||||||
KELVIN = 273.15
|
KELVIN = 273.15
|
||||||
|
|
||||||
|
@ -48,4 +55,3 @@ RETAIL = 'retail'
|
||||||
HALL = 'hall'
|
HALL = 'hall'
|
||||||
RESTAURANT = 'restaurant'
|
RESTAURANT = 'restaurant'
|
||||||
EDUCATION = 'education'
|
EDUCATION = 'education'
|
||||||
|
|
||||||
|
|
|
@ -10,25 +10,50 @@ from imports.schedules_factory import SchedulesFactory
|
||||||
|
|
||||||
|
|
||||||
class EnrichCity:
|
class EnrichCity:
|
||||||
|
"""
|
||||||
|
Enrich city
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, city):
|
def __init__(self, city):
|
||||||
self._city = city
|
self._city = city
|
||||||
self._enriched_city = None
|
self._enriched_city = None
|
||||||
self._errors = []
|
self._errors = []
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def errors(self):
|
def errors(self) -> [str]:
|
||||||
|
"""
|
||||||
|
Error list
|
||||||
|
"""
|
||||||
return self._errors
|
return self._errors
|
||||||
|
|
||||||
def enriched_city(self, construction_format=None, usage_format=None, schedules_format=None):
|
def enriched_city(self, construction_format=None, usage_format=None, schedules_format=None):
|
||||||
|
"""
|
||||||
|
Enrich the city with the given formats
|
||||||
|
:return: City
|
||||||
|
"""
|
||||||
if self._enriched_city is None:
|
if self._enriched_city is None:
|
||||||
self._errors = []
|
self._errors = []
|
||||||
|
|
||||||
print('original:', len(self._city.buildings))
|
print('original:', len(self._city.buildings))
|
||||||
if construction_format is not None:
|
if construction_format is not None:
|
||||||
|
self._enriched_city = self._construction(construction_format)
|
||||||
|
if len(self._errors) != 0:
|
||||||
|
return self._enriched_city
|
||||||
|
if usage_format is not None:
|
||||||
|
self._enriched_city = self._usage(usage_format)
|
||||||
|
if len(self._errors) != 0:
|
||||||
|
return self._enriched_city
|
||||||
|
if schedules_format is not None:
|
||||||
|
self._enriched_city = self._schedules(schedules_format)
|
||||||
|
if len(self._errors) != 0:
|
||||||
|
return self._enriched_city
|
||||||
|
self._enriched_city = self._city
|
||||||
|
return self._enriched_city
|
||||||
|
|
||||||
|
def _construction(self, construction_format):
|
||||||
|
|
||||||
# todo: in construction factory, when adding the values to the thermal zones,
|
# todo: in construction factory, when adding the values to the thermal zones,
|
||||||
# these are created using the just read storeys_above_ground -> review where to assign this value!!
|
# these are created using the just read storeys_above_ground -> review where to assign this value!!
|
||||||
ConstructionFactory(construction_format, self._city).enrich()
|
ConstructionFactory(construction_format, self._city).enrich()
|
||||||
|
|
||||||
for building in self._city.buildings:
|
for building in self._city.buildings:
|
||||||
# infiltration_rate_system_off is a mandatory parameter.
|
# infiltration_rate_system_off is a mandatory parameter.
|
||||||
# If it is not returned, extract the building from the calculation list
|
# If it is not returned, extract the building from the calculation list
|
||||||
|
@ -39,8 +64,9 @@ class EnrichCity:
|
||||||
self._enriched_city = self._city
|
self._enriched_city = self._city
|
||||||
return self._enriched_city
|
return self._enriched_city
|
||||||
print('enriched with construction:', len(self._city.buildings))
|
print('enriched with construction:', len(self._city.buildings))
|
||||||
|
return self._city
|
||||||
|
|
||||||
if usage_format is not None:
|
def _usage(self, usage_format):
|
||||||
UsageFactory(usage_format, self._city).enrich()
|
UsageFactory(usage_format, self._city).enrich()
|
||||||
for building in self._city.buildings:
|
for building in self._city.buildings:
|
||||||
# At least one thermal zone must be created.
|
# At least one thermal zone must be created.
|
||||||
|
@ -52,8 +78,9 @@ class EnrichCity:
|
||||||
self._enriched_city = self._city
|
self._enriched_city = self._city
|
||||||
return self._enriched_city
|
return self._enriched_city
|
||||||
print('enriched with usage:', len(self._city.buildings))
|
print('enriched with usage:', len(self._city.buildings))
|
||||||
|
return self._city
|
||||||
|
|
||||||
if schedules_format is not None:
|
def _schedules(self, schedules_format):
|
||||||
SchedulesFactory(schedules_format, self._city).enrich()
|
SchedulesFactory(schedules_format, self._city).enrich()
|
||||||
for building in self._city.buildings:
|
for building in self._city.buildings:
|
||||||
counter_schedules = 0
|
counter_schedules = 0
|
||||||
|
@ -69,6 +96,4 @@ class EnrichCity:
|
||||||
self._enriched_city = self._city
|
self._enriched_city = self._city
|
||||||
return self._enriched_city
|
return self._enriched_city
|
||||||
print('enriched with occupancy:', len(self._city.buildings))
|
print('enriched with occupancy:', len(self._city.buildings))
|
||||||
|
return self._city
|
||||||
self._enriched_city = self._city
|
|
||||||
return self._enriched_city
|
|
||||||
|
|
|
@ -9,10 +9,10 @@ import numpy as np
|
||||||
import requests
|
import requests
|
||||||
from trimesh import Trimesh
|
from trimesh import Trimesh
|
||||||
from trimesh import intersections
|
from trimesh import intersections
|
||||||
from helpers.configuration_helper import ConfigurationHelper
|
|
||||||
from city_model_structure.attributes.polygon import Polygon
|
from city_model_structure.attributes.polygon import Polygon
|
||||||
from city_model_structure.attributes.polyhedron import Polyhedron
|
from city_model_structure.attributes.polyhedron import Polyhedron
|
||||||
from helpers.location import Location
|
from helpers.location import Location
|
||||||
|
from helpers.configuration_helper import ConfigurationHelper
|
||||||
|
|
||||||
|
|
||||||
class GeometryHelper:
|
class GeometryHelper:
|
||||||
|
@ -47,7 +47,6 @@ class GeometryHelper:
|
||||||
delta = math.fabs(a1 - a2)
|
delta = math.fabs(a1 - a2)
|
||||||
return delta <= self._area_delta
|
return delta <= self._area_delta
|
||||||
|
|
||||||
|
|
||||||
def is_almost_same_surface(self, s1, s2):
|
def is_almost_same_surface(self, s1, s2):
|
||||||
"""
|
"""
|
||||||
Compare two surfaces and decides if they are almost equal (quadratic error under delta)
|
Compare two surfaces and decides if they are almost equal (quadratic error under delta)
|
||||||
|
@ -82,11 +81,13 @@ class GeometryHelper:
|
||||||
|
|
||||||
if minimum_distance > self._delta or s1.intersect(s2) is None:
|
if minimum_distance > self._delta or s1.intersect(s2) is None:
|
||||||
return False
|
return False
|
||||||
else:
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def segment_list_to_trimesh(lines) -> Trimesh:
|
def segment_list_to_trimesh(lines) -> Trimesh:
|
||||||
|
"""
|
||||||
|
Transform a list of segments into a Trimesh
|
||||||
|
"""
|
||||||
line_points = [lines[0][0], lines[0][1]]
|
line_points = [lines[0][0], lines[0][1]]
|
||||||
lines.remove(lines[0])
|
lines.remove(lines[0])
|
||||||
while len(lines) > 1:
|
while len(lines) > 1:
|
||||||
|
@ -97,7 +98,7 @@ class GeometryHelper:
|
||||||
line_points.append(line[1])
|
line_points.append(line[1])
|
||||||
lines.pop(i - 1)
|
lines.pop(i - 1)
|
||||||
break
|
break
|
||||||
elif GeometryHelper.distance_between_points(line[1], line_points[len(line_points) - 1]) < 1e-8:
|
if GeometryHelper.distance_between_points(line[1], line_points[len(line_points) - 1]) < 1e-8:
|
||||||
line_points.append(line[0])
|
line_points.append(line[0])
|
||||||
lines.pop(i - 1)
|
lines.pop(i - 1)
|
||||||
break
|
break
|
||||||
|
@ -161,17 +162,19 @@ class GeometryHelper:
|
||||||
return [trimesh_1, trimesh_2]
|
return [trimesh_1, trimesh_2]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_location(latitude, longitude):
|
def get_location(latitude, longitude) -> Location:
|
||||||
|
"""
|
||||||
|
Get Location from latitude and longitude
|
||||||
|
"""
|
||||||
url = 'https://nominatim.openstreetmap.org/reverse?lat={latitude}&lon={longitude}&format=json'
|
url = 'https://nominatim.openstreetmap.org/reverse?lat={latitude}&lon={longitude}&format=json'
|
||||||
response = requests.get(url.format(latitude=latitude, longitude=longitude))
|
response = requests.get(url.format(latitude=latitude, longitude=longitude))
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
# This means something went wrong.
|
# This means something went wrong.
|
||||||
raise Exception('GET /tasks/ {}'.format(response.status_code))
|
raise Exception('GET /tasks/ {}'.format(response.status_code))
|
||||||
else:
|
|
||||||
response = response.json()
|
response = response.json()
|
||||||
# todo: this is wrong, remove in the future
|
city = 'Unknown'
|
||||||
city = 'new_york_city'
|
country = 'ca'
|
||||||
country = 'us'
|
|
||||||
if 'city' in response['address']:
|
if 'city' in response['address']:
|
||||||
city = response['address']['city']
|
city = response['address']['city']
|
||||||
if 'country_code' in response['address']:
|
if 'country_code' in response['address']:
|
||||||
|
|
|
@ -1,12 +1,29 @@
|
||||||
|
"""
|
||||||
|
Location module
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
Contributors Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Location:
|
class Location:
|
||||||
|
"""
|
||||||
|
Location
|
||||||
|
"""
|
||||||
def __init__(self, country, city):
|
def __init__(self, country, city):
|
||||||
self._country = country
|
self._country = country
|
||||||
self._city = city
|
self._city = city
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def city(self):
|
def city(self):
|
||||||
|
"""
|
||||||
|
City name
|
||||||
|
"""
|
||||||
return self._city
|
return self._city
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def country(self):
|
def country(self):
|
||||||
|
"""
|
||||||
|
Country code
|
||||||
|
"""
|
||||||
return self._country
|
return self._country
|
|
@ -3,9 +3,9 @@ monthly_to_hourly_demand module
|
||||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
"""
|
"""
|
||||||
|
import calendar as cal
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from city_model_structure.building_demand.occupants import Occupants
|
from city_model_structure.building_demand.occupants import Occupants
|
||||||
import calendar as cal
|
|
||||||
import helpers.constants as cte
|
import helpers.constants as cte
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ class MonthlyToHourlyDemand:
|
||||||
for month in range(1, 13):
|
for month in range(1, 13):
|
||||||
temp_grad_month = 0
|
temp_grad_month = 0
|
||||||
month_range = cal.monthrange(2015, month)[1]
|
month_range = cal.monthrange(2015, month)[1]
|
||||||
for day in range(1, month_range+1):
|
for _ in range(1, month_range+1):
|
||||||
external_temp_med = 0
|
external_temp_med = 0
|
||||||
for hour in range(0, 24):
|
for hour in range(0, 24):
|
||||||
external_temp_med += external_temp[key][i]/24
|
external_temp_med += external_temp[key][i]/24
|
||||||
|
@ -66,10 +66,8 @@ class MonthlyToHourlyDemand:
|
||||||
temp_grad_month += temp_grad_day[i]
|
temp_grad_month += temp_grad_day[i]
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
for day in range(1, month_range + 1):
|
for _ in range(1, month_range + 1):
|
||||||
for hour in range(0, 24):
|
for hour in range(0, 24):
|
||||||
# monthly_demand = self._building.heating[cte.MONTH]['INSEL'][month-1] or maybe:
|
|
||||||
# monthly_demand = self._building.heating[cte.MONTH].INSEL[month-1]
|
|
||||||
monthly_demand = self._building.heating[cte.MONTH][month-1]
|
monthly_demand = self._building.heating[cte.MONTH][month-1]
|
||||||
if monthly_demand == 'NaN':
|
if monthly_demand == 'NaN':
|
||||||
monthly_demand = 0
|
monthly_demand = 0
|
||||||
|
@ -104,7 +102,7 @@ class MonthlyToHourlyDemand:
|
||||||
for month in range(1, 13):
|
for month in range(1, 13):
|
||||||
temp_grad_month = 0
|
temp_grad_month = 0
|
||||||
month_range = cal.monthrange(2015, month)[1]
|
month_range = cal.monthrange(2015, month)[1]
|
||||||
for day in range(1, month_range[1] + 1):
|
for _ in range(1, month_range[1] + 1):
|
||||||
for hour in range(0, 24):
|
for hour in range(0, 24):
|
||||||
if external_temp[key][i] > temp_set and cooling_schedule[month - 1] == 1:
|
if external_temp[key][i] > temp_set and cooling_schedule[month - 1] == 1:
|
||||||
if occupancy[hour] > 0:
|
if occupancy[hour] > 0:
|
||||||
|
@ -123,7 +121,7 @@ class MonthlyToHourlyDemand:
|
||||||
temp_grad_month += temp_grad_day[i]
|
temp_grad_month += temp_grad_day[i]
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
for day in range(1, month_range[1] + 1):
|
for _ in range(1, month_range[1] + 1):
|
||||||
for hour in range(0, 24):
|
for hour in range(0, 24):
|
||||||
# monthly_demand = self._building.heating[cte.MONTH]['INSEL'][month-1]
|
# monthly_demand = self._building.heating[cte.MONTH]['INSEL'][month-1]
|
||||||
monthly_demand = self._building.cooling[cte.MONTH][month - 1]
|
monthly_demand = self._building.cooling[cte.MONTH][month - 1]
|
||||||
|
|
|
@ -4,11 +4,8 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from imports.construction.nrel_physics_interface import NrelPhysicsInterface
|
|
||||||
from imports.construction.helpers.construction_helper import ConstructionHelper
|
from imports.construction.helpers.construction_helper import ConstructionHelper
|
||||||
from city_model_structure.building_demand.layer import Layer
|
from imports.construction.nrel_physics_interface import NrelPhysicsInterface
|
||||||
from city_model_structure.building_demand.material import Material
|
|
||||||
|
|
||||||
|
|
||||||
class CaPhysicsParameters(NrelPhysicsInterface):
|
class CaPhysicsParameters(NrelPhysicsInterface):
|
||||||
|
|
|
@ -8,6 +8,9 @@ from helpers import constants as cte
|
||||||
|
|
||||||
|
|
||||||
class ConstructionHelper:
|
class ConstructionHelper:
|
||||||
|
"""
|
||||||
|
Construction helper
|
||||||
|
"""
|
||||||
# NREL
|
# NREL
|
||||||
function_to_nrel = {
|
function_to_nrel = {
|
||||||
cte.RESIDENTIAL: 'residential',
|
cte.RESIDENTIAL: 'residential',
|
||||||
|
|
|
@ -16,6 +16,7 @@ class NrelPhysicsInterface:
|
||||||
"""
|
"""
|
||||||
NrelPhysicsInterface abstract class
|
NrelPhysicsInterface abstract class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, base_path, constructions_file='us_constructions.xml',
|
def __init__(self, base_path, constructions_file='us_constructions.xml',
|
||||||
archetypes_file='us_archetypes.xml'):
|
archetypes_file='us_archetypes.xml'):
|
||||||
self._building_archetypes = []
|
self._building_archetypes = []
|
||||||
|
@ -39,7 +40,7 @@ class NrelPhysicsInterface:
|
||||||
if units != 'm':
|
if units != 'm':
|
||||||
raise Exception(f'average storey height units = {units}, expected meters')
|
raise Exception(f'average storey height units = {units}, expected meters')
|
||||||
storeys_above_ground = archetype['number_of_storeys']['#text']
|
storeys_above_ground = archetype['number_of_storeys']['#text']
|
||||||
effective_thermal_capacity = float(archetype['thermal_capacity']['#text'])*1000
|
effective_thermal_capacity = float(archetype['thermal_capacity']['#text']) * 1000
|
||||||
units = archetype['thermal_capacity']['@units']
|
units = archetype['thermal_capacity']['@units']
|
||||||
if units != 'kJ/K m2':
|
if units != 'kJ/K m2':
|
||||||
raise Exception(f'thermal capacity units = {units}, expected kJ/K m2')
|
raise Exception(f'thermal capacity units = {units}, expected kJ/K m2')
|
||||||
|
@ -177,4 +178,7 @@ class NrelPhysicsInterface:
|
||||||
raise Exception('Construction type not found')
|
raise Exception('Construction type not found')
|
||||||
|
|
||||||
def enrich_buildings(self):
|
def enrich_buildings(self):
|
||||||
|
"""
|
||||||
|
Raise not implemented error
|
||||||
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
|
@ -3,9 +3,9 @@ ConstructionFactory (before PhysicsFactory) retrieve the specific construction m
|
||||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
"""
|
"""
|
||||||
|
from pathlib import Path
|
||||||
from imports.construction.us_physics_parameters import UsPhysicsParameters
|
from imports.construction.us_physics_parameters import UsPhysicsParameters
|
||||||
from imports.construction.ca_physics_parameters import CaPhysicsParameters
|
from imports.construction.ca_physics_parameters import CaPhysicsParameters
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
|
|
||||||
class ConstructionFactory:
|
class ConstructionFactory:
|
||||||
|
@ -23,9 +23,6 @@ class ConstructionFactory:
|
||||||
def _nrcan(self):
|
def _nrcan(self):
|
||||||
CaPhysicsParameters(self._city, self._base_path).enrich_buildings()
|
CaPhysicsParameters(self._city, self._base_path).enrich_buildings()
|
||||||
|
|
||||||
def _other_construction_library_format(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def enrich(self):
|
def enrich(self):
|
||||||
"""
|
"""
|
||||||
Enrich the city with the construction information
|
Enrich the city with the construction information
|
||||||
|
|
|
@ -20,13 +20,10 @@ class UsageFactory:
|
||||||
self._base_path = base_path
|
self._base_path = base_path
|
||||||
|
|
||||||
def _hft(self):
|
def _hft(self):
|
||||||
HftUsageParameters(self._city, self._base_path).enrich_buildings()
|
return HftUsageParameters(self._city, self._base_path).enrich_buildings()
|
||||||
|
|
||||||
def _ca(self):
|
def _ca(self):
|
||||||
CaUsageParameters(self._city, self._base_path).enrich_buildings()
|
return CaUsageParameters(self._city, self._base_path).enrich_buildings()
|
||||||
|
|
||||||
def _other_usage_library_format(self):
|
|
||||||
raise Exception('Not implemented')
|
|
||||||
|
|
||||||
def enrich(self):
|
def enrich(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -19,23 +19,16 @@ class WeatherFactory:
|
||||||
self._base_path = base_path
|
self._base_path = base_path
|
||||||
self._file_name = file_name
|
self._file_name = file_name
|
||||||
|
|
||||||
def _tmy3(self):
|
|
||||||
raise Exception('Not implemented')
|
|
||||||
|
|
||||||
def _tm2(self):
|
|
||||||
# Meteonorm (https://meteonorm.com/en/product/typical-years)
|
|
||||||
raise Exception('Not implemented')
|
|
||||||
|
|
||||||
def _epw(self):
|
def _epw(self):
|
||||||
# EnergyPlus Weather
|
# EnergyPlus Weather
|
||||||
# to download files: https://energyplus.net/weather
|
# to download files: https://energyplus.net/weather
|
||||||
# description of the format: https://energyplus.net/sites/default/files/pdfs_v8.3.0/AuxiliaryPrograms.pdf
|
# description of the format: https://energyplus.net/sites/default/files/pdfs_v8.3.0/AuxiliaryPrograms.pdf
|
||||||
_path = Path(self._base_path / 'epw').resolve()
|
_path = Path(self._base_path / 'epw').resolve()
|
||||||
EpwWeatherParameters(self._city, _path, self._file_name)
|
return EpwWeatherParameters(self._city, _path, self._file_name)
|
||||||
|
|
||||||
def _xls(self):
|
def _xls(self):
|
||||||
name = 'ISO_52016_1_BESTEST_ClimData_2016.08.24'
|
name = 'ISO_52016_1_BESTEST_ClimData_2016.08.24'
|
||||||
XlsWeatherParameters(self._city, self._base_path, name)
|
return XlsWeatherParameters(self._city, self._base_path, name)
|
||||||
|
|
||||||
def enrich(self):
|
def enrich(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -52,3 +52,9 @@ class TestSchedulesFactory(TestCase):
|
||||||
city = self._get_citygml(file)
|
city = self._get_citygml(file)
|
||||||
occupancy_handler = 'doe_idf'
|
occupancy_handler = 'doe_idf'
|
||||||
SchedulesFactory(occupancy_handler, city).enrich()
|
SchedulesFactory(occupancy_handler, city).enrich()
|
||||||
|
for building in city.buildings:
|
||||||
|
for usage_zone in building.usage_zones:
|
||||||
|
for schedule in usage_zone.schedules:
|
||||||
|
print(schedule)
|
||||||
|
print(usage_zone.schedules[schedule])
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user