Compare commits

..

No commits in common. "2b696f9251ff91087e837eeb1de75dfc42c91ced" and "a97c0685134cf39c235a512866ee2bf859f0b9a2" have entirely different histories.

13 changed files with 73 additions and 141 deletions

View File

@ -70,9 +70,6 @@ class Building(CityObject):
self._min_x = min(self._min_x, surface.lower_corner[0]) self._min_x = min(self._min_x, surface.lower_corner[0])
self._min_y = min(self._min_y, surface.lower_corner[1]) self._min_y = min(self._min_y, surface.lower_corner[1])
self._min_z = min(self._min_z, surface.lower_corner[2]) self._min_z = min(self._min_z, surface.lower_corner[2])
self._max_x = max(self._max_x, surface.upper_corner[0])
self._max_y = max(self._max_y, surface.upper_corner[1])
self._max_z = max(self._max_z, surface.upper_corner[2])
surface.id = surface_id surface.id = surface_id
if surface.type == cte.GROUND: if surface.type == cte.GROUND:
self._grounds.append(surface) self._grounds.append(surface)

View File

@ -14,7 +14,6 @@ class Construction:
""" """
def __init__(self): def __init__(self):
self._type = None self._type = None
self._name = None
self._layers = None self._layers = None
self._window_ratio = None self._window_ratio = None
self._window_frame_ratio = None self._window_frame_ratio = None
@ -38,22 +37,6 @@ class Construction:
""" """
self._type = value self._type = value
@property
def name(self):
"""
Get construction name
:return: str
"""
return self._name
@name.setter
def name(self, value):
"""
Set construction name
:param value: str
"""
self._name = value
@property @property
def layers(self) -> [Layer]: def layers(self) -> [Layer]:
""" """

View File

@ -130,7 +130,6 @@ class InternalZone:
for hole in surface.holes_polygons: for hole in surface.holes_polygons:
windows_areas.append(hole.area) windows_areas.append(hole.area)
_thermal_boundary = ThermalBoundary(surface, surface.solid_polygon.area, windows_areas) _thermal_boundary = ThermalBoundary(surface, surface.solid_polygon.area, windows_areas)
surface.associated_thermal_boundaries = [_thermal_boundary]
_thermal_boundaries.append(_thermal_boundary) _thermal_boundaries.append(_thermal_boundary)
_number_of_storeys = int(self.volume / self.area / self.thermal_archetype.average_storey_height) _number_of_storeys = int(self.volume / self.area / self.thermal_archetype.average_storey_height)
_thermal_zone = ThermalZone(_thermal_boundaries, self, self.volume, self.area, _number_of_storeys) _thermal_zone = ThermalZone(_thermal_boundaries, self, self.volume, self.area, _number_of_storeys)

View File

@ -59,7 +59,6 @@ class Layer:
Get material name Get material name
:return: str :return: str
""" """
# todo: this should be named material_name instead
return self._name return self._name
@name.setter @name.setter
@ -68,7 +67,6 @@ class Layer:
Set material name Set material name
:param value: string :param value: string
""" """
# todo: this should be named material_name instead
self._name = str(value) self._name = str(value)
@property @property

View File

@ -154,6 +154,7 @@ class Surface:
if self._inclination is None: if self._inclination is None:
self._inclination = np.arccos(self.perimeter_polygon.normal[2]) self._inclination = np.arccos(self.perimeter_polygon.normal[2])
return self._inclination return self._inclination
@property @property
def type(self): def type(self):
""" """

View File

@ -256,19 +256,6 @@ class ThermalBoundary:
raise TypeError('Constructions layers are not initialized') from TypeError raise TypeError('Constructions layers are not initialized') from TypeError
return self._u_value return self._u_value
@property
def construction_name(self):
"""
Get construction name
:return: str
"""
if self._construction_archetype is not None:
self._construction_name = self._construction_archetype.name
else:
logging.error('Construction name not defined\n')
raise ValueError('Construction name not defined')
return self._construction_name
@u_value.setter @u_value.setter
def u_value(self, value): def u_value(self, value):
""" """

