Merge remote-tracking branch 'origin/master'

This commit is contained in:
Pilar 2021-03-30 15:13:03 -04:00
commit 2702c77a33
5 changed files with 216 additions and 59 deletions

View File

@ -36,6 +36,9 @@ class Surface:
self._min_x = None self._min_x = None
self._min_y = None self._min_y = None
self._min_z = None self._min_z = None
self._max_x = None
self._max_y = None
self._max_z = None
self._shared_surfaces = [] self._shared_surfaces = []
self._global_irradiance = dict() self._global_irradiance = dict()
self._perimeter_polygon = None self._perimeter_polygon = None
@ -171,6 +174,21 @@ class Surface:
self._perimeter_points_list = np.reshape(s, len(s) * 3) self._perimeter_points_list = np.reshape(s, len(s) * 3)
return self._perimeter_points_list return self._perimeter_points_list
def _max_coord(self, axis):
if axis == 'x':
axis = 0
elif axis == 'y':
axis = 1
else:
axis = 2
max_coordinate = ''
for point in self.points:
if max_coordinate == '':
max_coordinate = point[axis]
elif max_coordinate < point[axis]:
max_coordinate = point[axis]
return max_coordinate
def _min_coord(self, axis): def _min_coord(self, axis):
if axis == 'x': if axis == 'x':
axis = 0 axis = 0
@ -186,6 +204,36 @@ class Surface:
min_coordinate = point[axis] min_coordinate = point[axis]
return min_coordinate return min_coordinate
@property
def max_x(self):
"""
Surface maximal x value
:return: float
"""
if self._max_x is None:
self._max_x = self._max_coord('x')
return self._max_x
@property
def max_y(self):
"""
Surface maximal y value
:return: float
"""
if self._max_y is None:
self._max_y = self._max_coord('y')
return self._max_y
@property
def max_z(self):
"""
Surface maximal z value
:return: float
"""
if self._max_z is None:
self._max_z = self._max_coord('z')
return self._max_z
@property @property
def min_x(self): def min_x(self):
""" """

View File

@ -4,8 +4,8 @@ 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
Contributors Pilar Monsalvete Álvarez de Uribarri pilar.monsalvete@concordia.ca Contributors Pilar Monsalvete Álvarez de Uribarri pilar.monsalvete@concordia.ca
""" """
import uuid
from typing import List, TypeVar from typing import List, TypeVar
from city_model_structure.attributes.surface import Surface from city_model_structure.attributes.surface import Surface
from city_model_structure.attributes.usage_zone import UsageZone from city_model_structure.attributes.usage_zone import UsageZone
@ -17,12 +17,10 @@ class ThermalZone:
""" """
ThermalZone class ThermalZone class
""" """
def __init__(self, surfaces, is_heated, is_cooled): def __init__(self, surfaces):
self._surfaces = surfaces self._surfaces = surfaces
self._floor_area = None self._floor_area = None
self._bounded = None self._bounded = None
self._is_heated = is_heated
self._is_cooled = is_cooled
self._is_mechanically_ventilated = None self._is_mechanically_ventilated = None
self._additional_thermal_bridge_u_value = None self._additional_thermal_bridge_u_value = None
self._effective_thermal_capacity = None self._effective_thermal_capacity = None
@ -32,6 +30,15 @@ class ThermalZone:
self._usage_zones = None self._usage_zones = None
self._volume = None self._volume = None
self._volume_geometry = None self._volume_geometry = None
self._id = None
self._is_heated = False
self._is_cooled = False
@property
def id(self):
if self._id is None:
self._id = uuid.uuid4()
return self._id
@property @property
def is_heated(self): def is_heated(self):
@ -41,6 +48,14 @@ class ThermalZone:
""" """
return self._is_heated return self._is_heated
@is_heated.setter
def is_heated(self, value):
"""
Set thermal zone heated flag
:return: Boolean
"""
self._is_heated = value
@property @property
def is_cooled(self): def is_cooled(self):
""" """
@ -49,6 +64,14 @@ class ThermalZone:
""" """
return self._is_cooled return self._is_cooled
@is_cooled.setter
def is_cooled(self, value):
"""
Set thermal zone cooled flag
:return: Boolean
"""
self._is_cooled = value
@property @property
def is_mechanically_ventilated(self): def is_mechanically_ventilated(self):
""" """

