modified structure

This commit is contained in:
pilar 2020-10-28 13:42:58 -04:00
parent 94c593b651
commit b3f8647036
21 changed files with 2145 additions and 0 deletions

View File

@ -0,0 +1,53 @@
"""
Building module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Sanam Dabirian sanam.dabirian@mail.concordia.ca
"""
class DomesticHotWaterFacility:
"""
Domestic Hot Water facilities class
"""
def __init__(self, number_of_baths, number_of_showers, number_of_basin, water_storage_volume):
"""
Constructor
"""
self._number_of_baths = number_of_baths
self._number_of_showers = number_of_showers
self._number_of_basin = number_of_basin
self._water_storage_volume = water_storage_volume
@property
def number_of_baths(self):
"""
Get number of baths of a building unit
:return: number of baths
"""
return self._number_of_baths
@property
def number_of_showers(self):
"""
Get number of showers of a building unit
:return: number of showers
"""
return self._number_of_showers
@property
def number_of_basin(self):
"""
Get number of wash basins of a building unit
:return: number of wash basins
"""
return self._number_of_basin
@property
def water_storage_volume(self):
"""
Get the volume of water storage
:return: volume of water storage
"""
return self._water_storage_volume

View File

@ -0,0 +1,26 @@
"""
Building module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Sanam Dabirian sanam.dabirian@mail.concordia.ca
"""
class appliances_facilities:
"""
Electric appliance facilities class
"""
def __init__(self, appliance_electric_power):
"""
Constructor
"""
self._appliance_electric_power = appliance_electric_power
@property
def appliance_electric_power(self):
"""
Get appliances electric power
:return: appliances electric power
"""
return self._appliance_electric_power

View File

@ -0,0 +1,59 @@
"""
Building module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Sanam Dabirian sanam.dabirian@mail.concordia.ca
"""
class Facility:
"""
facilities class
"""
def __init__(self, operation_schedules, convective_fraction, latent_fraction,
radiant_fraction, total_value_of_heat_dissipation):
self._operation_schedules = operation_schedules
self._convective_fraction = convective_fraction
self._latent_fraction = latent_fraction
self._radiant_fraction = radiant_fraction
self._total_value_of_heat_dissipation = total_value_of_heat_dissipation
@property
def operation_schedules(self):
"""
Get operation schedules of the facilities
:return: operation schedules
"""
return self._operation_schedules
@property
def convective_fraction(self):
"""
Get convective fraction value
:return: convective fraction
"""
return self._convective_fraction
@property
def latent_fraction(self):
"""
Get latent fraction value
:return: latent fraction
"""
return self._latent_fraction
@property
def radiant_fraction(self):
"""
Get radiant fraction value
:return: radiant fraction
"""
return self._radiant_fraction
@property
def total_value_of_heat_dissipation(self):
"""
Get heat dissipation value
:return: heat dissipation
"""
return self._total_value_of_heat_dissipation

View File

@ -0,0 +1,36 @@
"""
Building module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Sanam Dabirian sanam.dabirian@mail.concordia.ca
"""
class household:
"""
Household class
"""
def __init__(self, resident_type, household_type):
"""
Constructor
"""
self._resident_type = resident_type
self._household_type = household_type
@property
def resident_type(self):
"""
Get resident type
:return: resident type
"""
return self._resident_type
@property
def household_type(self):
"""
Get household type
:return: household type
"""
return self._household_type

View File

@ -0,0 +1,36 @@
"""
Building module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Sanam Dabirian sanam.dabirian@mail.concordia.ca
"""
class HvacFacility:
"""
HVAC facilities class
"""
def __init__(self, temperature_setpoints, hvac_schedules):
"""
Constructor
"""
self._temperature_setpoints = temperature_setpoints
self._hvac_schedules = hvac_schedules
@property
def temperature_setpoints(self):
"""
Get temperature setpoints
:return: temperature setpoints
"""
return self._temperature_setpoints
@property
def hvac_schedules(self):
"""
Get HVAC schedules
:return: HVAC schedules
"""
return self._hvac_schedules

View File

@ -0,0 +1,84 @@
"""
InternalGains module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
class InternalGains:
"""
InternalGains class
"""
def __init__(self):
self._average_internal_gain = None
self._convective_fraction = None
self._radiative_fraction = None
self._latent_fraction = None
@property
def average_internal_gain(self):
"""
Get internal gains average internal gain in w/m2
:return: float
"""
return self._average_internal_gain
@average_internal_gain.setter
def average_internal_gain(self, value):
"""
Set internal gains average internal gain in w/m2
:param value: float
:return: None
"""
self._average_internal_gain = value
@property
def convective_fraction(self):
"""
Get internal gains convective fraction
:return: float
"""
return self._convective_fraction
@convective_fraction.setter
def convective_fraction(self, value):
"""
Set internal gains convective fraction
:param value: float
:return: None
"""
self._convective_fraction = value
@property
def radiative_fraction(self):
"""
Get internal gains radiative fraction
:return: float
"""
return self._radiative_fraction
@radiative_fraction.setter
def radiative_fraction(self, value):
"""
Set internal gains convective fraction
:param value: float
:return: None
"""
self._radiative_fraction = value
@property
def latent_fraction(self):
"""
Get internal gains latent fraction
:return: float
"""
return self._latent_fraction
@latent_fraction.setter
def latent_fraction(self, value):
"""
Set internal gains latent fraction
:param value: float
:return: None
"""
self._latent_fraction = value

View File

@ -0,0 +1,49 @@
"""
Layers module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
from city_model_structure.attributes.material import Material
class Layer:
"""
Layer class
"""
def __init__(self):
self._material = None
self._thickness = None
@property
def material(self) -> Material:
"""
Get layer material
:return: Material
"""
return self._material
@material.setter
def material(self, value):
"""
Set layer material
:param value: Material
:return: None
"""
self._material = value
@property
def thickness(self):
"""
Get layer thickness in meters
:return: float
"""
return self._thickness
@thickness.setter
def thickness(self, value):
"""
Get layer thickness in meters
:param value: float
:return: None
"""
self._thickness = value

