Add code comment to the classes and improve overall quality
This commit is contained in:
parent
145520c8a2
commit
4284b2cb3e
|
@ -7,7 +7,7 @@ from __future__ import annotations
|
||||||
from typing import Union
|
from typing import Union
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pyny3d.geoms as pn
|
import pyny3d.geoms as pn
|
||||||
from helpers.geometry import Geometry
|
from helpers.geometry_helper import GeometryHelper
|
||||||
|
|
||||||
|
|
||||||
class Surface:
|
class Surface:
|
||||||
|
@ -21,7 +21,7 @@ class Surface:
|
||||||
self._swr = swr
|
self._swr = swr
|
||||||
self._remove_last = remove_last
|
self._remove_last = remove_last
|
||||||
self._is_projected = is_projected
|
self._is_projected = is_projected
|
||||||
self._geometry = Geometry()
|
self._geometry_helper = GeometryHelper()
|
||||||
self._polygon = None
|
self._polygon = None
|
||||||
self._ground_polygon = None
|
self._ground_polygon = None
|
||||||
self._area = None
|
self._area = None
|
||||||
|
@ -89,7 +89,7 @@ class Surface:
|
||||||
"""
|
"""
|
||||||
if self._points is None:
|
if self._points is None:
|
||||||
self._points = np.fromstring(self._coordinates, dtype=float, sep=' ')
|
self._points = np.fromstring(self._coordinates, dtype=float, sep=' ')
|
||||||
self._points = Geometry.to_points_matrix(self._points, self._remove_last)
|
self._points = GeometryHelper.to_points_matrix(self._points, self._remove_last)
|
||||||
return self._points
|
return self._points
|
||||||
|
|
||||||
def _min_coord(self, axis):
|
def _min_coord(self, axis):
|
||||||
|
@ -153,7 +153,7 @@ class Surface:
|
||||||
coordinates = coordinates + ' '
|
coordinates = coordinates + ' '
|
||||||
coordinates = coordinates + str(x) + ' ' + str(y) + ' ' + str(z)
|
coordinates = coordinates + str(x) + ' ' + str(y) + ' ' + str(z)
|
||||||
self._ground_points = np.fromstring(coordinates, dtype=float, sep=' ')
|
self._ground_points = np.fromstring(coordinates, dtype=float, sep=' ')
|
||||||
self._ground_points = Geometry.to_points_matrix(self._ground_points, False)
|
self._ground_points = GeometryHelper.to_points_matrix(self._ground_points, False)
|
||||||
return self._ground_points
|
return self._ground_points
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -309,7 +309,7 @@ class Surface:
|
||||||
"""
|
"""
|
||||||
if self.type != 'Wall' or surface.type != 'Wall':
|
if self.type != 'Wall' or surface.type != 'Wall':
|
||||||
return
|
return
|
||||||
if self._geometry.is_almost_same_surface(self, surface):
|
if self._geometry_helper.is_almost_same_surface(self, surface):
|
||||||
intersection_area = self.intersect(surface).area
|
intersection_area = self.intersect(surface).area
|
||||||
self.add_shared(surface, intersection_area)
|
self.add_shared(surface, intersection_area)
|
||||||
surface.add_shared(self, intersection_area)
|
surface.add_shared(self, intersection_area)
|
||||||
|
|
|
@ -7,7 +7,7 @@ from typing import List
|
||||||
from city_model_structure.thermal_opening import ThermalOpening
|
from city_model_structure.thermal_opening import ThermalOpening
|
||||||
from city_model_structure.thermal_zone import ThermalZone
|
from city_model_structure.thermal_zone import ThermalZone
|
||||||
from city_model_structure.layer import Layer
|
from city_model_structure.layer import Layer
|
||||||
from helpers.configuration import Configuration
|
from helpers.configuration_helper import ConfigurationHelper
|
||||||
|
|
||||||
|
|
||||||
class ThermalBoundary:
|
class ThermalBoundary:
|
||||||
|
@ -20,7 +20,7 @@ class ThermalBoundary:
|
||||||
# ToDo: up to at least LOD2 will be just one thermal opening per Thermal boundary, review for LOD3 and LOD4
|
# ToDo: up to at least LOD2 will be just one thermal opening per Thermal boundary, review for LOD3 and LOD4
|
||||||
self._thermal_openings = [ThermalOpening()]
|
self._thermal_openings = [ThermalOpening()]
|
||||||
self._layers = None
|
self._layers = None
|
||||||
self._outside_solar_absorptance = Configuration().outside_solar_absorptance
|
self._outside_solar_absorptance = ConfigurationHelper().outside_solar_absorptance
|
||||||
self._outside_thermal_absorptance = None
|
self._outside_thermal_absorptance = None
|
||||||
self._outside_visible_absorptance = None
|
self._outside_visible_absorptance = None
|
||||||
self._window_ratio = None
|
self._window_ratio = None
|
||||||
|
@ -208,8 +208,8 @@ class ThermalBoundary:
|
||||||
:return: float
|
:return: float
|
||||||
"""
|
"""
|
||||||
if self._u_value is None:
|
if self._u_value is None:
|
||||||
h_i = Configuration().h_i
|
h_i = ConfigurationHelper().h_i
|
||||||
h_e = Configuration().h_e
|
h_e = ConfigurationHelper().h_e
|
||||||
r_value = 1.0/h_i + 1.0/h_e
|
r_value = 1.0/h_i + 1.0/h_e
|
||||||
try:
|
try:
|
||||||
for layer in self.layers:
|
for layer in self.layers:
|
||||||
|
|
|
@ -3,7 +3,7 @@ ThermalOpening module
|
||||||
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 helpers.configuration import Configuration
|
from helpers.configuration_helper import ConfigurationHelper
|
||||||
|
|
||||||
|
|
||||||
class ThermalOpening:
|
class ThermalOpening:
|
||||||
|
@ -13,7 +13,7 @@ class ThermalOpening:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._openable_ratio = None
|
self._openable_ratio = None
|
||||||
self._conductivity = None
|
self._conductivity = None
|
||||||
self._frame_ratio = Configuration().frame_ratio
|
self._frame_ratio = ConfigurationHelper().frame_ratio
|
||||||
self._g_value = None
|
self._g_value = None
|
||||||
self._thickness = None
|
self._thickness = None
|
||||||
self._front_side_solar_transmittance_at_normal_incidence = None
|
self._front_side_solar_transmittance_at_normal_incidence = None
|
||||||
|
@ -56,8 +56,8 @@ class ThermalOpening:
|
||||||
# This ensures a more robust code that returns the overall_u_value regardless the order the parameters are read.
|
# This ensures a more robust code that returns the overall_u_value regardless the order the parameters are read.
|
||||||
self._conductivity = value
|
self._conductivity = value
|
||||||
if self._overall_u_value is None and self.thickness is not None:
|
if self._overall_u_value is None and self.thickness is not None:
|
||||||
h_i = Configuration().h_i
|
h_i = ConfigurationHelper().h_i
|
||||||
h_e = Configuration().h_e
|
h_e = ConfigurationHelper().h_e
|
||||||
r_value = 1 / h_i + 1 / h_e + float(self.conductivity) / float(self.thickness)
|
r_value = 1 / h_i + 1 / h_e + float(self.conductivity) / float(self.thickness)
|
||||||
self._overall_u_value = 1 / r_value
|
self._overall_u_value = 1 / r_value
|
||||||
|
|
||||||
|
@ -114,8 +114,8 @@ class ThermalOpening:
|
||||||
# This ensures a more robust code that returns the overall_u_value regardless the order the parameters are read.
|
# This ensures a more robust code that returns the overall_u_value regardless the order the parameters are read.
|
||||||
self._thickness = value
|
self._thickness = value
|
||||||
if self._overall_u_value is None and self.conductivity is not None:
|
if self._overall_u_value is None and self.conductivity is not None:
|
||||||
h_i = Configuration().h_i
|
h_i = ConfigurationHelper().h_i
|
||||||
h_e = Configuration().h_e
|
h_e = ConfigurationHelper().h_e
|
||||||
r_value = 1 / h_i + 1 / h_e + float(self.conductivity) / float(self.thickness)
|
r_value = 1 / h_i + 1 / h_e + float(self.conductivity) / float(self.thickness)
|
||||||
self._overall_u_value = 1 / r_value
|
self._overall_u_value = 1 / r_value
|
||||||
|
|
||||||
|
@ -156,7 +156,7 @@ class ThermalOpening:
|
||||||
@property
|
@property
|
||||||
def overall_u_value(self):
|
def overall_u_value(self):
|
||||||
"""
|
"""
|
||||||
Get thermal opening overall u value
|
Get thermal opening overall u value in W/m2K
|
||||||
:return: float
|
:return: float
|
||||||
"""
|
"""
|
||||||
return self._overall_u_value
|
return self._overall_u_value
|
||||||
|
@ -164,7 +164,7 @@ class ThermalOpening:
|
||||||
@overall_u_value.setter
|
@overall_u_value.setter
|
||||||
def overall_u_value(self, value):
|
def overall_u_value(self, value):
|
||||||
"""
|
"""
|
||||||
Get thermal opening overall u value
|
Get thermal opening overall u value in W/m2K
|
||||||
:param value: float
|
:param value: float
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -6,7 +6,7 @@ Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc
|
||||||
from typing import List, TypeVar
|
from typing import List, TypeVar
|
||||||
from city_model_structure.usage_zone import UsageZone
|
from city_model_structure.usage_zone import UsageZone
|
||||||
from city_model_structure.surface import Surface
|
from city_model_structure.surface import Surface
|
||||||
from helpers.configuration import Configuration
|
from helpers.configuration_helper import ConfigurationHelper
|
||||||
|
|
||||||
ThermalBoundary = TypeVar('ThermalBoundary')
|
ThermalBoundary = TypeVar('ThermalBoundary')
|
||||||
|
|
||||||
|
@ -19,12 +19,12 @@ class ThermalZone:
|
||||||
self._surfaces = surfaces
|
self._surfaces = surfaces
|
||||||
self._floor_area = None
|
self._floor_area = None
|
||||||
self._bounded = None
|
self._bounded = None
|
||||||
self._heated = Configuration().heated
|
self._heated = ConfigurationHelper().heated
|
||||||
self._cooled = Configuration().cooled
|
self._cooled = ConfigurationHelper().cooled
|
||||||
self._additional_thermal_bridge_u_value = Configuration().additional_thermal_bridge_u_value
|
self._additional_thermal_bridge_u_value = ConfigurationHelper().additional_thermal_bridge_u_value
|
||||||
self._effective_thermal_capacity = None
|
self._effective_thermal_capacity = None
|
||||||
self._indirectly_heated_area_ratio = Configuration().indirectly_heated_area_ratio
|
self._indirectly_heated_area_ratio = ConfigurationHelper().indirectly_heated_area_ratio
|
||||||
self._infiltration_rate_system_on = Configuration().infiltration_rate_system_on
|
self._infiltration_rate_system_on = ConfigurationHelper().infiltration_rate_system_on
|
||||||
self._infiltration_rate_system_off = None
|
self._infiltration_rate_system_off = None
|
||||||
self._usage_zones = None
|
self._usage_zones = None
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ class ThermalZone:
|
||||||
@property
|
@property
|
||||||
def additional_thermal_bridge_u_value(self):
|
def additional_thermal_bridge_u_value(self):
|
||||||
"""
|
"""
|
||||||
Get thermal zone additional thermal bridge u value
|
Get thermal zone additional thermal bridge u value W/m2K
|
||||||
:return: float
|
:return: float
|
||||||
"""
|
"""
|
||||||
return self._additional_thermal_bridge_u_value
|
return self._additional_thermal_bridge_u_value
|
||||||
|
@ -93,7 +93,7 @@ class ThermalZone:
|
||||||
@additional_thermal_bridge_u_value.setter
|
@additional_thermal_bridge_u_value.setter
|
||||||
def additional_thermal_bridge_u_value(self, value):
|
def additional_thermal_bridge_u_value(self, value):
|
||||||
"""
|
"""
|
||||||
Set thermal zone additional thermal bridge u value
|
Set thermal zone additional thermal bridge u value W/m2K
|
||||||
:param value: float
|
:param value: float
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
"""
|
|
||||||
Configuration helper
|
|
||||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
|
||||||
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
|
||||||
"""
|
|
||||||
import configparser
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
|
|
||||||
class Configuration:
|
|
||||||
"""
|
|
||||||
Configuration class
|
|
||||||
"""
|
|
||||||
def __init__(self):
|
|
||||||
base_path = Path().resolve().parent
|
|
||||||
config_file = Path(base_path / 'libs/config/configuration.ini').resolve()
|
|
||||||
self._config = configparser.ConfigParser()
|
|
||||||
self._config.read(config_file)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def h_i(self):
|
|
||||||
"""
|
|
||||||
Configured internal convective coefficient in W/m2K
|
|
||||||
:return: float
|
|
||||||
"""
|
|
||||||
return self._config.getfloat('convective_fluxes', 'h_i')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def h_e(self):
|
|
||||||
"""
|
|
||||||
Configured external convective coefficient in W/m2K
|
|
||||||
:return: float
|
|
||||||
"""
|
|
||||||
return self._config.getfloat('convective_fluxes', 'h_e')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def frame_ratio(self):
|
|
||||||
"""
|
|
||||||
Configured frame ratio
|
|
||||||
:return: float
|
|
||||||
"""
|
|
||||||
return self._config.getfloat('windows', 'frame_ratio')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def heated(self):
|
|
||||||
"""
|
|
||||||
Configured heated flag
|
|
||||||
:return: Boolean
|
|
||||||
"""
|
|
||||||
return self._config.getboolean('thermal_zones', 'heated')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def cooled(self):
|
|
||||||
"""
|
|
||||||
Configured cooled flag
|
|
||||||
:return: Boolean
|
|
||||||
"""
|
|
||||||
return self._config.getboolean('thermal_zones', 'cooled')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def additional_thermal_bridge_u_value(self):
|
|
||||||
"""
|
|
||||||
Configured additional thermal bridge u value W/m2K
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
return self._config.getfloat('thermal_zones', 'additional_thermal_bridge_u_value')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def indirectly_heated_area_ratio(self):
|
|
||||||
"""
|
|
||||||
Configured indirectly heated area ratio
|
|
||||||
:return: float
|
|
||||||
"""
|
|
||||||
|
|
||||||
return self._config.getfloat('thermal_zones', 'indirectly_heated_area_ratio')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def infiltration_rate_system_on(self):
|
|
||||||
"""
|
|
||||||
Configured infiltration rate system on in air change per hour
|
|
||||||
:return: float
|
|
||||||
"""
|
|
||||||
return self._config.getfloat('thermal_zones', 'infiltration_rate_system_on')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def outside_solar_absorptance(self):
|
|
||||||
"""
|
|
||||||
Configured infiltration rate system off in air change per hour
|
|
||||||
:return: float
|
|
||||||
"""
|
|
||||||
return self._config.getfloat('thermal_zones', 'outside_solar_absorptance')
|
|
|
@ -1,165 +0,0 @@
|
||||||
"""
|
|
||||||
Geometry helper
|
|
||||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
|
||||||
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
|
||||||
"""
|
|
||||||
import math
|
|
||||||
import numpy as np
|
|
||||||
from trimesh import Trimesh
|
|
||||||
from trimesh import intersections
|
|
||||||
import open3d as o3d
|
|
||||||
|
|
||||||
|
|
||||||
class Geometry:
|
|
||||||
"""
|
|
||||||
Geometry helper class
|
|
||||||
"""
|
|
||||||
def __init__(self, delta=0.5):
|
|
||||||
self._delta = delta
|
|
||||||
|
|
||||||
def almost_equal(self, v1, v2):
|
|
||||||
"""
|
|
||||||
Compare two points and decides if they are almost equal (quadratic error under delta)
|
|
||||||
:param v1: [x,y,z]
|
|
||||||
:param v2: [x,y,z]
|
|
||||||
:return: Boolean
|
|
||||||
"""
|
|
||||||
delta = math.sqrt(pow((v1[0] - v2[0]), 2) + pow((v1[1] - v2[1]), 2) + pow((v1[2] - v2[2]), 2))
|
|
||||||
return delta <= self._delta
|
|
||||||
|
|
||||||
def is_almost_same_surface(self, s1, s2):
|
|
||||||
"""
|
|
||||||
Compare two surfaces and decides if they are almost equal (quadratic error under delta)
|
|
||||||
:param s1: Surface
|
|
||||||
:param s2: Surface
|
|
||||||
:return: Boolean
|
|
||||||
"""
|
|
||||||
# delta is grads an need to be converted into radians
|
|
||||||
delta = np.rad2deg(self._delta)
|
|
||||||
difference = (s1.inclination - s2.inclination) % math.pi
|
|
||||||
if abs(difference) > delta:
|
|
||||||
return False
|
|
||||||
# s1 and s2 are at least almost parallel surfaces
|
|
||||||
# calculate distance point to plane using all the vertex
|
|
||||||
# select surface1 value for the point (X,Y,Z) where two of the values are 0
|
|
||||||
minimum_distance = self._delta + 1
|
|
||||||
parametric = s2.polygon.get_parametric()
|
|
||||||
n2 = s2.normal
|
|
||||||
for point in s1.points:
|
|
||||||
distance = abs(
|
|
||||||
(point[0] * parametric[0]) + (point[1] * parametric[1]) + (point[2] * parametric[2]) + parametric[3])
|
|
||||||
normal_module = math.sqrt(pow(n2[0], 2) + pow(n2[1], 2) + pow(n2[2], 2))
|
|
||||||
|
|
||||||
if normal_module == 0:
|
|
||||||
continue
|
|
||||||
distance = distance / normal_module
|
|
||||||
if distance < minimum_distance:
|
|
||||||
minimum_distance = distance
|
|
||||||
if minimum_distance <= self._delta:
|
|
||||||
break
|
|
||||||
if minimum_distance > self._delta or s1.intersect(s2) is None:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def to_points_matrix(points, remove_last=False):
|
|
||||||
"""
|
|
||||||
Transform a point vector into a point matrix
|
|
||||||
:param points: [x, y, z, x, y, z ...]
|
|
||||||
:param remove_last: Boolean
|
|
||||||
:return: [[x,y,z],[x,y,z]...]
|
|
||||||
"""
|
|
||||||
rows = points.size // 3
|
|
||||||
points = points.reshape(rows, 3)
|
|
||||||
if remove_last:
|
|
||||||
points = np.delete(points, rows - 1, 0)
|
|
||||||
return points
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _segment_list_to_point_cloud(segment_list):
|
|
||||||
point_list = np.asarray(segment_list[0])
|
|
||||||
for segment in segment_list:
|
|
||||||
for new_point in segment:
|
|
||||||
found = False
|
|
||||||
for point in point_list:
|
|
||||||
same_point = np.allclose(new_point, point)
|
|
||||||
if same_point:
|
|
||||||
found = True
|
|
||||||
break
|
|
||||||
if not found:
|
|
||||||
point_list = np.concatenate((point_list, [new_point]))
|
|
||||||
return point_list
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _point_cloud_to_mesh(point_list, normal_list):
|
|
||||||
# Return a mesh composed only by triangles
|
|
||||||
pcd = o3d.geometry.PointCloud()
|
|
||||||
pcd.points = o3d.utility.Vector3dVector(point_list)
|
|
||||||
pcd.normals = o3d.utility.Vector3dVector(normal_list)
|
|
||||||
distances = pcd.compute_nearest_neighbor_distance()
|
|
||||||
avg_dist = np.mean(distances)
|
|
||||||
radius = 3 * avg_dist
|
|
||||||
bpa_mesh = o3d.geometry.TriangleMesh().create_from_point_cloud_ball_pivoting(
|
|
||||||
pcd, o3d.utility.DoubleVector([radius, radius * 2]))
|
|
||||||
mesh_result = Trimesh(vertices=np.asarray(bpa_mesh.vertices), faces=np.asarray(bpa_mesh.triangles))
|
|
||||||
return mesh_result
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _merge_meshes(mesh1, mesh2):
|
|
||||||
v_1 = mesh1.vertices
|
|
||||||
f_1 = mesh1.faces
|
|
||||||
v_2 = mesh2.vertices
|
|
||||||
f_2 = mesh2.faces
|
|
||||||
length = len(v_1)
|
|
||||||
v_merge = np.concatenate((v_1, v_2))
|
|
||||||
f_merge = np.asarray(f_1)
|
|
||||||
|
|
||||||
for item in f_2:
|
|
||||||
point1 = item.item(0) + length
|
|
||||||
point2 = item.item(1) + length
|
|
||||||
point3 = item.item(2) + length
|
|
||||||
surface = np.asarray([point1, point2, point3])
|
|
||||||
f_merge = np.concatenate((f_merge, [surface]))
|
|
||||||
|
|
||||||
mesh_merge = Trimesh(vertices=v_merge, faces=f_merge)
|
|
||||||
|
|
||||||
return mesh_merge
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def divide_mesh_by_plane(mesh, normal_plane, point_plane):
|
|
||||||
"""
|
|
||||||
Divide a mesh by a plane
|
|
||||||
:param mesh: Trimesh
|
|
||||||
:param normal_plane: [x, y, z]
|
|
||||||
:param point_plane: [x, y, z]
|
|
||||||
:return: [Trimesh]
|
|
||||||
"""
|
|
||||||
# The first mesh returns the positive side of the plane and the second the negative side.
|
|
||||||
# If the plane does not divide the mesh (i.e. it does not touch it or it is coplanar with one or more faces),
|
|
||||||
# then it returns only the original mesh.
|
|
||||||
normal_plane_opp = [None] * len(normal_plane)
|
|
||||||
for i in range(0, len(normal_plane)):
|
|
||||||
normal_plane_opp[i] = - normal_plane[i]
|
|
||||||
|
|
||||||
normal = [normal_plane, normal_plane_opp]
|
|
||||||
normal_opp = [normal_plane_opp, normal_plane]
|
|
||||||
mesh_final = []
|
|
||||||
for i in range(0, 2):
|
|
||||||
mesh_1 = intersections.slice_mesh_plane(mesh, normal[i], point_plane)
|
|
||||||
mesh_1_segments = intersections.mesh_plane(mesh, normal[i], point_plane)
|
|
||||||
boo = mesh.difference(mesh_1, engine='blender')
|
|
||||||
print(boo)
|
|
||||||
quit()
|
|
||||||
if len(mesh_1_segments) <= 0 or len(mesh_1.faces) == len(mesh.faces):
|
|
||||||
mesh_final.append(mesh)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
points = Geometry._segment_list_to_point_cloud(mesh_1_segments)
|
|
||||||
points_normals = [[None] * 3] * len(points)
|
|
||||||
for j in range(0, len(points_normals)):
|
|
||||||
points_normals[j] = normal_opp[i]
|
|
||||||
mesh_2 = Geometry._point_cloud_to_mesh(points, points_normals)
|
|
||||||
mesh_final.append(Geometry._merge_meshes(mesh_1, mesh_2))
|
|
||||||
|
|
||||||
return mesh_final
|
|
|
@ -35,7 +35,7 @@ class UsBasePhysicsParameters:
|
||||||
archetype = self._search_archetype(building_type,
|
archetype = self._search_archetype(building_type,
|
||||||
UsToLibraryTypes.yoc_to_standard(city_object.year_of_construction),
|
UsToLibraryTypes.yoc_to_standard(city_object.year_of_construction),
|
||||||
self._climate_zone)
|
self._climate_zone)
|
||||||
# ToDo:remove this in the future
|
# ToDo: remove this in the future
|
||||||
# ToDo: Raise WrongArchetype if not all the surface types are defined for the given city_object
|
# ToDo: Raise WrongArchetype if not all the surface types are defined for the given city_object
|
||||||
if archetype is None:
|
if archetype is None:
|
||||||
print(building_type, UsToLibraryTypes.yoc_to_standard(city_object.year_of_construction),
|
print(building_type, UsToLibraryTypes.yoc_to_standard(city_object.year_of_construction),
|
||||||
|
|
|
@ -4,9 +4,9 @@ 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 unittest import TestCase
|
from unittest import TestCase
|
||||||
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from geometry.geometry_factory import GeometryFactory
|
from geometry.geometry_factory import GeometryFactory
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
class TestGeometryFactory(TestCase):
|
class TestGeometryFactory(TestCase):
|
||||||
|
@ -21,11 +21,7 @@ class TestGeometryFactory(TestCase):
|
||||||
self._city_gml = None
|
self._city_gml = None
|
||||||
self._example_path = (Path(__file__).parent.parent / 'tests_data').resolve()
|
self._example_path = (Path(__file__).parent.parent / 'tests_data').resolve()
|
||||||
|
|
||||||
def get_citygml(self):
|
def _get_citygml(self):
|
||||||
"""
|
|
||||||
Retrieve the test city gml
|
|
||||||
:return: City
|
|
||||||
"""
|
|
||||||
if self._city_gml is None:
|
if self._city_gml is None:
|
||||||
file_path = (self._example_path / 'buildings.gml').resolve()
|
file_path = (self._example_path / 'buildings.gml').resolve()
|
||||||
self._city_gml = GeometryFactory('citygml', file_path).city
|
self._city_gml = GeometryFactory('citygml', file_path).city
|
||||||
|
@ -37,7 +33,7 @@ class TestGeometryFactory(TestCase):
|
||||||
Test the City parsing
|
Test the City parsing
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
city = self.get_citygml()
|
city = self._get_citygml()
|
||||||
self.assertIsNotNone(city.city_objects, 'city_objects is none')
|
self.assertIsNotNone(city.city_objects, 'city_objects is none')
|
||||||
for city_object in city.city_objects:
|
for city_object in city.city_objects:
|
||||||
self.assertIsNotNone(city.city_object(city_object.name), 'city_object return none')
|
self.assertIsNotNone(city.city_object(city_object.name), 'city_object return none')
|
||||||
|
@ -52,7 +48,7 @@ class TestGeometryFactory(TestCase):
|
||||||
Test city objects in the city
|
Test city objects in the city
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
city = self.get_citygml()
|
city = self._get_citygml()
|
||||||
for city_object in city.city_objects:
|
for city_object in city.city_objects:
|
||||||
self.assertIsNotNone(city_object.name, 'city_object name is none')
|
self.assertIsNotNone(city_object.name, 'city_object name is none')
|
||||||
self.assertIsNotNone(city_object.lod, 'city_object lod is none')
|
self.assertIsNotNone(city_object.lod, 'city_object lod is none')
|
||||||
|
@ -80,7 +76,7 @@ class TestGeometryFactory(TestCase):
|
||||||
Test surfaces in city objects
|
Test surfaces in city objects
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
city = self.get_citygml()
|
city = self._get_citygml()
|
||||||
for city_object in city.city_objects:
|
for city_object in city.city_objects:
|
||||||
for surface in city_object.surfaces:
|
for surface in city_object.surfaces:
|
||||||
self.assertIsNotNone(surface.name, 'surface name is none')
|
self.assertIsNotNone(surface.name, 'surface name is none')
|
||||||
|
@ -111,7 +107,7 @@ class TestGeometryFactory(TestCase):
|
||||||
Test thermal zones in city objects
|
Test thermal zones in city objects
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
city = self.get_citygml()
|
city = self._get_citygml()
|
||||||
for city_object in city.city_objects:
|
for city_object in city.city_objects:
|
||||||
for thermal_zone in city_object.thermal_zones:
|
for thermal_zone in city_object.thermal_zones:
|
||||||
self.assertIsNotNone(thermal_zone.surfaces, 'thermal_zone surfaces is none')
|
self.assertIsNotNone(thermal_zone.surfaces, 'thermal_zone surfaces is none')
|
||||||
|
@ -137,7 +133,7 @@ class TestGeometryFactory(TestCase):
|
||||||
Test thermal boundaries in thermal zones
|
Test thermal boundaries in thermal zones
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
city = self.get_citygml()
|
city = self._get_citygml()
|
||||||
for city_object in city.city_objects:
|
for city_object in city.city_objects:
|
||||||
for thermal_zone in city_object.thermal_zones:
|
for thermal_zone in city_object.thermal_zones:
|
||||||
for thermal_boundary in thermal_zone.bounded:
|
for thermal_boundary in thermal_zone.bounded:
|
||||||
|
@ -167,7 +163,7 @@ class TestGeometryFactory(TestCase):
|
||||||
Test thermal openings in thermal zones
|
Test thermal openings in thermal zones
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
city = self.get_citygml()
|
city = self._get_citygml()
|
||||||
for city_object in city.city_objects:
|
for city_object in city.city_objects:
|
||||||
for thermal_zone in city_object.thermal_zones:
|
for thermal_zone in city_object.thermal_zones:
|
||||||
for thermal_boundary in thermal_zone.bounded:
|
for thermal_boundary in thermal_zone.bounded:
|
||||||
|
|
|
@ -13,26 +13,34 @@ class TestPhysicsFactory(TestCase):
|
||||||
"""
|
"""
|
||||||
TestPhysicsFactory TestCase
|
TestPhysicsFactory TestCase
|
||||||
"""
|
"""
|
||||||
def setup(self) -> None:
|
def setUp(self) -> None:
|
||||||
|
"""
|
||||||
|
Configure test environment
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
self._city_gml = None
|
self._city_gml = None
|
||||||
self._nyc_with_physics = None
|
self._nyc_with_physics = None
|
||||||
self._example_path = (Path(__file__).parent.parent / 'tests_data').resolve()
|
self._example_path = (Path(__file__).parent.parent / 'tests_data').resolve()
|
||||||
|
|
||||||
def get_citygml(self):
|
def _get_citygml(self):
|
||||||
if self._city_gml is None:
|
if self._city_gml is None:
|
||||||
file_path = (self._example_path / 'buildings.gml').resolve()
|
file_path = (self._example_path / 'buildings.gml').resolve()
|
||||||
self._city_gml = GeometryFactory('citygml', file_path).city
|
self._city_gml = GeometryFactory('citygml', file_path).city
|
||||||
self.assertIsNotNone(self._city_gml, 'city is none')
|
self.assertIsNotNone(self._city_gml, 'city is none')
|
||||||
return self._city_gml
|
return self._city_gml
|
||||||
|
|
||||||
def get_city_with_physics(self):
|
def _get_city_with_physics(self):
|
||||||
if self._nyc_with_physics is None:
|
if self._nyc_with_physics is None:
|
||||||
self._nyc_with_physics = self.get_citygml()
|
self._nyc_with_physics = self._get_citygml()
|
||||||
PhysicsFactory('us_new_york_city', self._nyc_with_physics, base_path=self._example_path)
|
PhysicsFactory('us_new_york_city', self._nyc_with_physics, base_path=self._example_path)
|
||||||
return self._nyc_with_physics
|
return self._nyc_with_physics
|
||||||
|
|
||||||
def test_city_with_physics(self):
|
def test_city_with_physics(self):
|
||||||
city = self.get_city_with_physics()
|
"""
|
||||||
|
Enrich the city with the physic information and verify ot
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
city = self._get_city_with_physics()
|
||||||
for city_object in city.city_objects:
|
for city_object in city.city_objects:
|
||||||
self.assertIsNotNone(city_object.average_storey_height, 'average_storey_height is none')
|
self.assertIsNotNone(city_object.average_storey_height, 'average_storey_height is none')
|
||||||
self.assertIsNotNone(city_object.storeys_above_ground, 'storeys_above_ground is none')
|
self.assertIsNotNone(city_object.storeys_above_ground, 'storeys_above_ground is none')
|
||||||
|
|
|
@ -8,22 +8,29 @@ from usage.usage_feeders.us_new_york_city_usage_parameters import UsNewYorkCityU
|
||||||
|
|
||||||
|
|
||||||
class UsageFactory:
|
class UsageFactory:
|
||||||
|
"""
|
||||||
|
UsageFactory class
|
||||||
|
"""
|
||||||
def __init__(self, handler, city):
|
def __init__(self, handler, city):
|
||||||
self._handler = handler.lower().replace(' ', '_')
|
self._handler = '_' + handler.lower().replace(' ', '_')
|
||||||
self._city = city
|
self._city = city
|
||||||
self.factory()
|
self.factory()
|
||||||
|
|
||||||
def us_new_york_city(self):
|
def _us_new_york_city(self):
|
||||||
UsNewYorkCityUsageParameters(self._city)
|
UsNewYorkCityUsageParameters(self._city)
|
||||||
|
|
||||||
def ca(self):
|
def _ca(self):
|
||||||
raise Exception('Not implemented')
|
raise Exception('Not implemented')
|
||||||
|
|
||||||
def de(self):
|
def _de(self):
|
||||||
DeUsageParameters(self._city)
|
DeUsageParameters(self._city)
|
||||||
|
|
||||||
def es(self):
|
def _es(self):
|
||||||
raise Exception('Not implemented')
|
raise Exception('Not implemented')
|
||||||
|
|
||||||
def factory(self):
|
def factory(self):
|
||||||
|
"""
|
||||||
|
Enrich the city with the usage information
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
getattr(self, self._handler, lambda: None)()
|
getattr(self, self._handler, lambda: None)()
|
||||||
|
|
|
@ -3,12 +3,15 @@ DeUsageParameters model the usage properties for a German building
|
||||||
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
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import xmltodict
|
||||||
from usage.usage_feeders.helpers.us_function_to_usage import UsFunctionToUsage
|
from usage.usage_feeders.helpers.us_function_to_usage import UsFunctionToUsage
|
||||||
|
|
||||||
|
|
||||||
class DeUsageParameters:
|
class DeUsageParameters:
|
||||||
|
"""
|
||||||
|
DeUsageParameters
|
||||||
|
"""
|
||||||
def __init__(self, city_objects):
|
def __init__(self, city_objects):
|
||||||
self._city_objects = city_objects
|
self._city_objects = city_objects
|
||||||
|
|
||||||
|
@ -17,4 +20,4 @@ class DeUsageParameters:
|
||||||
with open(path) as xml:
|
with open(path) as xml:
|
||||||
self._library = xmltodict.parse(xml.read())
|
self._library = xmltodict.parse(xml.read())
|
||||||
for city_object in city_objects:
|
for city_object in city_objects:
|
||||||
UsFunctionToUsage.function_to_usage(city_object.function)
|
UsFunctionToUsage.usage(city_object.function)
|
||||||
|
|
|
@ -3,13 +3,16 @@ UsBaseUsageParameters base class to model the usage properties for a building in
|
||||||
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
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import xmltodict
|
||||||
from city_model_structure.usage_zone import UsageZone
|
from city_model_structure.usage_zone import UsageZone
|
||||||
from city_model_structure.internal_gains import InternalGains
|
from city_model_structure.internal_gains import InternalGains
|
||||||
|
|
||||||
|
|
||||||
class UsBaseUsageParameters:
|
class UsBaseUsageParameters:
|
||||||
|
"""
|
||||||
|
UsBaseUsageParameters class
|
||||||
|
"""
|
||||||
def __init__(self, city, function_to_usage):
|
def __init__(self, city, function_to_usage):
|
||||||
self._city = city
|
self._city = city
|
||||||
# ToDo: this is using the german library as a temporary approach, need to use/define a library for US
|
# ToDo: this is using the german library as a temporary approach, need to use/define a library for US
|
||||||
|
@ -17,7 +20,7 @@ class UsBaseUsageParameters:
|
||||||
with open(path) as xml:
|
with open(path) as xml:
|
||||||
self._library = xmltodict.parse(xml.read(), force_list='zoneUsageVariant')
|
self._library = xmltodict.parse(xml.read(), force_list='zoneUsageVariant')
|
||||||
for city_object in self._city.city_objects:
|
for city_object in self._city.city_objects:
|
||||||
#ToDo: Right now is just one usage zone but will be multiple in the future
|
# ToDo: Right now is just one usage zone but will be multiple in the future
|
||||||
usage_zone = UsageZone()
|
usage_zone = UsageZone()
|
||||||
usage_zone.usage = function_to_usage(city_object.function)
|
usage_zone.usage = function_to_usage(city_object.function)
|
||||||
for zone_usage_type in self._library['buildingUsageLibrary']['zoneUsageType']:
|
for zone_usage_type in self._library['buildingUsageLibrary']['zoneUsageType']:
|
||||||
|
@ -31,14 +34,12 @@ class UsBaseUsageParameters:
|
||||||
city_object.usage_zone = [usage_zone]
|
city_object.usage_zone = [usage_zone]
|
||||||
break
|
break
|
||||||
continue
|
continue
|
||||||
else:
|
|
||||||
city_object.usage_zones = [UsBaseUsageParameters._parse_zone_usage_type(zone_usage_type, usage_zone)]
|
city_object.usage_zones = [UsBaseUsageParameters._parse_zone_usage_type(zone_usage_type, usage_zone)]
|
||||||
break
|
break
|
||||||
if city_object.usage_zones is None:
|
if city_object.usage_zones is None:
|
||||||
print(city_object.function)
|
print(city_object.function)
|
||||||
raise Exception('Usage not found for building function')
|
raise Exception('Usage not found for building function')
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parse_zone_usage_type(zone_usage_type, usage_zone):
|
def _parse_zone_usage_type(zone_usage_type, usage_zone):
|
||||||
usage_zone.hours_day = zone_usage_type['occupancy']['usageHoursPerDay']
|
usage_zone.hours_day = zone_usage_type['occupancy']['usageHoursPerDay']
|
||||||
|
|
|
@ -8,6 +8,9 @@ from usage.usage_feeders.helpers.us_pluto_to_usage import UsPlutoToUsage as Pu
|
||||||
|
|
||||||
|
|
||||||
class UsNewYorkCityUsageParameters(UsBaseUsageParameters):
|
class UsNewYorkCityUsageParameters(UsBaseUsageParameters):
|
||||||
|
"""
|
||||||
|
UsNewYorkCityUsageParameters class
|
||||||
|
"""
|
||||||
def __init__(self, city):
|
def __init__(self, city):
|
||||||
self._city = city
|
self._city = city
|
||||||
super().__init__(self._city, Pu.usage)
|
super().__init__(self._city, Pu.usage)
|
||||||
|
|
|
@ -8,5 +8,8 @@ from usage.usage_feeders.helpers.us_function_to_usage import UsFunctionToUsage
|
||||||
|
|
||||||
|
|
||||||
class UsUsageParameters(UsBaseUsageParameters):
|
class UsUsageParameters(UsBaseUsageParameters):
|
||||||
|
"""
|
||||||
|
UsUsageParameters class
|
||||||
|
"""
|
||||||
def __init__(self, city):
|
def __init__(self, city):
|
||||||
super().__init__(city, UsFunctionToUsage.usage)
|
super().__init__(city, UsFunctionToUsage.usage)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user