View File

@ -4,6 +4,7 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Pilar Monsalvete pilar_monsalvete@yahoo.es Copyright © 2020 Project Author Pilar Monsalvete pilar_monsalvete@yahoo.es
Contributors Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Contributors Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
""" """
import uuid
from typing import List, TypeVar from typing import List, TypeVar
InternalGains = TypeVar('InternalGains') InternalGains = TypeVar('InternalGains')
@ -17,6 +18,7 @@ class UsageZone:
UsageZone class UsageZone class
""" """
def __init__(self): def __init__(self):
self._id = None
self._usage = None self._usage = None
self._internal_gains = None self._internal_gains = None
self._heating_setpoint = None self._heating_setpoint = None
@ -36,6 +38,12 @@ class UsageZone:
self._ventilation_schedule = None self._ventilation_schedule = None
self._volume_geometry = None self._volume_geometry = None
@property
def id(self):
if self._id is None:
self._id = uuid.uuid4()
return self._id
@property @property
def lights(self) -> List[Lighting]: def lights(self) -> List[Lighting]:
return self._lights return self._lights

View File

@ -33,8 +33,6 @@ class Building(CityObject):
self._function = function self._function = function
self._city_lower_corner = city_lower_corner self._city_lower_corner = city_lower_corner
self._building_lower_corner = None self._building_lower_corner = None
self._heated = None
self._cooled = None
self._average_storey_height = None self._average_storey_height = None
self._storeys_above_ground = None self._storeys_above_ground = None
self._floor_area = None self._floor_area = None
@ -59,7 +57,7 @@ class Building(CityObject):
self._thermal_zones = [] self._thermal_zones = []
if self.lod < 4: if self.lod < 4:
# for lod under 4 is just one thermal zone # for lod under 4 is just one thermal zone
self._thermal_zones.append(ThermalZone(self.surfaces, self._heated, self._cooled)) self._thermal_zones.append(ThermalZone(self.surfaces))
for t_zones in self._thermal_zones: for t_zones in self._thermal_zones:
t_zones.bounded = [ThermalBoundary(s, [t_zones]) for s in t_zones.surfaces] t_zones.bounded = [ThermalBoundary(s, [t_zones]) for s in t_zones.surfaces]
@ -81,6 +79,28 @@ class Building(CityObject):
""" """
return self._grounds return self._grounds
@property
def is_heated(self):
"""
Get building heated flag
:return: Boolean
"""
for thermal_zone in self.thermal_zones:
if thermal_zone.is_heated:
return thermal_zone.is_heated
return False
@property
def is_cooled(self):
"""
Get building cooled flag
:return: Boolean
"""
for thermal_zone in self.thermal_zones:
if thermal_zone.is_cooled:
return thermal_zone.is_cooled
return False
@property @property
def roofs(self) -> [Surface]: def roofs(self) -> [Surface]:
""" """
@ -110,10 +130,9 @@ class Building(CityObject):
:param values: [UsageZones] :param values: [UsageZones]
:return: None :return: None
""" """
# ToDo: this is only valid for one usage zone need to be revised for multiple usage zones.
self._usage_zones = values self._usage_zones = values
for thermal_zone in self.thermal_zones: for thermal_zone in self.thermal_zones:
thermal_zone.usage_zones = [(100, usage_zone) for usage_zone in values] thermal_zone.usage_zones = values
@property @property
def terrains(self) -> List[Surface]: def terrains(self) -> List[Surface]:

View File