View File

@ -0,0 +1,26 @@
"""
Building module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Sanam Dabirian sanam.dabirian@mail.concordia.ca
"""
class lighting_facilities:
"""
Lighting facilities class
"""
def __init__(self, electric_power):
"""
Constructor
"""
self._electric_power = electric_power
@property
def electric_power(self):
"""
Get lighting electric power
:return: lighting electric power
"""
return self._electric_power

View File

@ -0,0 +1,156 @@
"""
Material module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
class Material:
"""
Material class
"""
def __init__(self):
self._conductivity = None
self._specific_heat = None
self._density = None
self._solar_absorptance = None
self._thermal_absorptance = None
self._visible_absorptance = None
self._no_mass = False
self._thermal_resistance = None
@property
def conductivity(self):
"""
Get material conductivity in W/mK
:return: float
"""
return self._conductivity
@conductivity.setter
def conductivity(self, value):
"""
Set material conductivity in W/mK
:param value: float
:return: None
"""
self._conductivity = value
@property
def specific_heat(self):
"""
Get material conductivity in J/kgK
:return: float
"""
return self._specific_heat
@specific_heat.setter
def specific_heat(self, value):
"""
Get material conductivity in J/kgK
:param value: float
:return: None
"""
self._specific_heat = value
@property
def density(self):
"""
Get material density in kg/m3
:return: float
"""
return self._density
@density.setter
def density(self, value):
"""
Set material density in kg/m3
:param value: float
:return: None
"""
self._density = value
@property
def solar_absorptance(self):
"""
Get material solar absorptance
:return: float
"""
return self._solar_absorptance
@solar_absorptance.setter
def solar_absorptance(self, value):
"""
Set material solar absorptance
:param value: float
:return: None
"""
self._solar_absorptance = value
@property
def thermal_absorptance(self):
"""
Get material thermal absorptance
:return: float
"""
return self._thermal_absorptance
@thermal_absorptance.setter
def thermal_absorptance(self, value):
"""
Set material thermal absorptance
:param value: float
:return: None
"""
self._thermal_absorptance = value
@property
def visible_absorptance(self):
"""
Get material visible absorptance
:return: float
"""
return self._visible_absorptance
@visible_absorptance.setter
def visible_absorptance(self, value):
"""
Set material visible absorptance
:param value: float
:return: None
"""
self._visible_absorptance = value
@property
def no_mass(self):
"""
Get material no mass flag
:return: Boolean
"""
return self._no_mass
@no_mass.setter
def no_mass(self, value):
"""
Set material no mass flag
:param value: Boolean
:return: None
"""
self._no_mass = value
@property
def thermal_resistance(self):
"""
Get material thermal resistance in m2K/W
:return: float
"""
return self._thermal_resistance
@thermal_resistance.setter
def thermal_resistance(self, value):
"""
Set material thermal resistance in m2K/W
:param value: float
:return: None
"""
self._thermal_resistance = value

View File

@ -0,0 +1,127 @@
"""
Building module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Sanam Dabirian sanam.dabirian@mail.concordia.ca
"""
class Occupancy:
"""
Occupancy class
"""
def __init__(self, internal_heat_gain, heat_dissipation, occupant_rate, occupant_type, occupant_zone,
number_of_occupants, arrival_time=None, departure_time=None, break_time=None, day_of_week=None,
pd_of_meetings_duration=None, occupant_schedule=None):
"""
Constructor
"""
self._internal_heat_gain = internal_heat_gain
self._heat_dissipation = heat_dissipation
self._occupant_rate = occupant_rate
self._occupant_type = occupant_type
self._occupant_zone = occupant_zone
self._occupant_schedule = occupant_schedule
self._number_of_occupants = number_of_occupants
self._arrival_time = arrival_time
self._departure_time = departure_time
self._break_time = break_time
self._day_of_week = day_of_week
self._pd_of_meetings_duration = pd_of_meetings_duration
@property
def internal_heat_gain(self):
"""
Get internal heat gain of occupants
:return: occupant heat gain
"""
return self._internal_heat_gain
@property
def heat_dissipation(self):
"""
Get heat dissipation of occupants
:return: heat dissipation
"""
return self._heat_dissipation
@property
def occupant_rate(self):
"""
Get rate of occupancy
:return: rate of occupancy
"""
return self._occupant_rate
@property
def occupant_type(self):
"""
Get type of occupancy
:return: type of occupancy
"""
return self._occupant_type
@property
def occupant_zone(self):
"""
Get the zone that occupant is in it
:return: occupant zone
"""
return self._occupant_zone
@property
def occupant_schedule(self):
"""
Get the schedule when an occupant is in a zone
:return: occupant schedule
"""
return self._occupant_schedule
@property
def number_of_occupants(self):
"""
Get the number of occupants
:return: number of occupants
"""
return self._number_of_occupants
@property
def arrival_time(self):
"""
Get the arrival time of the occupant (for office building)
:return: arrival time
"""
return self._arrival_time
@property
def departure_time(self):
"""
Get the departure time of the occupant (for office building)
:return: departure time
"""
return self._departure_time
@property
def break_time(self):
"""
Get the lunch or break time of the occupant (for office building)
:return: break time
"""
return self._break_time
@property
def day_of_week(self):
"""
Get the day of the week
:return: day of the week
"""
return self._day_of_week
@property
def pd_of_meetings_duration(self):
"""
Get the probability distribution of the meeting duration
:return: probability distribution of the meeting duration
"""
return self._pd_of_meetings_duration

View File

@ -0,0 +1,116 @@
"""
Polyhedron module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
import numpy as np
from trimesh import Trimesh
from helpers.geometry_helper import GeometryHelper
class Polyhedron:
"""
Polyhedron class
"""
def __init__(self, surfaces):
self._surfaces = list(surfaces)
self._polygons = [s.polygon for s in surfaces]
self._polyhedron = None
self._volume = None
self._faces = None
self._vertices = None
self._mesh = None
self._centroid = None
self._max_z = None
self._geometry = GeometryHelper()
def _position_of(self, point):
vertices = self.vertices
for i in range(len(vertices)):
if self._geometry.almost_equal(vertices[i], point):
return i
return -1
@property
def vertices(self) -> np.ndarray:
"""
Polyhedron vertices
:return: np.ndarray(int)
"""
if self._vertices is None:
vertices, self._vertices = [], []
_ = [vertices.extend(s.points) for s in self._surfaces]
for vertex_1 in vertices:
found = False
for vertex_2 in self._vertices:
found = False
if self._geometry.almost_equal(vertex_1, vertex_2):
found = True
break
if not found:
self._vertices.append(vertex_1)
self._vertices = np.asarray(self._vertices)
return self._vertices
@property
def faces(self) -> np.ndarray:
"""
Polyhedron faces
:return: np.ndarray([int])
"""
if self._faces is None:
self._faces = []
for surface in self._surfaces:
face = []
points = surface.points
for point in points:
face.append(self._position_of(point))
self._faces.append(face)
return self._faces
@property
def _polyhedron_mesh(self):
if self._mesh is None:
self._mesh = Trimesh(vertices=self.vertices, faces=self.faces)
return self._mesh
@property
def volume(self):
"""
Polyhedron volume in cubic meters
:return: float
"""
if self._volume is None:
if not self._polyhedron_mesh.is_volume:
print('The geometry is not a closed volume')
self._volume = np.inf
else:
self._volume = self._polyhedron_mesh.volume
return self._volume
@property
def max_z(self):
"""
Polyhedron maximal z value
:return: float
"""
bounds = self._polyhedron_mesh.bounds
z_max = max(bounds[:, 2])
return z_max
@property
def centroid(self):
"""
Polyhedron centroid
:return: [x,y,z]
"""
return self._polyhedron_mesh.centroid
def export(self, full_path):
"""
Export the polyhedron to stl given file
:param full_path: str
:return: None
"""
self._polyhedron_mesh.export(full_path)