View File

@ -148,28 +148,28 @@ class Idf:
def _add_material(self, layer): def _add_material(self, layer):
for material in self._idf.idfobjects[self._MATERIAL]: for material in self._idf.idfobjects[self._MATERIAL]:
if material.Name == layer.name: if material.Name == layer.material.name:
return return
for material in self._idf.idfobjects[self._MATERIAL_NOMASS]: for material in self._idf.idfobjects[self._MATERIAL_NOMASS]:
if material.Name == layer.name: if material.Name == layer.material.name:
return return
if layer.no_mass: if layer.material.no_mass:
self._idf.newidfobject(self._MATERIAL_NOMASS, self._idf.newidfobject(self._MATERIAL_NOMASS,
Name=layer.name, Name=layer.material.name,
Roughness=self._ROUGHNESS, Roughness=self._ROUGHNESS,
Thermal_Resistance=layer.thermal_resistance Thermal_Resistance=layer.material.thermal_resistance
) )
else: else:
self._idf.newidfobject(self._MATERIAL, self._idf.newidfobject(self._MATERIAL,
Name=layer.name, Name=layer.material.name,
Roughness=self._ROUGHNESS, Roughness=self._ROUGHNESS,
Thickness=layer.thickness, Thickness=layer.thickness,
Conductivity=layer.conductivity, Conductivity=layer.material.conductivity,
Density=layer.density, Density=layer.material.density,
Specific_Heat=layer.specific_heat, Specific_Heat=layer.material.specific_heat,
Thermal_Absorptance=layer.thermal_absorptance, Thermal_Absorptance=layer.material.thermal_absorptance,
Solar_Absorptance=layer.solar_absorptance, Solar_Absorptance=layer.material.solar_absorptance,
Visible_Absorptance=layer.visible_absorptance Visible_Absorptance=layer.material.visible_absorptance
) )
@staticmethod @staticmethod
@ -338,11 +338,11 @@ class Idf:
_kwargs = {'Name': vegetation_name, _kwargs = {'Name': vegetation_name,
'Outside_Layer': thermal_boundary.parent_surface.vegetation.name} 'Outside_Layer': thermal_boundary.parent_surface.vegetation.name}
for i in range(0, len(layers) - 1): 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: 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): 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) self._idf.newidfobject(self._CONSTRUCTION, **_kwargs)
def _add_window_construction_and_material(self, thermal_opening): def _add_window_construction_and_material(self, thermal_opening):
@ -512,12 +512,12 @@ class Idf:
self._rename_building(self._city.name) self._rename_building(self._city.name)
self._lod = self._city.level_of_detail.geometry self._lod = self._city.level_of_detail.geometry
for building in self._city.buildings: for building in self._city.buildings:
print('building name', building.name)
for internal_zone in building.internal_zones: for internal_zone in building.internal_zones:
if internal_zone.thermal_zones_from_internal_zones is None: if internal_zone.thermal_zones_from_internal_zones is None:
continue continue
for thermal_zone in internal_zone.thermal_zones_from_internal_zones: for thermal_zone in internal_zone.thermal_zones_from_internal_zones:
for thermal_boundary in thermal_zone.thermal_boundaries: for thermal_boundary in thermal_zone.thermal_boundaries:
self._add_construction(thermal_boundary) self._add_construction(thermal_boundary)
if thermal_boundary.parent_surface.vegetation is not None: if thermal_boundary.parent_surface.vegetation is not None:
self._add_vegetation_material(thermal_boundary.parent_surface.vegetation) self._add_vegetation_material(thermal_boundary.parent_surface.vegetation)
@ -560,7 +560,7 @@ class Idf:
self._add_dhw(thermal_zone, building.name) self._add_dhw(thermal_zone, building.name)
if self._export_type == "Surfaces": if self._export_type == "Surfaces":
if building.name in self._target_buildings or building.name in self._adjacent_buildings: if building.name in self._target_buildings or building.name in self._adjacent_buildings:
if building.thermal_zones_from_internal_zones is not None: if building.internal_zones[0].thermal_zones_from_internal_zones is not None:
self._add_surfaces(building, building.name) self._add_surfaces(building, building.name)
else: else:
self._add_pure_geometry(building, building.name) self._add_pure_geometry(building, building.name)
@ -677,40 +677,41 @@ class Idf:
self._idf.set_wwr(wwr) self._idf.set_wwr(wwr)
def _add_surfaces(self, building, zone_name): def _add_surfaces(self, building, zone_name):
for thermal_zone in building.thermal_zones_from_internal_zones: for internal_zone in building.internal_zones:
for boundary in thermal_zone.thermal_boundaries: for thermal_zone in internal_zone.thermal_zones_from_internal_zones:
idf_surface_type = self.idf_surfaces[boundary.parent_surface.type] for boundary in thermal_zone.thermal_boundaries:
outside_boundary_condition = 'Outdoors' idf_surface_type = self.idf_surfaces[boundary.parent_surface.type]
sun_exposure = 'SunExposed' outside_boundary_condition = 'Outdoors'
wind_exposure = 'WindExposed' sun_exposure = 'SunExposed'
_kwargs = {'Name': f'{boundary.parent_surface.name}', wind_exposure = 'WindExposed'
'Surface_Type': idf_surface_type, _kwargs = {'Name': f'{boundary.parent_surface.name}',
'Zone_Name': zone_name} 'Surface_Type': idf_surface_type,
if boundary.parent_surface.type == cte.GROUND: 'Zone_Name': zone_name}
outside_boundary_condition = 'Ground' if boundary.parent_surface.type == cte.GROUND:
sun_exposure = 'NoSun' outside_boundary_condition = 'Ground'
wind_exposure = 'NoWind' sun_exposure = 'NoSun'
if boundary.parent_surface.percentage_shared is not None and boundary.parent_surface.percentage_shared > 0.5: wind_exposure = 'NoWind'
outside_boundary_condition = 'Surface' if boundary.parent_surface.percentage_shared is not None and boundary.parent_surface.percentage_shared > 0.5:
outside_boundary_condition_object = boundary.parent_surface.name outside_boundary_condition = 'Surface'
sun_exposure = 'NoSun' outside_boundary_condition_object = boundary.parent_surface.name
wind_exposure = 'NoWind' sun_exposure = 'NoSun'
_kwargs['Outside_Boundary_Condition_Object'] = outside_boundary_condition_object wind_exposure = 'NoWind'
_kwargs['Outside_Boundary_Condition'] = outside_boundary_condition _kwargs['Outside_Boundary_Condition_Object'] = outside_boundary_condition_object
_kwargs['Sun_Exposure'] = sun_exposure _kwargs['Outside_Boundary_Condition'] = outside_boundary_condition
_kwargs['Wind_Exposure'] = wind_exposure _kwargs['Sun_Exposure'] = sun_exposure
_kwargs['Wind_Exposure'] = wind_exposure
if boundary.parent_surface.vegetation is not None: if boundary.parent_surface.vegetation is not None:
construction_name = f'{boundary.construction_name}_{boundary.parent_surface.vegetation.name}' construction_name = f'{boundary.construction_name}_{boundary.parent_surface.vegetation.name}'
else: else:
construction_name = boundary.construction_name construction_name = boundary.construction_name
_kwargs['Construction_Name'] = construction_name _kwargs['Construction_Name'] = construction_name
surface = self._idf.newidfobject(self._SURFACE, **_kwargs) surface = self._idf.newidfobject(self._SURFACE, **_kwargs)
coordinates = self._matrix_to_list(boundary.parent_surface.solid_polygon.coordinates, coordinates = self._matrix_to_list(boundary.parent_surface.solid_polygon.coordinates,
self._city.lower_corner) self._city.lower_corner)
surface.setcoords(coordinates) surface.setcoords(coordinates)
if self._lod >= 3: if self._lod >= 3:
for internal_zone in building.internal_zones: for internal_zone in building.internal_zones:

View File

@ -4,10 +4,8 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
""" """
import math
from pathlib import Path
import numpy as np from pathlib import Path
class Obj: class Obj:
@ -19,80 +17,41 @@ class Obj:
self._path = path self._path = path
self._export() self._export()
def _ground(self, coordinate): def _to_vertex(self, coordinate):
x = coordinate[0] - self._city.lower_corner[0] x = coordinate[0] - self._city.lower_corner[0]
y = coordinate[1] - self._city.lower_corner[1] y = coordinate[1] - self._city.lower_corner[1]
z = coordinate[2] - self._city.lower_corner[2] z = coordinate[2] - self._city.lower_corner[2]
return x, y, z return f'v {x} {y} {z}\n'
def _to_vertex(self, coordinate):
x, y, z = self._ground(coordinate)
return f'v {x} {z} {y}\n'
def _to_texture_vertex(self, coordinate):
u, v, _ = self._ground(coordinate)
return f'vt {u} {v}\n'
def _to_normal_vertex(self, coordinates):
ground_vertex = []
for coordinate in coordinates:
x, y, z = self._ground(coordinate)
ground_vertex.append(np.array([x, y, z]))
# recalculate the normal to get grounded values
edge_1 = ground_vertex[1] - ground_vertex[0]
edge_2 = ground_vertex[2] - ground_vertex[0]
normal = np.cross(edge_1, edge_2)
normal = normal / np.linalg.norm(normal)
return f'vn {normal[0]} {normal[1]} {normal[2]}\n'
def _export(self): def _export(self):
if self._city.name is None: if self._city.name is None:
self._city.name = 'unknown_city' self._city.name = 'unknown_city'
obj_name = f'{self._city.name}.obj' file_name = self._city.name + '.obj'
mtl_name = f'{self._city.name}.mtl' file_path = (Path(self._path).resolve() / file_name).resolve()
obj_file_path = (Path(self._path).resolve() / obj_name).resolve()
mtl_file_path = (Path(self._path).resolve() / mtl_name).resolve()
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("Ks 1.0 1.0 1.0 # Specular color (white)\n")
mtl.write("Ns 400.0 # Specular exponent (defines shininess)\n")
vertices = {} vertices = {}
normals_index = {} with open(file_path, 'w', encoding='utf-8') as obj:
faces = []
vertex_index = 0
normal_index = 0
with open(obj_file_path, 'w', encoding='utf-8') as obj:
obj.write("# cerc-hub export\n") obj.write("# cerc-hub export\n")
obj.write(f'mtllib {mtl_name}') vertex_index = 0
faces = []
for building in self._city.buildings: for building in self._city.buildings:
obj.write(f'# building {building.name}\n') obj.write(f'# building {building.name}\n')
obj.write(f'g {building.name}\n') obj.write(f'g {building.name}\n')
obj.write('s off\n') obj.write('s off\n')
for surface in building.surfaces: for surface in building.surfaces:
obj.write(f'# surface {surface.name}\n') obj.write(f'# surface {surface.name}\n')
face = [] face = 'f '
normal = self._to_normal_vertex(surface.perimeter_polygon.coordinates)
normal_index += 1
textures = []
for coordinate in surface.perimeter_polygon.coordinates: for coordinate in surface.perimeter_polygon.coordinates:
vertex = self._to_vertex(coordinate) vertex = self._to_vertex(coordinate)
if vertex not in vertices: if vertex not in vertices:
vertex_index += 1 vertex_index += 1
vertices[vertex] = vertex_index vertices[vertex] = vertex_index
current = vertex_index current = vertex_index
obj.write(vertex) obj.write(vertex)
textures.append(self._to_texture_vertex(coordinate)) # only append if non-existing
else: else:
current = vertices[vertex] current = vertices[vertex]
face.insert(0, f'{current}/{current}/{normal_index}') # insert counterclockwise face = f'{face} {current}'
obj.writelines(normal) # add the normal
obj.writelines(textures) # add the texture vertex
faces.append(f"f {' '.join(face)}\n") faces.append(f'{face} {face.split(" ")[1]}\n')
obj.writelines(faces) obj.writelines(faces)
faces = [] faces = []