@ -5,6 +5,7 @@ Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc
""" """
import xmltodict import xmltodict
import uuid import uuid
import datetime
from pathlib import Path from pathlib import Path
@ -27,7 +28,7 @@ class EnergyAde:
'@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:gen':'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',
@ -37,6 +38,7 @@ class EnergyAde:
'@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: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': {
@ -53,11 +55,31 @@ class EnergyAde:
for building in self._city.buildings: for building in self._city.buildings:
building_dic = { building_dic = {
'bldg:Building': { 'bldg:Building': {
'@gml:id': building.name '@gml:id': building.name,
'gml:description': f'Building {building.name} at {self._city.name}',
'gml:name': f'{building.name}',
'core:creationDate': datetime.datetime.now().strftime('%Y-%m-%d')
} }
} }
building_dic = EnergyAde._measures(building, building_dic) building_dic = EnergyAde._measures(building, building_dic)
building_dic = EnergyAde._building_geometry(building, building_dic, self._city) building_dic = EnergyAde._building_geometry(building, building_dic, self._city)
building_dic['bldg:Building']['energy:volume'] = {
'energy:VolumeType': {
'energy:type':'grossVolume',
'energy:value': {
'@uom': 'm3',
'energy:value': building.volume
}
}
}
building_dic['bldg:Building']['energy:referencePoint'] = {
'gml:Point': {
'@srsName': self._city.srs_name,
'@gml:id': f'GML_{uuid.uuid4()}',
'gml:Pos': f'{" ".join(map(str,building.centroid))}'
}
}
building_dic['bldg:Building']['energy:thermalZone'] = EnergyAde._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
@ -76,21 +98,20 @@ class EnergyAde:
if measure is not None: if measure is not None:
measures.append(measure) measures.append(measure)
if len(measures) != 0: if len(measures) != 0:
building_dic['genobj:measureAttribute'] = measures building_dic['bldg:Building']['genobj:measureAttribute'] = measures
periods = [] demands = []
for key in building.heating: for key in building.heating:
if key != 'year': if key != 'year':
period = EnergyAde._period(building.heating, key, 'Heating energy', 'INSEL') demand = EnergyAde._demand(building.heating, key, 'Heating energy', 'INSEL')
periods.append(period) demands.append(demand)
for key in building.cooling: for key in building.cooling:
if key != 'year': if key != 'year':
period = EnergyAde._period(building.cooling, key, 'Cooling energy', 'INSEL') demand = EnergyAde._demand(building.cooling, key, 'Cooling energy', 'INSEL')
periods.append(period) demands.append(demand)
if len(demands) != 0:
if len(periods) != 0: building_dic['bldg:Building']['energy:demands'] = demands
building_dic['energy:demands'] = {'energy:EnergyDemand': periods}
return building_dic return building_dic
@ -108,45 +129,46 @@ class EnergyAde:
return measure return measure
@staticmethod @staticmethod
def _period(measure_dict, key_value, description, source): def _demand(measure_dict, key_value, description, source):
period = { demand = {
'@gml:id': uuid.uuid4(), 'energy:EnergyDemand': {
'energy:energyAmount': { '@gml:id': f'GML_{uuid.uuid4()}',
'energy:RegularTimeSeries': { 'energy:energyAmount': {
'energy:variableProperties': { 'energy:RegularTimeSeries': {
'energy:TimeValuesProperties': { 'energy:variableProperties': {
'energy:acquisitionMethod': 'simulation', 'energy:TimeValuesProperties': {
'energy:source': source, 'energy:acquisitionMethod': 'simulation',
'energy:thematicDescription': description, 'energy:source': source,
}, 'energy:thematicDescription': description,
'energy:timeInterval': { },
'@unit': key_value, 'energy:timeInterval': {
'#text': '1', '@unit': key_value,
}, '#text': '1',
'energy:values': { },
'@uom': 'kWh', 'energy:values': {
'#text': ' '.join([str(float(e) / 1000) for e in measure_dict[key_value][source]]) '@uom': 'kWh',
'#text': ' '.join([str(float(e) / 1000) for e in measure_dict[key_value][source]])
}
} }
} }
} },
'energy:endUse': 'spaceHeating'
} }
} }
return period return demand
@staticmethod @staticmethod
def _building_geometry(building, building_dic, city): def _building_geometry(building, building_dic, city):
building_dic['bldg:function'] = building.function building_dic['bldg:Building']['bldg:function'] = building.function
building_dic['bldg:usage'] = ', '.join([u.usage for u in building.usage_zones]) building_dic['bldg:Building']['bldg:usage'] = ', '.join([u.usage for u in building.usage_zones])
building_dic['bldg:yearOfConstruction'] = building.year_of_construction building_dic['bldg:Building']['bldg:yearOfConstruction'] = building.year_of_construction
building_dic['bldg:roofType'] = building.roof_type building_dic['bldg:Building']['bldg:roofType'] = building.roof_type
building_dic['bldg:Building']['bldg:measuredHeight'] = {
building_dic['bldg:measuredHeight'] = {
'@uom': 'm', '@uom': 'm',
'#text': f'{building.max_height}' '#text': f'{building.max_height}'
} }
building_dic['bldg:Building']['bldg:storeysAboveGround'] = building.storeys_above_ground
building_dic['bldg:storeysAboveGround'] = building.storeys_above_ground
if building.lod == 1: if building.lod == 1:
building_dic = EnergyAde._lod1(building, building_dic, city) building_dic = EnergyAde._lod1(building, building_dic, city)
elif building.lod == 2: elif building.lod == 2:
@ -178,8 +200,6 @@ class EnergyAde:
else: else:
surface_type = 'bldg:RoofSurface' surface_type = 'bldg:RoofSurface'
surface_dic = { surface_dic = {
surface_type: { surface_type: {
'@gml:id': f'GML_{uuid.uuid4()}', '@gml:id': f'GML_{uuid.uuid4()}',
@ -198,13 +218,13 @@ class EnergyAde:
'surfaceMember': { 'surfaceMember': {
'gml:Polygon': { 'gml:Polygon': {
'@srsName': city.srs_name, '@srsName': city.srs_name,
'@gml:id': f'#PolyId{surface.name}', '@gml:id': f'PolyId{surface.name}',
'gml:exterior': { 'gml:exterior': {
'gml:LinearRing': { 'gml:LinearRing': {
'@gml:id': f'#PolyId{surface.name}_0', '@gml:id': f'PolyId{surface.name}_0',
'gml:posList': { 'gml:posList': {
'@srsDimension': '3', '@srsDimension': '3',
'@count': len(surface.points_list) + 1, '@count': len(surface.points) + 1,
'#text': f'{" ".join(map(str, surface.points_list))} {" ".join(map(str, surface.points[0]))}' '#text': f'{" ".join(map(str, surface.points_list))} {" ".join(map(str, surface.points[0]))}'
} }
} }
@ -215,10 +235,8 @@ class EnergyAde:
} }
} }
} }
print(surface_dic)
boundaries.append(surface_dic) boundaries.append(surface_dic)
print(surface_dic) building_dic['bldg:Building']['bldg:lod2Solid'] = {
building_dic['bldg:lod2Solid'] = {
'gml:Solid': { 'gml:Solid': {
'@gml:id': f'GML_{uuid.uuid4()}', '@gml:id': f'GML_{uuid.uuid4()}',
'gml:exterior': { 'gml:exterior': {
@ -231,6 +249,47 @@ class EnergyAde:
} }
} }
building_dic['gml:boundedBy'] = boundaries building_dic['bldg:Building']['gml:boundedBy'] = boundaries
print(building_dic)
return building_dic return building_dic
@staticmethod
def _thermal_zones(building, city):
thermal_zones = []
for index, thermal_zone in enumerate(building.thermal_zones):
usage_zones = []
for usage_zone in thermal_zone.usage_zones:
usage_zones.append({'@xlink:href': f'#GML_{usage_zone.id}'})
thermal_zone_dic = {
'energy:ThermalZone': {
'@gml:id': f'GML_{thermal_zone.id}',
'gml:name': f'Thermal zone {index} in {building.name} building',
'energy:contains': [],
'energy:floorArea': {
'energy:FloorArea' : {
'energy:type': 'grossFloorArea',
'energy:value': {
'@uom': 'm2',
'#text': f'{thermal_zone.floor_area}'
}
}
},
'energy:volume': {
'energy:VolumeType': {
'energy:type': 'grossVolume',
'energy:value': {
'@uom': 'm3',
#todo: for now we have just one thermal zone, therefore is the building volume, this need to be changed
'#text': f'{building.volume}'
}
}
},
'energy:isCooled': f'{thermal_zone.is_cooled}',
'energy:isHeated': f'{thermal_zone.is_heated}',
}
}
thermal_zone_dic['energy:ThermalZone']['energy:contains'] = usage_zones
thermal_zones.append(thermal_zone_dic)
print(thermal_zones)
return thermal_zones