View File

@ -0,0 +1,34 @@
"""
Building module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Sanam Dabirian sanam.dabirian@mail.concordia.ca
"""
class ScheduleValue:
"""
Schedule Values class
"""
def __init__(self, hour, probability):
"""
Constructor
"""
self._hour = hour
self._probability = probability
@property
def hour(self):
"""
Get hours
:return: hour of a day
"""
return self._hour
@property
def probability(self):
"""
Get probabilities of occupants' presence
:return: occupants' presence probabilities
"""
return self._probability

View File

@ -0,0 +1,443 @@
"""
Surface module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
from __future__ import annotations
from typing import Union
import numpy as np
import pyny3d.geoms as pn
import pandas as pd
import helpers.constants as cte
from helpers.geometry_helper import GeometryHelper
class Surface:
"""
Surface class
"""
def __init__(self, coordinates, surface_type=None, name=None, swr='0.2', remove_last=True, is_projected=False):
self._coordinates = coordinates
self._type = surface_type
self._name = name
self._swr = swr
self._remove_last = remove_last
self._is_projected = is_projected
self._geometry_helper = GeometryHelper()
self._polygon = None
self._ground_polygon = None
self._area = None
self._points = None
self._ground_points = None
self._points_list = None
self._normal = None
self._azimuth = None
self._inclination = None
self._area_above_ground = None
self._area_below_ground = None
self._parent = None
self._shapely = None
self._projected_surface = None
self._min_x = None
self._min_y = None
self._min_z = None
self._shared_surfaces = []
self._global_irradiance = pd.DataFrame()
self._global_irradiance_hour = np.zeros(8760)
self._global_irradiance_month = np.zeros(12)
self._ground_coordinates = (self.min_x, self.min_y, self.min_z)
def parent(self, parent, surface_id):
"""
Assign a city object as surface parent and a surface id
:param parent: CityObject
:param surface_id: str
:return: None
"""
self._parent = parent
self._name = str(surface_id)
@property
def name(self):
"""
Surface name
:return: str
"""
if self._name is None:
raise Exception('surface has no name')
return self._name
@property
def swr(self):
"""
Get surface short wave reflectance
:return: float
"""
return self._swr
@swr.setter
def swr(self, value):
"""
Set surface short wave reflectance
:param value: float
:return: None
"""
self._swr = value
@property
def points(self) -> np.ndarray:
"""
Surface point matrix
:return: np.ndarray
"""
if self._points is None:
self._points = np.fromstring(self._coordinates, dtype=float, sep=' ')
self._points = GeometryHelper.to_points_matrix(self._points, self._remove_last)
return self._points
def _min_coord(self, axis):
if axis == 'x':
axis = 0
elif axis == 'y':
axis = 1
else:
axis = 2
min_coordinate = ''
for point in self.points:
if min_coordinate == '':
min_coordinate = point[axis]
elif min_coordinate > point[axis]:
min_coordinate = point[axis]
return min_coordinate
@property
def min_x(self):
"""
Surface minimal x value
:return: float
"""
if self._min_x is None:
self._min_x = self._min_coord('x')
return self._min_x
@property
def min_y(self):
"""
Surface minimal y value
:return: float
"""
if self._min_y is None:
self._min_y = self._min_coord('y')
return self._min_y
@property
def min_z(self):
"""
Surface minimal z value
:return: float
"""
if self._min_z is None:
self._min_z = self._min_coord('z')
return self._min_z
@property
def ground_points(self) -> np.ndarray:
"""
Surface grounded points matrix
:return: np.ndarray
"""
if self._ground_points is None:
coordinates = ''
for point in self.points:
x = point[0] - self._ground_coordinates[0]
y = point[1] - self._ground_coordinates[1]
z = point[2] - self._ground_coordinates[2]
if coordinates != '':
coordinates = coordinates + ' '
coordinates = coordinates + str(x) + ' ' + str(y) + ' ' + str(z)
self._ground_points = np.fromstring(coordinates, dtype=float, sep=' ')
self._ground_points = GeometryHelper.to_points_matrix(self._ground_points, False)
return self._ground_points
@property
def points_list(self) -> np.ndarray:
"""
Surface point list
:return: np.ndarray
"""
if self._points_list is None:
s = self.points
self._points_list = np.reshape(s, len(s) * 3)
return self._points_list
@property
def polygon(self) -> Union[pn.Polygon, None]:
"""
Surface polygon
:return: None or pyny3d.Polygon
"""
if self._polygon is None:
try:
self._polygon = pn.Polygon(self.points)
except ValueError:
# is not really a polygon but a line so just return none
self._polygon = None
return self._polygon
@property
def ground_polygon(self) -> Union[pn.Polygon, None]:
"""
Surface grounded polygon
:return: None or pyny3d.Polygon
"""
if self._ground_polygon is None:
try:
self._ground_polygon = pn.Polygon(self.ground_points)
except ValueError:
# is not really a polygon but a line so just return none
self._ground_polygon = None
return self._ground_polygon
@property
def area(self):
"""
Surface area in square meters
:return: float
"""
if self._area is None:
self._area = self.polygon.get_area()
return self._area
def _is_almost_same_terrain(self, terrain_points, ground_points):
equal = 0
for terrain_point in terrain_points:
for ground_point in ground_points:
if self._geometry_helper.almost_equal(terrain_point, ground_point):
equal += 1
return equal == len(terrain_points)
@property
def _is_terrain(self):
for t_points in self._parent.terrains:
if len(t_points) == len(self.points) and self._is_almost_same_terrain(t_points, self.points):
return True
return False
@property
def area_above_ground(self):
"""
Surface area above ground in square meters
:return: float
"""
if self._area_above_ground is None:
self._area_above_ground = self.area - self.area_below_ground
return self._area_above_ground
@property
def area_below_ground(self):
"""
Surface area below ground in square meters
:return: float
"""
if self._area_below_ground is None:
self._area_below_ground = 0.0
if self._is_terrain:
self._area_below_ground = self.area
return self._area_below_ground
@property
def normal(self) -> np.ndarray:
"""
Surface normal vector
:return: np.ndarray
"""
if self._normal is None:
points = self.points
cross_product = np.cross(points[1] - points[0], points[2] - points[0])
self._normal = cross_product / np.linalg.norm(cross_product)
return self._normal
@property
def azimuth(self):
"""
Surface azimuth in radians
:return: float
"""
if self._azimuth is None:
normal = self.normal
self._azimuth = np.arctan2(normal[1], normal[0])
return self._azimuth
@property
def inclination(self):
"""
Surface inclination in radians
:return: float
"""
if self._inclination is None:
self._inclination = np.arccos(self.normal[2])
return self._inclination
@property
def type(self):
"""
Surface type Ground, Wall or Roof
:return: str
"""
if self._type is None:
grad = np.rad2deg(self.inclination)
if grad >= 170:
self._type = 'Ground'
elif 80 <= grad <= 100:
self._type = 'Wall'
else:
self._type = 'Roof'
return self._type
def add_shared(self, surface, intersection_area):
"""
Add a given surface and shared area in percent to this surface.
:param surface:
:param intersection_area:
:return:
"""
percent = intersection_area / self.area
self._shared_surfaces.append((percent, surface))
def shared(self, surface):
"""
Check if given surface share some area with this surface
:param surface: Surface
:return: None
"""
if self.type != 'Wall' or surface.type != 'Wall':
return
if self._geometry_helper.is_almost_same_surface(self, surface):
intersection_area = self.intersect(surface).area
self.add_shared(surface, intersection_area)
surface.add_shared(self, intersection_area)
@property
def global_irradiance_hour(self):
"""
Get surface global irradiance hour in Wh/m2
:return: float
"""
return self._global_irradiance_hour
@global_irradiance_hour.setter
def global_irradiance_hour(self, value):
"""
Set surface global irradiance per hour in Wh/m2
:param value: float
:return: None
"""
self._global_irradiance_hour = value
@property
def global_irradiance_month(self):
"""
Get surface global irradiance per month in Wh/m2
:return: float
"""
return self._global_irradiance_month
@global_irradiance_month.setter
def global_irradiance_month(self, value):
"""
Set surface global irradiance per month in Wh/m2
:param value: float
:return: None
"""
self._global_irradiance_month = value
def global_irradiance(self, time_scale):
"""
Get surface global irradiance in Wh/m2 in a defined time_scale
:param time_scale: string.
:return: DataFrame(float)
"""
if time_scale == cte.time_scale['hour']:
self._global_irradiance = self.global_irradiance_hour
elif time_scale == cte.time_scale['month']:
self._global_irradiance = self.global_irradiance_month
else:
raise NotImplementedError
return self._global_irradiance
@property
def shapely(self) -> Union[None, pn.Polygon]:
"""
Surface shapely (Z projection)
:return: None or pyny3d.Polygon
"""
if self.polygon is None:
return None
if self._shapely is None:
self._shapely = self.polygon.get_shapely()
return self._shapely
@staticmethod
def _polygon_to_surface(polygon) -> Surface:
coordinates = ''
for coordinate in polygon.exterior.coords:
if coordinates != '':
coordinates = coordinates + ' '
coordinates = coordinates + str(coordinate[0]) + ' ' + str(coordinate[1]) + ' 0.0'
return Surface(coordinates, remove_last=False)
@property
def projection(self) -> Surface:
"""
Projected surface (Z projection)
:return: Surface
"""
if self._is_projected:
return self
if self._projected_surface is None:
shapely = self.shapely
if shapely is not None:
self._projected_surface = self._polygon_to_surface(shapely)
return self._projected_surface
def intersect(self, surface) -> Union[Surface, None]:
"""
Get the intersection surface, if any, between the given surface and this surface
:param surface: Surface
:return: None or Surface
"""
min_x = min(self.min_x, surface.min_x)
min_y = min(self.min_y, surface.min_y)
min_z = min(self.min_z, surface.min_z)
self._ground_coordinates = (min_x, min_y, min_z)
surface._ground_coordinates = (min_x, min_y, min_z)
origin = (0, 0, 0)
azimuth = self.azimuth - (np.pi / 2)
while azimuth < 0:
azimuth += (np.pi / 2)
inclination = self.inclination - np.pi
while inclination < 0:
inclination += np.pi
polygon1 = self.ground_polygon.rotate(azimuth, 'z', origin).rotate(inclination, 'x', origin)
polygon2 = surface.ground_polygon.rotate(azimuth, 'z', origin).rotate(inclination, 'x', origin)
try:
coordinates = ''
intersection = pn.Surface([polygon1]).intersect_with(polygon2)
if len(intersection) == 0:
return None
for coordinate in pn.Surface([polygon1]).intersect_with(polygon2)[0]:
if coordinates != '':
coordinates = coordinates + ' '
coordinates = coordinates + str(coordinate[0]) + ' ' + str(coordinate[1]) + ' 0.0'
if coordinates == '':
return None
intersect_surface = Surface(coordinates, remove_last=False)
if intersect_surface.polygon is None:
return None
return Surface(coordinates, remove_last=False)
except Exception as err:
print('Error', err)
return None

View File

@ -0,0 +1,242 @@
"""
ThermalBoundary module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
from typing import List
from city_model_structure.attributes.layer import Layer
from city_model_structure.attributes.thermal_opening import ThermalOpening
from city_model_structure.attributes.thermal_zone import ThermalZone
from helpers.configuration_helper import ConfigurationHelper
class ThermalBoundary:
"""
ThermalBoundary class
"""
def __init__(self, surface, delimits):
self._surface = surface
self._delimits = delimits
# 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._layers = None
self._outside_solar_absorptance = ConfigurationHelper().outside_solar_absorptance
self._outside_thermal_absorptance = None
self._outside_visible_absorptance = None
self._window_ratio = None
self._u_value = None
self._window_area = None
self._shortwave_reflectance = 1 - self._outside_solar_absorptance
@property
def delimits(self) -> List[ThermalZone]:
"""
Get the thermal zones delimited by the thermal boundary
:return: [ThermalZone]
"""
return self._delimits
@property
def azimuth(self):
"""
Thermal boundary azimuth in radians
:return: float
"""
return self._surface.azimuth
@property
def inclination(self):
"""
Thermal boundary inclination in radians
:return: float
"""
return self._surface.inclination
@property
def area(self):
"""
Thermal boundary area in square meters
:return: float
"""
return self._surface.area
@property
def area_above_ground(self):
"""
Thermal boundary area above ground in square meters
:return: float
"""
return self._surface.area_above_ground
@property
def area_below_ground(self):
"""
Thermal boundary area below ground in square meters
:return: float
"""
return self._surface.area_below_ground
@property
def outside_solar_absorptance(self):
"""
Get thermal boundary outside solar absorptance
:return: float
"""
return self._outside_solar_absorptance
@outside_solar_absorptance.setter
def outside_solar_absorptance(self, value):
"""
Set thermal boundary outside solar absorptance
:param value: float
:return: None
"""
self._outside_solar_absorptance = value
self._shortwave_reflectance = 1.0 - float(value)
@property
def outside_thermal_absorptance(self):
"""
Get thermal boundary outside thermal absorptance
:return: float
"""
return self._outside_thermal_absorptance
@outside_thermal_absorptance.setter
def outside_thermal_absorptance(self, value):
"""
Set thermal boundary outside thermal absorptance
:param value: float
:return: None
"""
self._outside_thermal_absorptance = value
@property
def outside_visible_absorptance(self):
"""
Get thermal boundary outside visible absorptance
:return: float
"""
return self._outside_visible_absorptance
@outside_visible_absorptance.setter
def outside_visible_absorptance(self, value):
"""
Set thermal boundary outside visible absorptance
:param value: float
:return: None
"""
self._outside_visible_absorptance = value
@property
def thermal_openings(self) -> List[ThermalOpening]:
"""
Get thermal boundary thermal openings
:return: [ThermalOpening]
"""
return self._thermal_openings
@thermal_openings.setter
def thermal_openings(self, value):
"""
Set thermal boundary thermal openings
:param value: [ThermalOpening]
:return: None
"""
self._thermal_openings = value
@property
def layers(self) -> List[Layer]:
"""
Get thermal boundary layers
:return: [Layers]
"""
return self._layers
@layers.setter
def layers(self, value):
"""
Set thermal boundary layers
:param value: [Layer]
:return: None
"""
self._layers = value
@property
def type(self):
"""
Thermal boundary surface type
:return: str
"""
return self._surface.type
@property
def window_ratio(self):
"""
Get thermal boundary window ratio
:return: float
"""
return self._window_ratio
@window_ratio.setter
def window_ratio(self, value):
"""
Set thermal boundary window ratio
:param value: float
:return: None
"""
self._window_ratio = value
@property
def window_area(self):
"""
Thermal boundary window area in square meters
:return: float
"""
if self._window_area is None:
try:
self._window_area = float(self._surface.area) * float(self.window_ratio)
except TypeError:
raise Exception('Window ratio is not defined or invalid surface area')
return self._window_area
@property
def u_value(self):
"""
Thermal boundary u value in W/m2K
internal and external convective coefficient in W/m2K values, can be configured at configuration.ini
:return: float
"""
if self._u_value is None:
h_i = ConfigurationHelper().h_i
h_e = ConfigurationHelper().h_e
r_value = 1.0/h_i + 1.0/h_e
try:
for layer in self.layers:
if layer.material.no_mass:
r_value += float(layer.material.thermal_resistance)
else:
r_value = r_value + float(layer.material.conductivity) / float(layer.thickness)
self._u_value = 1.0/r_value
except TypeError:
raise Exception('Constructions layers are not initialized')
return self._u_value
@property
def shortwave_reflectance(self):
"""
Get thermal boundary shortwave reflectance
:return: float
"""
return self._shortwave_reflectance
@shortwave_reflectance.setter
def shortwave_reflectance(self, value):
"""
Set thermal boundary shortwave reflectance
:param value: float
:return:
"""
self._shortwave_reflectance = value
self._outside_solar_absorptance = 1.0 - float(value)