View File

@ -71,7 +71,6 @@ class EilatPhysicsParameters:
for catalog_construction in catalog_archetype.constructions: for catalog_construction in catalog_archetype.constructions:
construction = Construction() construction = Construction()
construction.type = catalog_construction.type construction.type = catalog_construction.type
construction.name = catalog_construction.name
if catalog_construction.window_ratio is not None: if catalog_construction.window_ratio is not None:
for _orientation in catalog_construction.window_ratio: for _orientation in catalog_construction.window_ratio:
if catalog_construction.window_ratio[_orientation] is None: if catalog_construction.window_ratio[_orientation] is None:

View File

@ -71,7 +71,6 @@ class NrcanPhysicsParameters:
for catalog_construction in catalog_archetype.constructions: for catalog_construction in catalog_archetype.constructions:
construction = Construction() construction = Construction()
construction.type = catalog_construction.type construction.type = catalog_construction.type
construction.name = catalog_construction.name
if catalog_construction.window_ratio is not None: if catalog_construction.window_ratio is not None:
for _orientation in catalog_construction.window_ratio: for _orientation in catalog_construction.window_ratio:
if catalog_construction.window_ratio[_orientation] is None: if catalog_construction.window_ratio[_orientation] is None:

View File

@ -73,7 +73,6 @@ class NrelPhysicsParameters:
for catalog_construction in catalog_archetype.constructions: for catalog_construction in catalog_archetype.constructions:
construction = Construction() construction = Construction()
construction.type = catalog_construction.type construction.type = catalog_construction.type
construction.name = catalog_construction.name
if catalog_construction.window_ratio is not None: if catalog_construction.window_ratio is not None:
construction.window_ratio = {'north': catalog_construction.window_ratio, construction.window_ratio = {'north': catalog_construction.window_ratio,
'east': catalog_construction.window_ratio, 'east': catalog_construction.window_ratio,

View File

@ -116,7 +116,6 @@ class Geojson:
if self._extrusion_height_field is not None: if self._extrusion_height_field is not None:
extrusion_height = float(feature['properties'][self._extrusion_height_field]) extrusion_height = float(feature['properties'][self._extrusion_height_field])
lod = 1 lod = 1
self._max_z = max(self._max_z, extrusion_height)
year_of_construction = None year_of_construction = None
if self._year_of_construction_field is not None: if self._year_of_construction_field is not None:
year_of_construction = int(feature['properties'][self._year_of_construction_field]) year_of_construction = int(feature['properties'][self._year_of_construction_field])

View File

@ -66,7 +66,12 @@ class TestExports(TestCase):
def _export(self, export_type, from_pickle=False): def _export(self, export_type, from_pickle=False):
self._complete_city = self._get_complete_city(from_pickle) self._complete_city = self._get_complete_city(from_pickle)
ExportsFactory(export_type, self._complete_city, self._output_path).export() try:
ExportsFactory(export_type, self._complete_city, self._output_path).export()
except ValueError as err:
if export_type != 'stl':
logging.warning('No backend export for STL test, skipped')
raise err
def _export_building_energy(self, export_type, from_pickle=False): def _export_building_energy(self, export_type, from_pickle=False):
self._complete_city = self._get_complete_city(from_pickle) self._complete_city = self._get_complete_city(from_pickle)
@ -78,6 +83,12 @@ class TestExports(TestCase):
""" """
self._export('obj', False) self._export('obj', False)
def test_stl_export(self):
"""
export to stl
"""
self._export('stl', False)
def test_energy_ade_export(self): def test_energy_ade_export(self):
""" """
export to energy ADE export to energy ADE