View File

@ -0,0 +1,171 @@
"""
ThermalOpening module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
from helpers.configuration_helper import ConfigurationHelper
class ThermalOpening:
"""
ThermalOpening class
"""
def __init__(self):
self._openable_ratio = None
self._conductivity = None
self._frame_ratio = ConfigurationHelper().frame_ratio
self._g_value = None
self._thickness = None
self._front_side_solar_transmittance_at_normal_incidence = None
self._back_side_solar_transmittance_at_normal_incidence = None
self._overall_u_value = None
@property
def openable_ratio(self):
"""
Get thermal opening openable ratio, NOT IMPLEMENTED
:return: Exception
"""
raise NotImplementedError()
@openable_ratio.setter
def openable_ratio(self, value):
"""
Set thermal opening openable ratio, NOT IMPLEMENTED
:param value: Any
:return: Exception
"""
raise NotImplementedError()
@property
def conductivity(self):
"""
Get thermal opening conductivity in W/mK
:return: float
"""
return self._conductivity
@conductivity.setter
def conductivity(self, value):
"""
Get thermal opening conductivity in W/mK
:param value: float
:return: None
"""
# The code to calculate overall_u_value is duplicated here and in thickness_m.
# This ensures a more robust code that returns the overall_u_value regardless the order the parameters are read.
self._conductivity = value
if self._overall_u_value is None and self.thickness is not None:
h_i = ConfigurationHelper().h_i
h_e = ConfigurationHelper().h_e
r_value = 1 / h_i + 1 / h_e + float(self.conductivity) / float(self.thickness)
self._overall_u_value = 1 / r_value
@property
def frame_ratio(self):
"""
Get thermal opening frame ratio
:return: float
"""
return self._frame_ratio
@frame_ratio.setter
def frame_ratio(self, value):
"""
Set thermal opening frame ratio
:param value: float
:return: None
"""
self._frame_ratio = value
@property
def g_value(self):
"""
Get thermal opening g value
:return: float
"""
return self._g_value
@g_value.setter
def g_value(self, value):
"""
Set thermal opening g value
:param value:
:return:
"""
self._g_value = value
@property
def thickness(self):
"""
Get thermal opening thickness in meters
:return:
"""
return self._thickness
@thickness.setter
def thickness(self, value):
"""
Set thermal opening thickness in meters
:param value: float
:return: None
"""
# The code to calculate overall_u_value is duplicated here and in conductivity.
# This ensures a more robust code that returns the overall_u_value regardless the order the parameters are read.
self._thickness = value
if self._overall_u_value is None and self.conductivity is not None:
h_i = ConfigurationHelper().h_i
h_e = ConfigurationHelper().h_e
r_value = 1 / h_i + 1 / h_e + float(self.conductivity) / float(self.thickness)
self._overall_u_value = 1 / r_value
@property
def front_side_solar_transmittance_at_normal_incidence(self):
"""
Get thermal opening front side solar transmittance at normal incidence
:return: float
"""
return self._front_side_solar_transmittance_at_normal_incidence
@front_side_solar_transmittance_at_normal_incidence.setter
def front_side_solar_transmittance_at_normal_incidence(self, value):
"""
Set thermal opening front side solar transmittance at normal incidence
:param value: float
:return: None
"""
self._front_side_solar_transmittance_at_normal_incidence = value
@property
def back_side_solar_transmittance_at_normal_incidence(self):
"""
Get thermal opening back side solar transmittance at normal incidence
:return: float
"""
return self._back_side_solar_transmittance_at_normal_incidence
@back_side_solar_transmittance_at_normal_incidence.setter
def back_side_solar_transmittance_at_normal_incidence(self, value):
"""
Set thermal opening back side solar transmittance at normal incidence
:param value: float
:return: None
"""
self._back_side_solar_transmittance_at_normal_incidence = value
@property
def overall_u_value(self):
"""
Get thermal opening overall u value in W/m2K
:return: float
"""
return self._overall_u_value
@overall_u_value.setter
def overall_u_value(self, value):
"""
Get thermal opening overall u value in W/m2K
:param value: float
:return: None
"""
self._overall_u_value = value

View File

@ -0,0 +1,187 @@
"""
ThermalZone module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
from typing import List, TypeVar
from city_model_structure.attributes.surface import Surface
from city_model_structure.attributes.usage_zone import UsageZone
from helpers.configuration_helper import ConfigurationHelper
ThermalBoundary = TypeVar('ThermalBoundary')
class ThermalZone:
"""
ThermalZone class
"""
def __init__(self, surfaces):
self._surfaces = surfaces
self._floor_area = None
self._bounded = None
self._heated = ConfigurationHelper().heated
self._cooled = ConfigurationHelper().cooled
self._additional_thermal_bridge_u_value = ConfigurationHelper().additional_thermal_bridge_u_value
self._effective_thermal_capacity = None
self._indirectly_heated_area_ratio = ConfigurationHelper().indirectly_heated_area_ratio
self._infiltration_rate_system_on = ConfigurationHelper().infiltration_rate_system_on
self._infiltration_rate_system_off = None
self._usage_zones = None
@property
def heated(self):
"""
Get thermal zone heated flag
:return: Boolean
"""
return self._heated
@property
def cooled(self):
"""
Get thermal zone cooled flag
:return: Boolean
"""
return self._cooled
@property
def floor_area(self):
"""
Get thermal zone floor area in square meters
:return: float
"""
if self._floor_area is None:
self._floor_area = 0
for s in self._surfaces:
if s.type == 'Ground':
self._floor_area += s.area
return self._floor_area
@property
def bounded(self) -> List[ThermalBoundary]:
"""
Get thermal boundaries bounding with the thermal zone
:return: [ThermalBoundary]
"""
return self._bounded
@bounded.setter
def bounded(self, value):
"""
Set thermal boundaries bounding with the thermal zone
:param value: [ThermalBoundary]
:return: None
"""
self._bounded = value
@property
def surfaces(self) -> List[Surface]:
# todo: This property should be erased
"""
Get thermal zone surfaces
:return: [Surface]
"""
return self._surfaces
@property
def additional_thermal_bridge_u_value(self):
"""
Get thermal zone additional thermal bridge u value W/m2K
:return: float
"""
return self._additional_thermal_bridge_u_value
@additional_thermal_bridge_u_value.setter
def additional_thermal_bridge_u_value(self, value):
"""
Set thermal zone additional thermal bridge u value W/m2K
:param value: float
:return: None
"""
self._additional_thermal_bridge_u_value = value
@property
def effective_thermal_capacity(self):
"""
Get thermal zone effective thermal capacity
:return: float
"""
return self._effective_thermal_capacity
@effective_thermal_capacity.setter
def effective_thermal_capacity(self, value):
"""
Set thermal zone effective thermal capacity
:param value: float
:return: None
"""
self._effective_thermal_capacity = value
@property
def indirectly_heated_area_ratio(self):
"""
Get thermal zone indirectly heated area ratio
:return: float
"""
return self._indirectly_heated_area_ratio
@indirectly_heated_area_ratio.setter
def indirectly_heated_area_ratio(self, value):
"""
Set thermal zone indirectly heated area ratio
:param value: float
:return: None
"""
self._indirectly_heated_area_ratio = value
@property
def infiltration_rate_system_on(self):
"""
Get thermal zone infiltration rate system on in air changes per hour
:return: float
"""
return self._infiltration_rate_system_on
@infiltration_rate_system_on.setter
def infiltration_rate_system_on(self, value):
"""
Set thermal zone infiltration rate system on in air changes per hour
:param value: float
:return: None
"""
self._infiltration_rate_system_on = value
@property
def infiltration_rate_system_off(self):
"""
Get thermal zone infiltration rate system off in air changes per hour
:return: float
"""
return self._infiltration_rate_system_off
@infiltration_rate_system_off.setter
def infiltration_rate_system_off(self, value):
"""
Set thermal zone infiltration rate system on in air changes per hour
:param value: float
:return: None
"""
self._infiltration_rate_system_off = value
@property
def usage_zones(self) -> List[UsageZone]:
"""
Get thermal zone usage zones
:return: [UsageZone]
"""
return self._usage_zones
@usage_zones.setter
def usage_zones(self, values):
"""
Set thermal zone usage zones
:param values: [UsageZone]
:return: None
"""
self._usage_zones = values

View File

@ -0,0 +1,195 @@
"""
UsageZone module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
from typing import List
from city_model_structure.attributes.internal_gains import InternalGains
from helpers.configuration_helper import ConfigurationHelper
class UsageZone:
"""
UsageZone class
"""
def __init__(self):
self._usage = None
self._internal_gains = None
self._heating_setpoint = None
self._heating_setback = None
self._cooling_setpoint = None
self._hours_day = None
self._days_year = None
# todo: this must come from library, talk to Rabeeh
self._mechanical_air_change = ConfigurationHelper().min_air_change
self._occupancy = None
self._schedules = None
@property
def internal_gains(self) -> List[InternalGains]:
"""
Get usage zone internal gains
:return: [InternalGains]
"""
return self._internal_gains
@internal_gains.setter
def internal_gains(self, value):
"""
Set usage zone internal gains
:param value: [InternalGains]
:return: None
"""
self._internal_gains = value
@property
def heating_setpoint(self):
"""
Get usage zone heating set point in celsius grads
:return: float
"""
return self._heating_setpoint
@heating_setpoint.setter
def heating_setpoint(self, value):
"""
Set usage zone heating set point in celsius grads
:param value: float
:return: None
"""
self._heating_setpoint = value
@property
def heating_setback(self):
"""
Get usage zone heating setback in celsius grads
:return: float
"""
return self._heating_setback
@heating_setback.setter
def heating_setback(self, value):
"""
Set usage zone heating setback in celsius grads
:param value: float
:return: None
"""
self._heating_setback = value
@property
def cooling_setpoint(self):
"""
Get usage zone cooling setpoint in celsius grads
:return: float
"""
return self._cooling_setpoint
@cooling_setpoint.setter
def cooling_setpoint(self, value):
"""
Set usage zone cooling setpoint in celsius grads
:param value: float
:return: None
"""
self._cooling_setpoint = value
@property
def hours_day(self):
"""
Get usage zone usage hours per day
:return: float
"""
return self._hours_day
@hours_day.setter
def hours_day(self, value):
"""
Set usage zone usage hours per day
:param value: float
:return: float
"""
self._hours_day = value
@property
def days_year(self):
"""
Get usage zone usage days per year
:return: float
"""
return self._days_year
@days_year.setter
def days_year(self, value):
"""
Set usage zone usage days per year
:param value: float
:return: None
"""
self._days_year = value
@property
def mechanical_air_change(self):
"""
Set usage zone mechanical air change in air change per hour
:return: float
"""
return self._mechanical_air_change
@mechanical_air_change.setter
def mechanical_air_change(self, value):
"""
Get usage zone mechanical air change in air change per hour
:param value: float
:return: None
"""
self._mechanical_air_change = value
@property
def usage(self):
"""
Get usage zone usage
:return: str
"""
return self._usage
@usage.setter
def usage(self, value):
"""
Get usage zone usage
:param value: str
:return: None
"""
self._usage = value
@property
def occupancy(self):
"""
Get occupancy data
:return: [Occupancy]
"""
return self._occupancy
@occupancy.setter
def occupancy(self, values):
"""
Set occupancy
:param values: [Occupancy]
"""
self._occupancy = values
@property
def schedules(self):
"""
Get schedule of Sundays
:return: [Sundays Schedule_Values]
"""
return self._schedules
@schedules.setter
def schedules(self, value):
"""
occupancy schedules of Sundays
:param value: Sunday schedules
"""
self._schedules = value

View File

@ -0,0 +1,37 @@
"""
WeatherFactory retrieve the specific weather module for the given source format
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Pilar Monsalvete pilar_monsalvete@yahoo.es
"""
from factories.weather.weather_feeders.cli_weather_parameters import CliWeatherParameters
from factories.weather.weather_feeders.dat_weather_parameters import DatWeatherParameters
class WeatherFactory:
"""
WeatherFactory class
"""
# todo: modify full_path_weather to make it depending on "city"
def __init__(self, handler, city, full_path_weather):
self._handler = '_' + handler.lower().replace(' ', '_')
self._city = city
self._full_path_weather = full_path_weather
self.factory()
def _cli(self):
CliWeatherParameters(self._full_path_weather)
def _dat(self):
DatWeatherParameters(self._full_path_weather)
def _tmy(self):
raise Exception('Not implemented')
def factory(self):
"""
Enrich the city with the usage information
:return: None
"""
getattr(self, self._handler, lambda: None)()

View File

@ -0,0 +1,13 @@
"""
CliWeatherParameters class to extract weather parameters from a defined region in .cli format
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Pilar Monsalvete pilar_monsalvete@yahoo.es
"""
class CliWeatherParameters:
"""
CliWeatherParameters class
"""
def __init__(self, full_path_weather):
self._full_path_weather = full_path_weather

View File

@ -0,0 +1,24 @@
"""
CliWeatherParameters class to extract weather parameters from a defined region in .dat format
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Pilar Monsalvete pilar_monsalvete@yahoo.es
"""
import pandas as pd
class DatWeatherParameters:
"""
DatWeatherParameters class
"""
def __init__(self, full_path_weather):
self._full_path_weather = full_path_weather
self._weather_values = None
def weather_values(self):
if self._full_path_weather is not None:
# TODO: catch error if file does not exist
if self._weather_values is None:
self._weather_values = pd.read_csv(self._full_path_weather, sep='\s+', header=None,
names=['hour', 'global_horiz', 'temperature', 'diffuse', 'beam', 'empty'])
return self._weather_values

View File

@ -0,0 +1,31 @@
import math
import pandas as pd
import helpers.constants as cte
from helpers import monthly_values as mv
class Weather(object):
def __init__(self, weather_values):
self._weather_values = pd.concat([mv.MonthlyValues().month_hour, weather_values], axis=1)
self._temperatures_hourly = None
@property
def external_and_sky_temperatures(self):
if self._temperatures_hourly is None:
self._temperatures_hourly = self._weather_values[['month', 'temperature']]
sky_temperature = self.sky_temperature(self._temperatures_hourly)
self._temperatures_hourly = pd.concat([self._temperatures_hourly, sky_temperature], axis=1)
return self._temperatures_hourly
@staticmethod
def sky_temperature(ambient_temperature):
# Swinbank - Fuentes sky model approximation(1963) based on cloudiness statistics(32 %) in United States
# ambient temperatures( in °C)
# sky temperatures( in °C)
_ambient_temperature = ambient_temperature[['temperature']].to_numpy()
values = []
for temperature in _ambient_temperature:
value = 0.037536 * math.pow((temperature + cte.celsius_to_kelvin), 1.5) \
+ 0.32 * (temperature + cte.celsius_to_kelvin) - cte.celsius_to_kelvin
values.append(value)
return pd.DataFrame(values, columns=['sky temperature'])