diff --git a/catalogs/greenery/ecore_greenery/greenerycatalog.ecore b/catalogs/greenery/ecore_greenery/greenerycatalog.ecore
deleted file mode 100644
index 5c3bb85d..00000000
--- a/catalogs/greenery/ecore_greenery/greenerycatalog.ecore
+++ /dev/null
@@ -1,268 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/catalogs/greenery/ecore_greenery/greenerycatalog.py b/catalogs/greenery/ecore_greenery/greenerycatalog.py
deleted file mode 100644
index 30ac116c..00000000
--- a/catalogs/greenery/ecore_greenery/greenerycatalog.py
+++ /dev/null
@@ -1,318 +0,0 @@
-"""Definition of meta model 'greenerycatalog'."""
-from functools import partial
-import pyecore.ecore as Ecore
-from pyecore.ecore import *
-
-
-name = 'greenerycatalog'
-nsURI = 'http://ca.concordia/greenerycatalog'
-nsPrefix = 'greenery'
-
-eClass = EPackage(name=name, nsURI=nsURI, nsPrefix=nsPrefix)
-
-eClassifiers = {}
-getEClassifier = partial(Ecore.getEClassifier, searchspace=eClassifiers)
-Management = EEnum('Management', literals=['Intensive', 'Extensive', 'SemiIntensive', 'NA'])
-
-Roughness = EEnum('Roughness', literals=['VeryRough', 'Rough',
- 'MediumRough', 'MediumSmooth', 'Smooth', 'VerySmooth'])
-
-
-class Soil(EObject, metaclass=MetaEClass):
-
- name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
- roughness = EAttribute(eType=Roughness, unique=True, derived=False,
- changeable=True, default_value=Roughness.MediumRough)
- conductivityOfDrySoil = EAttribute(
- eType=EString, unique=True, derived=False, changeable=True, default_value='1.0 W/(m*K)')
- densityOfDrySoil = EAttribute(eType=EString, unique=True, derived=False,
- changeable=True, default_value='1100 kg/m³')
- specificHeatOfDrySoil = EAttribute(
- eType=EString, unique=True, derived=False, changeable=True, default_value='1200 J/(kg*K)')
- thermalAbsorptance = EAttribute(eType=EString, unique=True,
- derived=False, changeable=True, default_value='0.9')
- solarAbsorptance = EAttribute(eType=EString, unique=True,
- derived=False, changeable=True, default_value='0.7')
- visibleAbsorptance = EAttribute(eType=EString, unique=True,
- derived=False, changeable=True, default_value='0.75')
- saturationVolumetricMoistureContent = EAttribute(
- eType=EString, unique=True, derived=False, changeable=True, default_value='0.0')
- residualVolumetricMoistureContent = EAttribute(
- eType=EString, unique=True, derived=False, changeable=True, default_value='0.05')
- initialVolumetricMoistureContent = EAttribute(
- eType=EString, unique=True, derived=False, changeable=True, default_value='0.1')
-
- def __init__(self, *, name=None, roughness=None, conductivityOfDrySoil=None, densityOfDrySoil=None, specificHeatOfDrySoil=None, thermalAbsorptance=None, solarAbsorptance=None, visibleAbsorptance=None, saturationVolumetricMoistureContent=None, residualVolumetricMoistureContent=None, initialVolumetricMoistureContent=None):
- # if kwargs:
- # raise AttributeError('unexpected arguments: {}'.format(kwargs))
-
- super().__init__()
-
- if name is not None:
- self.name = name
-
- if roughness is not None:
- self.roughness = roughness
-
- if conductivityOfDrySoil is not None:
- self.conductivityOfDrySoil = conductivityOfDrySoil
-
- if densityOfDrySoil is not None:
- self.densityOfDrySoil = densityOfDrySoil
-
- if specificHeatOfDrySoil is not None:
- self.specificHeatOfDrySoil = specificHeatOfDrySoil
-
- if thermalAbsorptance is not None:
- self.thermalAbsorptance = thermalAbsorptance
-
- if solarAbsorptance is not None:
- self.solarAbsorptance = solarAbsorptance
-
- if visibleAbsorptance is not None:
- self.visibleAbsorptance = visibleAbsorptance
-
- if saturationVolumetricMoistureContent is not None:
- self.saturationVolumetricMoistureContent = saturationVolumetricMoistureContent
-
- if residualVolumetricMoistureContent is not None:
- self.residualVolumetricMoistureContent = residualVolumetricMoistureContent
-
- if initialVolumetricMoistureContent is not None:
- self.initialVolumetricMoistureContent = initialVolumetricMoistureContent
-
-
-class Plant(EObject, metaclass=MetaEClass):
-
- name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
- height = EAttribute(eType=EString, unique=True, derived=False,
- changeable=True, default_value='0.1 m')
- leafAreaIndex = EAttribute(eType=EString, unique=True, derived=False,
- changeable=True, default_value='2.5')
- leafReflectivity = EAttribute(eType=EString, unique=True,
- derived=False, changeable=True, default_value='0.1')
- leafEmissivity = EAttribute(eType=EString, unique=True, derived=False,
- changeable=True, default_value='0.9')
- minimalStomatalResistance = EAttribute(
- eType=EString, unique=True, derived=False, changeable=True, default_value='100.0 s/m')
- co2Sequestration = EAttribute(eType=EString, unique=True, derived=False,
- changeable=True, default_value='kgCO₂eq')
- growsOn = EReference(ordered=True, unique=True, containment=False, derived=False, upper=-1)
-
- def __init__(self, *, name=None, height=None, leafAreaIndex=None, leafReflectivity=None, leafEmissivity=None, minimalStomatalResistance=None, growsOn=None, co2Sequestration=None):
- # if kwargs:
- # raise AttributeError('unexpected arguments: {}'.format(kwargs))
-
- super().__init__()
-
- if name is not None:
- self.name = name
-
- if height is not None:
- self.height = height
-
- if leafAreaIndex is not None:
- self.leafAreaIndex = leafAreaIndex
-
- if leafReflectivity is not None:
- self.leafReflectivity = leafReflectivity
-
- if leafEmissivity is not None:
- self.leafEmissivity = leafEmissivity
-
- if minimalStomatalResistance is not None:
- self.minimalStomatalResistance = minimalStomatalResistance
-
- if co2Sequestration is not None:
- self.co2Sequestration = co2Sequestration
-
- if growsOn:
- self.growsOn.extend(growsOn)
-
-
-class SupportEnvelope(EObject, metaclass=MetaEClass):
-
- roughness = EAttribute(eType=Roughness, unique=True, derived=False,
- changeable=True, default_value=Roughness.MediumRough)
- solarAbsorptance = EAttribute(eType=EDouble, unique=True,
- derived=False, changeable=True, default_value=0.0)
- conductivity = EAttribute(eType=EDouble, unique=True, derived=False,
- changeable=True, default_value=0.0)
- visibleAbsorptance = EAttribute(eType=EDouble, unique=True,
- derived=False, changeable=True, default_value=0.0)
- specificHeat = EAttribute(eType=EDouble, unique=True, derived=False,
- changeable=True, default_value=0.0)
- density = EAttribute(eType=EDouble, unique=True, derived=False,
- changeable=True, default_value=0.0)
- thermalAbsorptance = EAttribute(eType=EDouble, unique=True,
- derived=False, changeable=True, default_value=0.0)
-
- def __init__(self, *, roughness=None, solarAbsorptance=None, conductivity=None, visibleAbsorptance=None, specificHeat=None, density=None, thermalAbsorptance=None):
- # if kwargs:
- # raise AttributeError('unexpected arguments: {}'.format(kwargs))
-
- super().__init__()
-
- if roughness is not None:
- self.roughness = roughness
-
- if solarAbsorptance is not None:
- self.solarAbsorptance = solarAbsorptance
-
- if conductivity is not None:
- self.conductivity = conductivity
-
- if visibleAbsorptance is not None:
- self.visibleAbsorptance = visibleAbsorptance
-
- if specificHeat is not None:
- self.specificHeat = specificHeat
-
- if density is not None:
- self.density = density
-
- if thermalAbsorptance is not None:
- self.thermalAbsorptance = thermalAbsorptance
-
-
-class GreeneryCatalog(EObject, metaclass=MetaEClass):
-
- name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
- description = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
- source = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
- plantCategories = EReference(ordered=True, unique=True,
- containment=True, derived=False, upper=-1)
- vegetationCategories = EReference(ordered=True, unique=True,
- containment=True, derived=False, upper=-1)
- soils = EReference(ordered=True, unique=True, containment=True, derived=False, upper=-1)
-
- def __init__(self, *, name=None, description=None, source=None, plantCategories=None, vegetationCategories=None, soils=None):
- # if kwargs:
- # raise AttributeError('unexpected arguments: {}'.format(kwargs))
-
- super().__init__()
-
- if name is not None:
- self.name = name
-
- if description is not None:
- self.description = description
-
- if source is not None:
- self.source = source
-
- if plantCategories:
- self.plantCategories.extend(plantCategories)
-
- if vegetationCategories:
- self.vegetationCategories.extend(vegetationCategories)
-
- if soils:
- self.soils.extend(soils)
-
-
-class PlantCategory(EObject, metaclass=MetaEClass):
- """Excluding (that is non-overlapping) categories like Trees, Hedeges, Grasses that help users finding a specific biol. plant species."""
- name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
- plants = EReference(ordered=True, unique=True, containment=True, derived=False, upper=-1)
-
- def __init__(self, *, name=None, plants=None):
- # if kwargs:
- # raise AttributeError('unexpected arguments: {}'.format(kwargs))
-
- super().__init__()
-
- if name is not None:
- self.name = name
-
- if plants:
- self.plants.extend(plants)
-
-
-class IrrigationSchedule(EObject, metaclass=MetaEClass):
-
- name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
-
- def __init__(self, *, name=None):
- # if kwargs:
- # raise AttributeError('unexpected arguments: {}'.format(kwargs))
-
- super().__init__()
-
- if name is not None:
- self.name = name
-
-
-class Vegetation(EObject, metaclass=MetaEClass):
- """Plant life or total plant cover (as of an area)"""
- name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
- thicknessOfSoil = EAttribute(eType=EString, unique=True, derived=False,
- changeable=True, default_value='20 cm')
- management = EAttribute(eType=Management, unique=True, derived=False,
- changeable=True, default_value=Management.NA)
- airGap = EAttribute(eType=EString, unique=True, derived=False,
- changeable=True, default_value='0.0 cm')
- soil = EReference(ordered=True, unique=True, containment=False, derived=False)
- plants = EReference(ordered=True, unique=True, containment=True, derived=False, upper=-1)
-
- def __init__(self, *, name=None, thicknessOfSoil=None, soil=None, plants=None, management=None, airGap=None):
- # if kwargs:
- # raise AttributeError('unexpected arguments: {}'.format(kwargs))
-
- super().__init__()
-
- if name is not None:
- self.name = name
-
- if thicknessOfSoil is not None:
- self.thicknessOfSoil = thicknessOfSoil
-
- if management is not None:
- self.management = management
-
- if airGap is not None:
- self.airGap = airGap
-
- if soil is not None:
- self.soil = soil
-
- if plants:
- self.plants.extend(plants)
-
-
-class VegetationCategory(EObject, metaclass=MetaEClass):
- """Excluding (that is non-overlapping) categories to help users finding a specific vegetation template."""
- name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
- vegetationTemplates = EReference(ordered=True, unique=True,
- containment=True, derived=False, upper=-1)
-
- def __init__(self, *, vegetationTemplates=None, name=None):
- # if kwargs:
- # raise AttributeError('unexpected arguments: {}'.format(kwargs))
-
- super().__init__()
-
- if name is not None:
- self.name = name
-
- if vegetationTemplates:
- self.vegetationTemplates.extend(vegetationTemplates)
-
-
-class PlantPercentage(EObject, metaclass=MetaEClass):
-
- percentage = EAttribute(eType=EString, unique=True, derived=False,
- changeable=True, default_value='100')
- plant = EReference(ordered=True, unique=True, containment=False, derived=False)
-
- def __init__(self, *, percentage=None, plant=None):
- # if kwargs:
- # raise AttributeError('unexpected arguments: {}'.format(kwargs))
-
- super().__init__()
-
- if percentage is not None:
- self.percentage = percentage
-
- if plant is not None:
- self.plant = plant
diff --git a/catalogs/greenery/ecore_greenery/greenerycatalog_no_quantities.ecore b/catalogs/greenery/ecore_greenery/greenerycatalog_no_quantities.ecore
deleted file mode 100644
index db58a9c0..00000000
--- a/catalogs/greenery/ecore_greenery/greenerycatalog_no_quantities.ecore
+++ /dev/null
@@ -1,268 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/city_model_structure/attributes/polygon.py b/city_model_structure/attributes/polygon.py
index cdfd56da..126e9ae4 100644
--- a/city_model_structure/attributes/polygon.py
+++ b/city_model_structure/attributes/polygon.py
@@ -16,6 +16,7 @@ from city_model_structure.attributes.plane import Plane
from city_model_structure.attributes.point import Point
import helpers.constants as cte
+
class Polygon:
"""
Polygon class
@@ -78,6 +79,48 @@ class Polygon:
z = vector_0[2] * vector_1[2]
return x+y+z
+ def contains_point(self, point):
+ """
+ Determines if the given point is contained by the current polygon
+ :return: boolean
+ """
+ # fixme: This method doesn't seems to work.
+ n = len(self.vertices)
+ angle_sum = 0
+ for i in range(0, n):
+ vector_0 = self.vertices[i]
+ vector_1 = self.vertices[(i+1) % n]
+ # set to origin
+ vector_0[0] = vector_0[0] - point.coordinates[0]
+ vector_0[1] = vector_0[1] - point.coordinates[1]
+ vector_0[2] = vector_0[2] - point.coordinates[2]
+ vector_1[0] = vector_1[0] - point.coordinates[0]
+ vector_1[1] = vector_1[1] - point.coordinates[1]
+ vector_1[2] = vector_1[2] - point.coordinates[2]
+ module = Polygon._module(vector_0) * Polygon._module(vector_1)
+
+ scalar_product = Polygon._scalar_product(vector_0, vector_1)
+ angle = np.pi/2
+ if module != 0:
+ angle = abs(np.arcsin(scalar_product / module))
+ angle_sum += angle
+ print(angle_sum)
+ return abs(angle_sum - math.pi*2) < cte.EPSILON
+
+ def contains_polygon(self, polygon):
+ """
+ Determines if the given polygon is contained by the current polygon
+ :return: boolean
+ """
+ print('contains')
+ for point in polygon.points:
+ print(point.coordinates, self.contains_point(point))
+
+ if not self.contains_point(point):
+ return False
+ print('Belong!')
+ return True
+
@property
def points_list(self) -> np.ndarray:
"""
@@ -249,7 +292,7 @@ class Polygon:
points_list = self.points_list
normal = self.normal
if np.linalg.norm(normal) == 0:
- sys.stderr.write(f'Not able to triangulate polygon [normal length is 0]')
+ sys.stderr.write('Not able to triangulate polygon\n')
return [self]
# are points concave or convex?
total_points_list, concave_points, convex_points = self._starting_lists(points_list, normal)
@@ -297,10 +340,10 @@ class Polygon:
continue
break
if len(total_points_list) <= 3 and len(convex_points) > 0:
- sys.stderr.write('Not able to triangulate polygon [convex surface]\n')
+ sys.stderr.write('Not able to triangulate polygon\n')
return [self]
if j >= 100:
- sys.stderr.write('Not able to triangulate polygon [infinite loop]\n')
+ sys.stderr.write('Not able to triangulate polygon\n')
return [self]
last_ear = self._triangle(points_list, total_points_list, concave_points[1])
ears.append(last_ear)
diff --git a/city_model_structure/attributes/polyhedron.py b/city_model_structure/attributes/polyhedron.py
index 44a1f8bc..3f292f79 100644
--- a/city_model_structure/attributes/polyhedron.py
+++ b/city_model_structure/attributes/polyhedron.py
@@ -114,7 +114,7 @@ class Polyhedron:
if self._trimesh is None:
for face in self.faces:
if len(face) != 3:
- sys.stderr.write(f'Not able to generate trimesh the face has {len(face)} vertices\n')
+ sys.stderr.write('Not able to generate trimesh\n')
return None
self._trimesh = Trimesh(vertices=self.vertices, faces=self.faces)
return self._trimesh
diff --git a/city_model_structure/building.py b/city_model_structure/building.py
index 951eab05..f7d2dc16 100644
--- a/city_model_structure/building.py
+++ b/city_model_structure/building.py
@@ -5,17 +5,17 @@ Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc
contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
-from typing import List, Union
+from typing import List, Union, TypeVar
import numpy as np
from city_model_structure.building_demand.surface import Surface
-from city_model_structure.building_demand.thermal_zone import ThermalZone
-from city_model_structure.building_demand.thermal_boundary import ThermalBoundary
from city_model_structure.building_demand.usage_zone import UsageZone
from city_model_structure.building_demand.storey import Storey
from city_model_structure.city_object import CityObject
from city_model_structure.building_demand.household import Household
from city_model_structure.attributes.polyhedron import Polyhedron
+ThermalZone = TypeVar('ThermalZone')
+
class Building(CityObject):
"""
@@ -35,9 +35,8 @@ class Building(CityObject):
self._roof_type = None
self._storeys = None
self._geometrical_zones = None
- self._thermal_zones = []
- self._thermal_boundaries = None
- self._usage_zones = []
+ self._thermal_zones = None
+ self._usage_zones = None
self._type = 'building'
self._heating = dict()
self._cooling = dict()
@@ -78,28 +77,6 @@ class Building(CityObject):
"""
return self._grounds
- @property
- def is_heated(self):
- """
- Get building heated flag
- :return: Boolean
- """
- for usage_zone in self.usage_zones:
- if usage_zone.is_heated:
- return usage_zone.is_heated
- return False
-
- @property
- def is_cooled(self):
- """
- Get building cooled flag
- :return: Boolean
- """
- for usage_zone in self.usage_zones:
- if usage_zone.is_cooled:
- return usage_zone.is_cooled
- return False
-
@property
def roofs(self) -> List[Surface]:
"""
@@ -117,18 +94,23 @@ class Building(CityObject):
return self._walls
@property
- def usage_zones(self) -> List[UsageZone]:
+ def usage_zones(self) -> Union[None, List[UsageZone]]:
"""
Get city object usage zones
:return: [UsageZone]
"""
- if len(self._usage_zones) == 0:
- for thermal_zone in self.thermal_zones:
- self._usage_zones.extend(thermal_zone.usage_zones)
return self._usage_zones
+ @usage_zones.setter
+ def usage_zones(self, value):
+ """
+ Set city object usage zones
+ :param value: [UsageZone]
+ """
+ self._usage_zones = value
+
@property
- def terrains(self) -> List[Surface]:
+ def terrains(self) -> Union[None, List[Surface]]:
"""
Get city object terrain surfaces
:return: [Surface]
@@ -170,26 +152,21 @@ class Building(CityObject):
self._basement_heated = int(value)
@property
- def name(self):
- """
- Get building name
- :return: str
- """
- return self._name
-
- @property
- def thermal_zones(self) -> List[ThermalZone]:
+ def thermal_zones(self) -> Union[None, List[ThermalZone]]:
"""
Get building thermal zones
:return: [ThermalZone]
"""
- if len(self._thermal_zones) == 0:
- if self.storeys is None:
- return []
- for storey in self.storeys:
- self._thermal_zones.append(storey.thermal_zone)
return self._thermal_zones
+ @thermal_zones.setter
+ def thermal_zones(self, value):
+ """
+ Set city object thermal zones
+ :param value: [ThermalZone]
+ """
+ self._thermal_zones = value
+
@property
def heated_volume(self):
"""
@@ -213,7 +190,7 @@ class Building(CityObject):
:param value: int
"""
if value is not None:
- self._year_of_construction = value
+ self._year_of_construction = int(value)
@property
def function(self) -> Union[None, str]:
@@ -341,6 +318,14 @@ class Building(CityObject):
break
return self._roof_type
+ @roof_type.setter
+ def roof_type(self, value):
+ """
+ Set roof type for the building flat or pitch
+ :return: str
+ """
+ self._roof_type = value
+
@property
def floor_area(self):
"""
@@ -354,24 +339,6 @@ class Building(CityObject):
self._floor_area += surface.perimeter_polygon.area
return self._floor_area
- @property
- def thermal_boundaries(self) -> List[ThermalBoundary]:
- """
- Get all thermal boundaries associated to the building's thermal zones
- :return: [ThermalBoundary]
- """
- if self._thermal_boundaries is None:
- self._thermal_boundaries = []
- for thermal_zone in self.thermal_zones:
- _thermal_boundary_duplicated = False
- for thermal_boundary in thermal_zone.thermal_boundaries:
- if len(thermal_boundary.thermal_zones) > 1:
- if thermal_zone != thermal_boundary.thermal_zones[1]:
- self._thermal_boundaries.append(thermal_boundary)
- else:
- self._thermal_boundaries.append(thermal_boundary)
- return self._thermal_boundaries
-
@property
def households(self) -> List[Household]:
"""
@@ -379,3 +346,16 @@ class Building(CityObject):
:return: List[Household]
"""
return self._households
+
+ @property
+ def is_conditioned(self):
+ """
+ Get building heated flag
+ :return: Boolean
+ """
+ if self.thermal_zones is None:
+ return False
+ for thermal_zone in self.thermal_zones:
+ if thermal_zone.hvac_system is not None:
+ return True
+ return False
diff --git a/city_model_structure/building_demand/appliances.py b/city_model_structure/building_demand/appliances.py
new file mode 100644
index 00000000..ed09f795
--- /dev/null
+++ b/city_model_structure/building_demand/appliances.py
@@ -0,0 +1,103 @@
+"""
+Appliances module
+SPDX - License - Identifier: LGPL - 3.0 - or -later
+Copyright © 2022 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
+"""
+from typing import Union
+from city_model_structure.attributes.schedule import Schedule
+
+
+class Appliances:
+ """
+ Appliances class
+ """
+ def __init__(self):
+ self._lighting_density = None
+ self._convective_fraction = None
+ self._radiant_fraction = None
+ self._latent_fraction = None
+ self._schedule = None
+
+ @property
+ def lighting_density(self) -> Union[None, float]:
+ """
+ Get lighting density in Watts per m2
+ :return: None or float
+ """
+ return self._lighting_density
+
+ @lighting_density.setter
+ def lighting_density(self, value):
+ """
+ Set lighting density in Watts per m2
+ :param value: float
+ """
+ if value is not None:
+ self._lighting_density = float(value)
+
+ @property
+ def convective_fraction(self) -> Union[None, float]:
+ """
+ Get convective fraction
+ :return: None or float
+ """
+ return self._convective_fraction
+
+ @convective_fraction.setter
+ def convective_fraction(self, value):
+ """
+ Set convective fraction
+ :param value: float
+ """
+ if value is not None:
+ self._convective_fraction = float(value)
+
+ @property
+ def radiant_fraction(self) -> Union[None, float]:
+ """
+ Get radiant fraction
+ :return: None or float
+ """
+ return self._radiant_fraction
+
+ @radiant_fraction.setter
+ def radiant_fraction(self, value):
+ """
+ Set radiant fraction
+ :param value: float
+ """
+ if value is not None:
+ self._radiant_fraction = float(value)
+
+ @property
+ def latent_fraction(self) -> Union[None, float]:
+ """
+ Get latent fraction
+ :return: None or float
+ """
+ return self._latent_fraction
+
+ @latent_fraction.setter
+ def latent_fraction(self, value):
+ """
+ Set latent fraction
+ :param value: float
+ """
+ if value is not None:
+ self._latent_fraction = float(value)
+
+ @property
+ def schedule(self) -> Union[None, Schedule]:
+ """
+ Get schedule
+ :return: None or Schedule
+ """
+ return self._schedule
+
+ @schedule.setter
+ def schedule(self, value):
+ """
+ Set schedule
+ :param value: Schedule
+ """
+ self._schedule = value
diff --git a/city_model_structure/building_demand/internal_gains.py b/city_model_structure/building_demand/internal_gains.py
index 75e60cb7..4cfeabb9 100644
--- a/city_model_structure/building_demand/internal_gains.py
+++ b/city_model_structure/building_demand/internal_gains.py
@@ -5,6 +5,7 @@ Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc
"""
from typing import Union
+from city_model_structure.attributes.schedule import Schedule
class InternalGains:
@@ -18,6 +19,7 @@ class InternalGains:
self._convective_fraction = None
self._radiative_fraction = None
self._latent_fraction = None
+ self._schedule = None
@property
def type(self) -> Union[None, str]:
@@ -103,3 +105,19 @@ class InternalGains:
"""
if value is not None:
self._latent_fraction = float(value)
+
+ @property
+ def schedule(self) -> Union[None, Schedule]:
+ """
+ Get internal gain schedule
+ :return: Schedule
+ """
+ return self._schedule
+
+ @schedule.setter
+ def schedule(self, value):
+ """
+ Set internal gain schedule
+ :param value: Schedule
+ """
+ self._schedule = value
diff --git a/city_model_structure/building_demand/lighting.py b/city_model_structure/building_demand/lighting.py
new file mode 100644
index 00000000..0407a432
--- /dev/null
+++ b/city_model_structure/building_demand/lighting.py
@@ -0,0 +1,103 @@
+"""
+Lighting module
+SPDX - License - Identifier: LGPL - 3.0 - or -later
+Copyright © 2022 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
+"""
+from typing import Union
+from city_model_structure.attributes.schedule import Schedule
+
+
+class Lighting:
+ """
+ Lighting class
+ """
+ def __init__(self):
+ self._lighting_density = None
+ self._convective_fraction = None
+ self._radiant_fraction = None
+ self._latent_fraction = None
+ self._schedule = None
+
+ @property
+ def lighting_density(self) -> Union[None, float]:
+ """
+ Get lighting density in Watts per m2
+ :return: None or float
+ """
+ return self._lighting_density
+
+ @lighting_density.setter
+ def lighting_density(self, value):
+ """
+ Set lighting density in Watts per m2
+ :param value: float
+ """
+ if value is not None:
+ self._lighting_density = float(value)
+
+ @property
+ def convective_fraction(self) -> Union[None, float]:
+ """
+ Get convective fraction
+ :return: None or float
+ """
+ return self._convective_fraction
+
+ @convective_fraction.setter
+ def convective_fraction(self, value):
+ """
+ Set convective fraction
+ :param value: float
+ """
+ if value is not None:
+ self._convective_fraction = float(value)
+
+ @property
+ def radiant_fraction(self) -> Union[None, float]:
+ """
+ Get radiant fraction
+ :return: None or float
+ """
+ return self._radiant_fraction
+
+ @radiant_fraction.setter
+ def radiant_fraction(self, value):
+ """
+ Set radiant fraction
+ :param value: float
+ """
+ if value is not None:
+ self._radiant_fraction = float(value)
+
+ @property
+ def latent_fraction(self) -> Union[None, float]:
+ """
+ Get latent fraction
+ :return: None or float
+ """
+ return self._latent_fraction
+
+ @latent_fraction.setter
+ def latent_fraction(self, value):
+ """
+ Set latent fraction
+ :param value: float
+ """
+ if value is not None:
+ self._latent_fraction = float(value)
+
+ @property
+ def schedule(self) -> Union[None, Schedule]:
+ """
+ Get schedule
+ :return: None or Schedule
+ """
+ return self._schedule
+
+ @schedule.setter
+ def schedule(self, value):
+ """
+ Set schedule
+ :param value: Schedule
+ """
+ self._schedule = value
diff --git a/city_model_structure/building_demand/material.py b/city_model_structure/building_demand/material.py
index cb015fa7..5762550d 100644
--- a/city_model_structure/building_demand/material.py
+++ b/city_model_structure/building_demand/material.py
@@ -6,6 +6,7 @@ Contributor Atiya atiya.atiya@mail.concordia.ca
Contributor Mohammad Reza mohammad.seyedabadi@mail.concordia.ca
"""
+import ast
from typing import Union
@@ -14,6 +15,7 @@ class Material:
Material class
"""
def __init__(self):
+ self._type = type
self._id = None
self._name = None
self._conductivity = None
@@ -25,7 +27,30 @@ class Material:
self._visible_absorptance = None
self._no_mass = False
self._thermal_resistance = None
- self._lca_id = None
+ self._embodied_carbon = None
+ self._embodied_carbon_unit = None
+ self._recycling_ratio = None
+ self._onsite_recycling_ratio = None
+ self._company_recycling_ratio = None
+ self._landfilling_ratio = None
+ self._cost = None
+ self._cost_unit = None
+
+ @property
+ def type(self):
+ """
+ Get material type
+ :return: str
+ """
+ return self._type
+
+ @type.setter
+ def type(self, value):
+ """
+ Set material type
+ :param value: string
+ """
+ self._type = str(value)
@property
def id(self):
@@ -213,10 +238,137 @@ class Material:
self._thermal_resistance = float(value)
@property
- def lca_id(self):
- return self._lca_id
+ def embodied_carbon(self) -> Union[None, float]:
+ """
+ Get material embodied carbon
+ :return: None or float
+ """
+ return self._embodied_carbon
- @lca_id.setter
- def lca_id(self, value):
- self._lca_id = value
+ @embodied_carbon.setter
+ def embodied_carbon(self, value):
+ """
+ Set material embodied carbon
+ :param value: float
+ """
+ if value is not None:
+ self._embodied_carbon = float(value)
+ @property
+ def embodied_carbon_unit(self) -> Union[None, str]:
+ """
+ Get material embodied carbon unit
+ :return: None or string
+ """
+ return self._embodied_carbon
+
+ @embodied_carbon_unit.setter
+ def embodied_carbon_unit(self, value):
+ """
+ Set material embodied carbon unit
+ :param value: string
+ """
+ if value is not None:
+ self._embodied_carbon_unit = str(value)
+
+ @property
+ def recycling_ratio(self) -> Union[None, float]:
+ """
+ Get material recycling ratio
+ :return: None or float
+ """
+ return self._recycling_ratio
+
+ @recycling_ratio.setter
+ def recycling_ratio(self, value):
+ """
+ Set material recycling ratio
+ :param value: float
+ """
+ if value is not None:
+ self._recycling_ratio = float(value)
+
+ @property
+ def onsite_recycling_ratio(self) -> Union[None, float]:
+ """
+ Get material onsite recycling ratio
+ :return: None or float
+ """
+ return self._onsite_recycling_ratio
+
+ @onsite_recycling_ratio.setter
+ def onsite_recycling_ratio(self, value):
+ """
+ Set material onsite recycling ratio
+ :param value: float
+ """
+ if value is not None:
+ self._onsite_recycling_ratio = float(value)
+
+ @property
+ def company_recycling_ratio(self) -> Union[None, float]:
+ """
+ Get material company recycling ratio
+ :return: None or float
+ """
+ return self._company_recycling_ratio
+
+ @company_recycling_ratio.setter
+ def company_recycling_ratio(self, value):
+ """
+ Set material company recycling ratio
+ :param value: float
+ """
+ if value is not None:
+ self._company_recycling_ratio = float(value)
+
+ @property
+ def landfilling_ratio(self) -> Union[None, float]:
+ """
+ Get material landfilling ratio
+ :return: None or float
+ """
+ return self._landfilling_ratio
+
+ @landfilling_ratio.setter
+ def landfilling_ratio(self, value):
+ """
+ Set material landfilling ratio
+ :param value: float
+ """
+ if value is not None:
+ self._landfilling_ratio = float(value)
+
+ @property
+ def cost(self) -> Union[None, float]:
+ """
+ Get material cost
+ :return: None or float
+ """
+ return self._cost
+
+ @cost.setter
+ def cost(self, value):
+ """
+ Set material cost
+ :param value: float
+ """
+ if value is not None:
+ self._cost = float(value)
+
+ @property
+ def cost_unit(self) -> Union[None, str]:
+ """
+ Get material cost unit
+ :return: None or string
+ """
+ return self._cost_unit
+
+ @cost_unit.setter
+ def cost_unit(self, value):
+ """
+ Set material cost unit
+ :param value: string
+ """
+ if value is not None:
+ self._cost_unit = float(value)
diff --git a/city_model_structure/building_demand/occupancy.py b/city_model_structure/building_demand/occupancy.py
new file mode 100644
index 00000000..443030ca
--- /dev/null
+++ b/city_model_structure/building_demand/occupancy.py
@@ -0,0 +1,121 @@
+"""
+Occupancy module
+SPDX - License - Identifier: LGPL - 3.0 - or -later
+Copyright © 2022 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
+"""
+from typing import Union, List
+from city_model_structure.attributes.schedule import Schedule
+from city_model_structure.building_demand.occupant import Occupant
+
+
+class Occupancy:
+ """
+ Occupancy class
+ """
+ def __init__(self):
+ self._occupancy_density = None
+ self._sensible_convective_internal_gain = None
+ self._sensible_radiant_internal_gain = None
+ self._latent_internal_gain = None
+ self._occupancy_schedule = None
+ self._occupants = None
+
+ @property
+ def occupancy_density(self) -> Union[None, float]:
+ """
+ Get density in m2 per person
+ :return: None or float
+ """
+ return self._occupancy_density
+
+ @occupancy_density.setter
+ def occupancy_density(self, value):
+ """
+ Set density in m2 per persons
+ :param value: float
+ """
+ if value is not None:
+ self._occupancy_density = float(value)
+
+ @property
+ def sensible_convective_internal_gain(self) -> Union[None, float]:
+ """
+ Get sensible convective internal gain in Watts per m2
+ :return: None or float
+ """
+ return self._sensible_convective_internal_gain
+
+ @sensible_convective_internal_gain.setter
+ def sensible_convective_internal_gain(self, value):
+ """
+ Set sensible convective internal gain in Watts per m2
+ :param value: float
+ """
+ if value is not None:
+ self._sensible_convective_internal_gain = float(value)
+
+ @property
+ def sensible_radiant_internal_gain(self) -> Union[None, float]:
+ """
+ Get sensible radiant internal gain in Watts per m2
+ :return: None or float
+ """
+ return self._sensible_radiant_internal_gain
+
+ @sensible_radiant_internal_gain.setter
+ def sensible_radiant_internal_gain(self, value):
+ """
+ Set sensible radiant internal gain in Watts per m2
+ :param value: float
+ """
+ if value is not None:
+ self._sensible_radiant_internal_gain = float(value)
+
+ @property
+ def latent_internal_gain(self) -> Union[None, float]:
+ """
+ Get latent internal gain in Watts per m2
+ :return: None or float
+ """
+ return self._latent_internal_gain
+
+ @latent_internal_gain.setter
+ def latent_internal_gain(self, value):
+ """
+ Set latent internal gain in Watts per m2
+ :param value: float
+ """
+ if value is not None:
+ self._latent_internal_gain = float(value)
+
+ @property
+ def occupancy_schedule(self) -> Union[None, Schedule]:
+ """
+ Get occupancy schedule
+ :return: None or Schedule
+ """
+ return self._occupancy_schedule
+
+ @occupancy_schedule.setter
+ def occupancy_schedule(self, value):
+ """
+ Set occupancy schedule
+ :param value: Schedule
+ """
+ self._occupancy_schedule = value
+
+ @property
+ def occupants(self) -> Union[None, List[Occupant]]:
+ """
+ Get list of occupants
+ :return: None or List of Occupant
+ """
+ return self._occupants
+
+ @occupants.setter
+ def occupants(self, value):
+ """
+ Set list of occupants
+ :param value: [Occupant]
+ """
+ self._occupants = value
diff --git a/city_model_structure/building_demand/occupants.py b/city_model_structure/building_demand/occupant.py
similarity index 79%
rename from city_model_structure/building_demand/occupants.py
rename to city_model_structure/building_demand/occupant.py
index 3d7a39b6..8a7dd533 100644
--- a/city_model_structure/building_demand/occupants.py
+++ b/city_model_structure/building_demand/occupant.py
@@ -1,19 +1,16 @@
"""
-Occupants module
+Occupant module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Sanam Dabirian sanam.dabirian@mail.concordia.ca
Contributors Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
-from typing import TypeVar
import calendar as cal
-UsageZone = TypeVar('UsageZone')
-
-class Occupants:
+class Occupant:
"""
- Occupants class
+ Occupant class
"""
def __init__(self):
@@ -24,15 +21,11 @@ class Occupants:
self._heat_dissipation = None
self._occupancy_rate = None
self._occupant_type = None
- self._usage_zone = None
- self._occupant_schedule = None
- self._number_of_occupants = None
self._arrival_time = None
self._departure_time = None
self._break_time = None
self._day_of_week = None
self._pd_of_meetings_duration = None
- self._complete_year_schedule = None
@property
def heat_dissipation(self):
@@ -82,46 +75,6 @@ class Occupants:
"""
self._occupant_type = float(value)
- @property
- def usage_zone(self) -> UsageZone:
- """
- Get the zone an occupant is in
- :return: UsageZone
- """
- return self._usage_zone
-
- @property
- def occupant_schedule(self):
- """
- Get the schedules when an occupant is in a zone (24 values, 1 per hour of the day)
- :return: [float]
- """
- return self._occupant_schedule
-
- @occupant_schedule.setter
- def occupant_schedule(self, value):
- """
- Set the schedules when an occupant is in a zone (24 values, 1 per hour of the day)
- :param value: [float]
- """
- self._occupant_schedule = [float(i) for i in value]
-
- @property
- def number_of_occupants(self):
- """
- Get the number of occupants
- :return: int
- """
- return self._number_of_occupants
-
- @number_of_occupants.setter
- def number_of_occupants(self, value):
- """
- Set the number of occupants
- :param value: int
- """
- self._number_of_occupants = int(value)
-
@property
def arrival_time(self):
"""
diff --git a/city_model_structure/building_demand/storey.py b/city_model_structure/building_demand/storey.py
index f7a211d8..143eec29 100644
--- a/city_model_structure/building_demand/storey.py
+++ b/city_model_structure/building_demand/storey.py
@@ -12,12 +12,10 @@ from city_model_structure.building_demand.thermal_zone import ThermalZone
class Storey:
- # todo: rethink this class for buildings with windows
"""
Storey class
"""
- def __init__(self, name, storey_surfaces, neighbours, volume):
- # todo: the information of the parent surface is lost -> need to recover it
+ def __init__(self, name, storey_surfaces, neighbours, volume, floor_area):
self._name = name
self._storey_surfaces = storey_surfaces
self._thermal_boundaries = None
@@ -25,6 +23,7 @@ class Storey:
self._thermal_zone = None
self._neighbours = neighbours
self._volume = volume
+ self._floor_area = floor_area
@property
def name(self):
@@ -59,7 +58,13 @@ class Storey:
if self._thermal_boundaries is None:
self._thermal_boundaries = []
for surface in self.surfaces:
- self._thermal_boundaries.append(ThermalBoundary(surface))
+ if surface.holes_polygons is None:
+ windows_areas = None
+ else:
+ windows_areas = []
+ for hole in surface.holes_polygons:
+ windows_areas.append(hole.area)
+ self._thermal_boundaries.append(ThermalBoundary(surface, surface.solid_polygon.area, windows_areas))
return self._thermal_boundaries
@property
@@ -81,7 +86,7 @@ class Storey:
:return: ThermalZone
"""
if self._thermal_zone is None:
- self._thermal_zone = ThermalZone(self.thermal_boundaries, self.volume)
+ self._thermal_zone = ThermalZone(self.thermal_boundaries, self.volume, self.floor_area)
return self._thermal_zone
@property
@@ -91,3 +96,11 @@ class Storey:
:return: float
"""
return self._volume
+
+ @property
+ def floor_area(self):
+ """
+ Get storey's floor area in square meters
+ :return: float
+ """
+ return self._floor_area
diff --git a/city_model_structure/building_demand/surface.py b/city_model_structure/building_demand/surface.py
index 1e2178c8..6c73d268 100644
--- a/city_model_structure/building_demand/surface.py
+++ b/city_model_structure/building_demand/surface.py
@@ -71,6 +71,7 @@ class Surface:
if value is not None:
self._id = str(value)
+ # todo: implement share surfaces
@property
def share_surfaces(self):
"""
@@ -191,6 +192,8 @@ class Surface:
Get surface type Ground, Wall or Roof
:return: str
"""
+
+ # todo: there are more types: internal wall, internal floor... this method must be redefined
if self._type is None:
grad = np.rad2deg(self.inclination)
if grad >= 170:
diff --git a/city_model_structure/building_demand/thermal_boundary.py b/city_model_structure/building_demand/thermal_boundary.py
index 7b444a14..946e7642 100644
--- a/city_model_structure/building_demand/thermal_boundary.py
+++ b/city_model_structure/building_demand/thermal_boundary.py
@@ -5,25 +5,24 @@ Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc
Contributors Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
import uuid
-from typing import List, TypeVar, Union
+from typing import List, Union
+from helpers.configuration_helper import ConfigurationHelper as ch
from city_model_structure.building_demand.layer import Layer
from city_model_structure.building_demand.thermal_opening import ThermalOpening
from city_model_structure.building_demand.thermal_zone import ThermalZone
from city_model_structure.building_demand.surface import Surface
-Polygon = TypeVar('Polygon')
-
class ThermalBoundary:
"""
ThermalBoundary class
"""
- def __init__(self, surface):
- self._surface = surface
+ def __init__(self, parent_surface, opaque_area, windows_areas):
+ self._parent_surface = parent_surface
+ self._opaque_area = opaque_area
+ self._windows_areas = windows_areas
self._id = None
self._thermal_zones = None
- # ToDo: up to at least LOD2 will be just one thermal opening per Thermal boundary only if window_ratio > 0,
- # review for LOD3 and LOD4
self._thermal_openings = None
self._layers = None
self._outside_solar_absorptance = None
@@ -32,16 +31,16 @@ class ThermalBoundary:
self._u_value = None
self._shortwave_reflectance = None
self._construction_name = None
- self._hi = 3.5
- self._he = 20
- self._window_ratio = None
+ self._hi = ch().convective_heat_transfer_coefficient_interior
+ self._he = ch().convective_heat_transfer_coefficient_exterior
self._refurbishment_measure = None
- self._surface_geometry = None
self._thickness = None
self._virtual_internal_surface = None
self._inside_emissivity = None
self._alpha_coefficient = None
self._radiative_coefficient = None
+ self._window_ratio = None
+ self._calculated = False
@property
def id(self):
@@ -54,13 +53,12 @@ class ThermalBoundary:
return self._id
@property
- def surface(self) -> Surface:
+ def parent_surface(self) -> Surface:
"""
Get the surface that belongs to the thermal boundary
:return: Surface
"""
- # todo: in LoD4 this property will be a list of surfaces, not only one
- return self._surface
+ return self._parent_surface
@property
def thermal_zones(self) -> List[ThermalZone]:
@@ -73,47 +71,35 @@ class ThermalBoundary:
@thermal_zones.setter
def thermal_zones(self, value):
"""
- Set the thermal zones delimited by the thermal boundary
+ Get the thermal zones delimited by the thermal boundary
:param value: [ThermalZone]
"""
self._thermal_zones = value
+ # todo: do I need these two??
@property
def azimuth(self):
"""
Get the thermal boundary azimuth in radians
:return: float
"""
- return self._surface.azimuth
+ return self.parent_surface.azimuth
@property
def inclination(self):
"""
- Set the thermal boundary inclination in radians
+ Get the thermal boundary inclination in radians
:return: float
"""
- return self._surface.inclination
+ return self.parent_surface.inclination
@property
- def area(self):
+ def opaque_area(self):
"""
- Set the thermal boundary area in square meters
+ Get the thermal boundary area in square meters
:return: float
"""
- # to check the lod without depending on that parameter
- if float(self.surface.solid_polygon.area) - float(self.surface.perimeter_polygon.area) < 1e-3:
- area = float(self.surface.perimeter_polygon.area) * (1 - float(self.window_ratio))
- else:
- area = self.surface.solid_polygon.area
- return area
-
- @property
- def _total_area_including_windows(self):
- """
- Get the thermal boundary plus windows area in square meters
- :return: float
- """
- return self.surface.perimeter_polygon.area
+ return float(self._opaque_area)
@property
def thickness(self):
@@ -125,7 +111,8 @@ class ThermalBoundary:
self._thickness = 0.0
if self.layers is not None:
for layer in self.layers:
- self._thickness += layer.thickness
+ if not layer.material.no_mass:
+ self._thickness += layer.thickness
return self._thickness
@property
@@ -181,30 +168,34 @@ class ThermalBoundary:
self._outside_visible_absorptance = float(value)
@property
- def thermal_openings(self) -> List[ThermalOpening]:
+ def thermal_openings(self) -> Union[None, List[ThermalOpening]]:
"""
Get thermal boundary thermal openings
:return: [ThermalOpening]
"""
if self._thermal_openings is None:
- if float(self.window_ratio) > 0:
- thermal_opening = ThermalOpening()
- thermal_opening.area = float(self._total_area_including_windows) * float(self.window_ratio)
- thermal_opening.hi = self.hi
- thermal_opening.he = self.he
- self._thermal_openings = [thermal_opening]
+ if self.window_ratio is not None:
+ if self.window_ratio == 0:
+ self._thermal_openings = []
+ else:
+ thermal_opening = ThermalOpening()
+ if self.window_ratio == 1:
+ _area = self.opaque_area
+ else:
+ _area = self.opaque_area * self.window_ratio / (1-self.window_ratio)
+ thermal_opening.area = _area
+ self._thermal_openings = [thermal_opening]
else:
- self._thermal_openings = []
+ if len(self.windows_areas) > 0:
+ self._thermal_openings = []
+ for window_area in self.windows_areas:
+ thermal_opening = ThermalOpening()
+ thermal_opening.area = window_area
+ self._thermal_openings.append(thermal_opening)
+ else:
+ self._thermal_openings = []
return self._thermal_openings
- @thermal_openings.setter
- def thermal_openings(self, value):
- """
- Set thermal boundary thermal openings
- :param value: [ThermalOpening]
- """
- self._thermal_openings = value
-
@property
def construction_name(self) -> Union[None, str]:
"""
@@ -244,24 +235,47 @@ class ThermalBoundary:
Get thermal boundary surface type
:return: str
"""
- return self._surface.type
+ return self.parent_surface.type
@property
def window_ratio(self) -> Union[None, float]:
"""
Get thermal boundary window ratio
- :return: None or float
+ It returns the window ratio calculated as the total windows' areas in a wall divided by
+ the total (opaque + transparent) area of that wall if windows are defined in the geometry imported.
+ If not, it returns the window ratio imported from an external source (e.g. construction library, manually assigned).
+ If none of those sources are available, it returns None.
+ :return: float
"""
+ if self.windows_areas is not None:
+ if not self._calculated:
+ _calculated = True
+ if len(self.windows_areas) == 0:
+ self._window_ratio = 0
+ else:
+ total_window_area = 0
+ for window_area in self.windows_areas:
+ total_window_area += window_area
+ self._window_ratio = total_window_area / (self.opaque_area + total_window_area)
return self._window_ratio
@window_ratio.setter
def window_ratio(self, value):
"""
Set thermal boundary window ratio
- :param value: float
+ :param value: str
"""
- if value is not None:
- self._window_ratio = float(value)
+ if self._calculated:
+ raise ValueError('Window ratio cannot be assigned when the windows are defined in the geometry.')
+ self._window_ratio = float(value)
+
+ @property
+ def windows_areas(self) -> [float]:
+ """
+ Get windows areas
+ :return: [float]
+ """
+ return self._windows_areas
@property
def u_value(self) -> Union[None, float]:
@@ -327,7 +341,7 @@ class ThermalBoundary:
:param value: float
"""
if value is not None:
- self._hi = float(value)
+ self._hi = value
@property
def he(self) -> Union[None, float]:
@@ -344,14 +358,7 @@ class ThermalBoundary:
:param value: float
"""
if value is not None:
- self._he = float(value)
-
- @property
- def surface_geometry(self):
- """
- Raises not implemented error
- """
- raise NotImplementedError
+ self._he = value
@property
def virtual_internal_surface(self) -> Surface:
@@ -360,10 +367,9 @@ class ThermalBoundary:
:return: Surface
"""
if self._virtual_internal_surface is None:
- self._virtual_internal_surface = self.surface.inverse
+ self._virtual_internal_surface = self.parent_surface.inverse
return self._virtual_internal_surface
- # todo: need extract information from construction library or assume them at the beginning of workflows
@property
def inside_emissivity(self) -> Union[None, float]:
"""
diff --git a/city_model_structure/building_demand/thermal_control.py b/city_model_structure/building_demand/thermal_control.py
new file mode 100644
index 00000000..20888023
--- /dev/null
+++ b/city_model_structure/building_demand/thermal_control.py
@@ -0,0 +1,86 @@
+"""
+ThermalControl module
+SPDX - License - Identifier: LGPL - 3.0 - or -later
+Copyright © 2022 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
+"""
+from typing import Union
+from city_model_structure.attributes.schedule import Schedule
+
+
+class ThermalControl:
+ """
+ ThermalControl class
+ """
+ def __init__(self):
+ self._heating_set_point = None
+ self._cooling_set_point = None
+ self._hvac_availability = None
+ self._heating_set_back = None
+
+ @property
+ def heating_set_point(self) -> Union[None, Schedule]:
+ """
+ Get heating set point defined for a thermal zone in Celsius
+ :return: None or Schedule
+ """
+ return self._heating_set_point
+
+ @heating_set_point.setter
+ def heating_set_point(self, value):
+ """
+ Set heating set point defined for a thermal zone in Celsius
+ :param value: Schedule
+ """
+ self._heating_set_point = value
+
+ @property
+ def heating_set_back(self) -> Union[None, float]:
+ """
+ Get heating set back defined for a thermal zone in Celsius
+ Heating set back is the only parameter which is not a schedule as it is either one value or it is implicit in the
+ set point schedule
+ :return: None or float
+ """
+ return self._heating_set_back
+
+ @heating_set_back.setter
+ def heating_set_back(self, value):
+ """
+ Set heating set back defined for a thermal zone in Celsius
+ :param value: float
+ """
+ if value is not None:
+ self._heating_set_back = float(value)
+
+ @property
+ def cooling_set_point(self) -> Union[None, Schedule]:
+ """
+ Get cooling set point defined for a thermal zone in Celsius
+ :return: None or Schedule
+ """
+ return self._cooling_set_point
+
+ @cooling_set_point.setter
+ def cooling_set_point(self, value):
+ """
+ Set cooling set point defined for a thermal zone in Celsius
+ :param value: Schedule
+ """
+ self._cooling_set_point = value
+
+ @property
+ def hvac_availability(self) -> Union[None, Schedule]:
+ """
+ Get the availability of the conditioning system defined for a thermal zone
+ :return: None or Schedule
+ """
+ return self._hvac_availability
+
+ @hvac_availability.setter
+ def hvac_availability(self, value):
+ """
+ Set the availability of the conditioning system defined for a thermal zone
+ :param value: Schedule
+ """
+ self._hvac_availability = value
+
diff --git a/city_model_structure/building_demand/thermal_opening.py b/city_model_structure/building_demand/thermal_opening.py
index 0683f095..101dcffc 100644
--- a/city_model_structure/building_demand/thermal_opening.py
+++ b/city_model_structure/building_demand/thermal_opening.py
@@ -6,6 +6,7 @@ Contributors Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
import uuid
from typing import TypeVar, Union
+from helpers.configuration_helper import ConfigurationHelper as ch
Polygon = TypeVar('Polygon')
@@ -25,9 +26,8 @@ class ThermalOpening:
self._front_side_solar_transmittance_at_normal_incidence = None
self._back_side_solar_transmittance_at_normal_incidence = None
self._overall_u_value = None
- self._hi = None
- self._he = None
- self._surface_geometry = None
+ self._hi = ch().convective_heat_transfer_coefficient_interior
+ self._he = ch().convective_heat_transfer_coefficient_exterior
self._inside_emissivity = None
self._alpha_coefficient = None
self._radiative_coefficient = None
@@ -240,15 +240,6 @@ class ThermalOpening:
if value is not None:
self._he = float(value)
- @property
- def surface_geometry(self) -> Polygon:
- """
- Get the polygon that defines the thermal opening
- :return: Polygon
- """
- return self._surface_geometry
-
- # todo: need extract information from construction library or assume them at the beginning of workflows
@property
def inside_emissivity(self) -> Union[None, float]:
"""
diff --git a/city_model_structure/building_demand/thermal_zone.py b/city_model_structure/building_demand/thermal_zone.py
index a3ad26e1..dd20bc28 100644
--- a/city_model_structure/building_demand/thermal_zone.py
+++ b/city_model_structure/building_demand/thermal_zone.py
@@ -5,32 +5,33 @@ Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc
Contributors Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
import uuid
-from typing import List, TypeVar, Union
+from typing import List, Union, Tuple, TypeVar
from city_model_structure.building_demand.usage_zone import UsageZone
-import ast
+from city_model_structure.building_demand.thermal_control import ThermalControl
+from city_model_structure.energy_systems.hvac_system import HvacSystem
+from city_model_structure.attributes.schedule import Schedule
ThermalBoundary = TypeVar('ThermalBoundary')
-Polyhedron = TypeVar('Polyhedron')
class ThermalZone:
"""
ThermalZone class
"""
- def __init__(self, thermal_boundaries, volume):
- self._floor_area = None
+ def __init__(self, thermal_boundaries, volume, floor_area):
+ self._id = None
+ self._floor_area = floor_area
self._thermal_boundaries = thermal_boundaries
- self._is_mechanically_ventilated = None
self._additional_thermal_bridge_u_value = None
self._effective_thermal_capacity = None
self._indirectly_heated_area_ratio = None
self._infiltration_rate_system_on = None
self._infiltration_rate_system_off = None
- self._usage_zones = []
+ self._usage_zones = None
self._volume = volume
- self._volume_geometry = None
- self._id = None
self._ordinate_number = None
+ self._thermal_control = None
+ self._hvac_system = None
@property
def id(self):
@@ -43,34 +44,11 @@ class ThermalZone:
return self._id
@property
- def is_mechanically_ventilated(self) -> Union[None, bool]:
- """
- Get thermal zone mechanical ventilation flag
- :return: None or Boolean
- """
- return self._is_mechanically_ventilated
-
- @is_mechanically_ventilated.setter
- def is_mechanically_ventilated(self, value):
- """
- Set thermal zone mechanical ventilation flag
- :param value: Boolean
- """
- if value is not None:
- self._is_mechanically_ventilated = ast.literal_eval(value)
-
- @property
- def floor_area(self):
+ def floor_area(self) -> float:
"""
Get thermal zone floor area in m2
:return: float
"""
- if self._floor_area is None:
- self._floor_area = 0
- for thermal_boundary in self.thermal_boundaries:
- s = thermal_boundary.surface
- if s.type == 'Ground':
- self._floor_area += s.perimeter_polygon.area
return self._floor_area
@property
@@ -133,10 +111,10 @@ class ThermalZone:
self._indirectly_heated_area_ratio = float(value)
@property
- def infiltration_rate_system_on(self) -> Union[None, float]:
+ def infiltration_rate_system_on(self) -> Union[None, Schedule]:
"""
Get thermal zone infiltration rate system on in air changes per hour (ACH)
- :return: None or float
+ :return: None or Schedule
"""
return self._infiltration_rate_system_on
@@ -144,16 +122,15 @@ class ThermalZone:
def infiltration_rate_system_on(self, value):
"""
Set thermal zone infiltration rate system on in air changes per hour (ACH)
- :param value: float
+ :param value: Schedule
"""
- if value is not None:
- self._infiltration_rate_system_on = float(value)
+ self._infiltration_rate_system_on = value
@property
- def infiltration_rate_system_off(self) -> Union[None, float]:
+ def infiltration_rate_system_off(self) -> Union[None, Schedule]:
"""
Get thermal zone infiltration rate system off in air changes per hour (ACH)
- :return: None or float
+ :return: None or Schedule
"""
return self._infiltration_rate_system_off
@@ -161,15 +138,14 @@ class ThermalZone:
def infiltration_rate_system_off(self, value):
"""
Set thermal zone infiltration rate system on in air changes per hour (ACH)
- :param value: float
+ :param value: Schedule
"""
- if value is not None:
- self._infiltration_rate_system_off = float(value)
+ self._infiltration_rate_system_off = value
@property
- def usage_zones(self) -> List[UsageZone]:
+ def usage_zones(self) -> Tuple[float, UsageZone]:
"""
- Get thermal zone usage zones
+ Get list of usage zones and the percentage of thermal zone's volume affected by that usage
:return: [UsageZone]
"""
return self._usage_zones
@@ -177,8 +153,8 @@ class ThermalZone:
@usage_zones.setter
def usage_zones(self, values):
"""
- Set thermal zone usage zones
- :param values: [UsageZone]
+ Set list of usage zones and the percentage of thermal zone's volume affected by that usage
+ :param values: Tuple[float, UsageZone]
"""
self._usage_zones = values
@@ -190,14 +166,6 @@ class ThermalZone:
"""
return self._volume
- @property
- def volume_geometry(self) -> Polyhedron:
- """
- Get the polyhedron defined by the thermal zone
- :return: Polyhedron
- """
- return self._volume_geometry
-
@property
def ordinate_number(self) -> Union[None, int]:
"""
@@ -214,3 +182,35 @@ class ThermalZone:
"""
if value is not None:
self._ordinate_number = int(value)
+
+ @property
+ def thermal_control(self) -> Union[None, ThermalControl]:
+ """
+ Get thermal control of this thermal zone
+ :return: None or ThermalControl
+ """
+ return self._thermal_control
+
+ @thermal_control.setter
+ def thermal_control(self, value):
+ """
+ Set thermal control for this thermal zone
+ :param value: ThermalControl
+ """
+ self._thermal_control = value
+
+ @property
+ def hvac_system(self) -> Union[None, HvacSystem]:
+ """
+ Get HVAC system installed for this thermal zone
+ :return: None or HvacSystem
+ """
+ return self._hvac_system
+
+ @hvac_system.setter
+ def hvac_system(self, value):
+ """
+ Set HVAC system installed for this thermal zone
+ :param value: HvacSystem
+ """
+ self._hvac_system = value
diff --git a/city_model_structure/building_demand/usage_zone.py b/city_model_structure/building_demand/usage_zone.py
index a2c16435..590ce6d5 100644
--- a/city_model_structure/building_demand/usage_zone.py
+++ b/city_model_structure/building_demand/usage_zone.py
@@ -5,13 +5,12 @@ Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.mons
Contributors Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
import uuid
-from typing import List, TypeVar, Union
-import ast
-
-InternalGains = TypeVar('InternalGains')
-Occupants = TypeVar('Occupants')
-Polyhedron = TypeVar('Polyhedron')
-Schedule = TypeVar('Schedule')
+from typing import List, Union
+import helpers.constants as cte
+from city_model_structure.building_demand.internal_gains import InternalGains
+from city_model_structure.building_demand.occupancy import Occupancy
+from city_model_structure.building_demand.lighting import Lighting
+from city_model_structure.building_demand.appliances import Appliances
class UsageZone:
@@ -21,23 +20,15 @@ class UsageZone:
def __init__(self):
self._id = None
self._usage = None
- self._internal_gains = None
- self._heating_setpoint = None
- self._heating_setback = None
- self._cooling_setpoint = None
- self._occupancy_density = None
+ self._not_detailed_source_mean_annual_internal_gains = None
self._hours_day = None
self._days_year = None
- self._dhw_average_volume_pers_day = None
- self._dhw_preparation_temperature = None
- self._electrical_app_average_consumption_sqm_year = None
+# self._electrical_app_average_consumption_sqm_year = None
self._mechanical_air_change = None
- self._occupants = None
- self._schedules = None
- self._volume = None
- self._volume_geometry = None
- self._is_heated = False
- self._is_cooled = False
+ self._occupancy = None
+ self._lighting = None
+ self._appliances = None
+ self._internal_gains = None
@property
def id(self):
@@ -50,71 +41,20 @@ class UsageZone:
return self._id
@property
- def internal_gains(self) -> List[InternalGains]:
+ def not_detailed_source_mean_annual_internal_gains(self) -> List[InternalGains]:
"""
- Get usage zone internal gains
+ Get usage zone internal gains with unknown energy source
:return: [InternalGains]
"""
- return self._internal_gains
+ return self._not_detailed_source_mean_annual_internal_gains
- @internal_gains.setter
- def internal_gains(self, value):
+ @not_detailed_source_mean_annual_internal_gains.setter
+ def not_detailed_source_mean_annual_internal_gains(self, value):
"""
- Set usage zone internal gains
+ Set usage zone internal gains with unknown energy source
:param value: [InternalGains]
"""
- self._internal_gains = value
-
- @property
- def heating_setpoint(self) -> Union[None, float]:
- """
- Get usage zone heating set point in Celsius
- :return: None or float
- """
- return self._heating_setpoint
-
- @heating_setpoint.setter
- def heating_setpoint(self, value):
- """
- Set usage zone heating set point in Celsius
- :param value: float
- """
- if value is not None:
- self._heating_setpoint = float(value)
-
- @property
- def heating_setback(self) -> Union[None, float]:
- """
- Get usage zone heating setback in Celsius
- :return: None or float
- """
- return self._heating_setback
-
- @heating_setback.setter
- def heating_setback(self, value):
- """
- Set usage zone heating setback in Celsius
- :param value: float
- """
- if value is not None:
- self._heating_setback = float(value)
-
- @property
- def cooling_setpoint(self) -> Union[None, float]:
- """
- Get usage zone cooling setpoint in Celsius
- :return: None or float
- """
- return self._cooling_setpoint
-
- @cooling_setpoint.setter
- def cooling_setpoint(self, value):
- """
- Set usage zone cooling setpoint in Celsius
- :param value: float
- """
- if value is not None:
- self._cooling_setpoint = float(value)
+ self._not_detailed_source_mean_annual_internal_gains = value
@property
def hours_day(self) -> Union[None, float]:
@@ -184,89 +124,6 @@ class UsageZone:
if value is not None:
self._usage = str(value)
- @property
- def occupants(self) -> List[Occupants]:
- """
- Get occupants data
- :return: [Occupants]
- """
- return self._occupants
-
- @occupants.setter
- def occupants(self, values):
- """
- Set occupants data
- :param values: [Occupants]
- """
- self._occupants = values
-
- @property
- def schedules(self) -> List[Schedule]:
- """
- Get usage zone schedules
- :return: List[Schedule]
- """
- return self._schedules
-
- @schedules.setter
- def schedules(self, values):
- """
- Set usage zone schedules
- :param values: List[Schedule]
- """
- self._schedules = values
-
- @property
- def occupancy_density(self) -> Union[None, float]:
- """
- Get density in persons per m2
- :return: None or float
- """
- return self._occupancy_density
-
- @occupancy_density.setter
- def occupancy_density(self, value):
- """
- Set density in persons per m2
- :param value: float
- """
- if value is not None:
- self._occupancy_density = float(value)
-
- @property
- def dhw_average_volume_pers_day(self) -> Union[None, float]:
- """
- Get average DHW consumption in cubic meters per person per day
- :return: None or float
- """
- return self._dhw_average_volume_pers_day
-
- @dhw_average_volume_pers_day.setter
- def dhw_average_volume_pers_day(self, value):
- """
- Set average DHW consumption in cubic meters per person per day
- :param value: float
- """
- if value is not None:
- self._dhw_average_volume_pers_day = float(value)
-
- @property
- def dhw_preparation_temperature(self) -> Union[None, float]:
- """
- Get preparation temperature of the DHW in Celsius
- :return: None or float
- """
- return self._dhw_preparation_temperature
-
- @dhw_preparation_temperature.setter
- def dhw_preparation_temperature(self, value):
- """
- Set preparation temperature of the DHW in Celsius
- :param value: float
- """
- if value is not None:
- self._dhw_preparation_temperature = float(value)
-
@property
def electrical_app_average_consumption_sqm_year(self) -> Union[None, float]:
"""
@@ -285,60 +142,93 @@ class UsageZone:
self._electrical_app_average_consumption_sqm_year = float(value)
@property
- def volume_geometry(self) -> Polyhedron:
+ def occupancy(self) -> Union[None, Occupancy]:
"""
- Get the polyhedron defined by the usage zone
- :return: Polyhedron
+ Get occupancy in the usage zone
+ :return: None or Occupancy
"""
- return self._volume_geometry
+ return self._occupancy
+
+ @occupancy.setter
+ def occupancy(self, value):
+ """
+ Set occupancy in the usage zone
+ :param value: Occupancy
+ """
+ self._occupancy = value
@property
- def volume(self) -> Union[None, float]:
+ def lighting(self) -> Union[None, Lighting]:
"""
- Get the volume in cubic meters
- :return: None or float
+ Get lighting information
+ :return: None or Lighting
"""
- return self._volume
+ return self._lighting
- @volume.setter
- def volume(self, value):
+ @lighting.setter
+ def lighting(self, value):
"""
- Set volume in cubic meters
- :param value: float
+ Set lighting information
+ :param value: Lighting
"""
- if value is not None:
- self._volume = float(value)
+ self._lighting = value
@property
- def is_heated(self) -> Union[None, bool]:
+ def appliances(self) -> Union[None, Appliances]:
"""
- Get thermal zone heated flag
- :return: None or Boolean
+ Get appliances information
+ :return: None or Appliances
"""
- return self._is_heated
+ return self._appliances
- @is_heated.setter
- def is_heated(self, value):
+ @appliances.setter
+ def appliances(self, value):
"""
- Set thermal zone heated flag
- :param value: Boolean
+ Set appliances information
+ :param value: Appliances
"""
- if value is not None:
- self._is_heated = ast.literal_eval(value)
+ self._appliances = value
- @property
- def is_cooled(self) -> Union[None, bool]:
+ def get_internal_gains(self) -> [InternalGains]:
"""
- Get thermal zone cooled flag
- :return: None or Boolean
+ Calculates and returns the list of all internal gains defined
+ :return: InternalGains
"""
- return self._is_cooled
-
- @is_cooled.setter
- def is_cooled(self, value):
- """
- Set thermal zone cooled flag
- :param value: Boolean
- """
- if value is not None:
- self._is_cooled = ast.literal_eval(value)
+ if self.occupancy is not None:
+ if self.occupancy.latent_internal_gain is not None:
+ _internal_gain = InternalGains()
+ _internal_gain.type = cte.OCCUPANCY
+ _total_heat_gain = (self.occupancy.sensible_convective_internal_gain
+ + self.occupancy.sensible_radiant_internal_gain
+ + self.occupancy.latent_internal_gain)
+ _internal_gain.average_internal_gain = _total_heat_gain
+ _internal_gain.latent_fraction = self.occupancy.latent_internal_gain / _total_heat_gain
+ _internal_gain.radiative_fraction = self.occupancy.sensible_radiant_internal_gain / _total_heat_gain
+ _internal_gain.convective_fraction = self.occupancy.sensible_convective_internal_gain / _total_heat_gain
+ _internal_gain.schedule = self.occupancy.occupancy_schedule
+ self._internal_gains = [_internal_gain]
+ if self.lighting is not None:
+ _internal_gain = InternalGains()
+ _internal_gain.type = cte.LIGHTING
+ _internal_gain.average_internal_gain = self.lighting.lighting_density
+ _internal_gain.latent_fraction = self.lighting.latent_fraction
+ _internal_gain.radiative_fraction = self.lighting.radiant_fraction
+ _internal_gain.convective_fraction = self.lighting.convective_fraction
+ _internal_gain.schedule = self.lighting.schedule
+ if self._internal_gains is not None:
+ self._internal_gains.append(_internal_gain)
+ else:
+ self._internal_gains = [_internal_gain]
+ if self.appliances is not None:
+ _internal_gain = InternalGains()
+ _internal_gain.type = cte.APPLIANCES
+ _internal_gain.average_internal_gain = self.appliances.lighting_density
+ _internal_gain.latent_fraction = self.appliances.latent_fraction
+ _internal_gain.radiative_fraction = self.appliances.radiant_fraction
+ _internal_gain.convective_fraction = self.appliances.convective_fraction
+ _internal_gain.schedule = self.appliances.schedule
+ if self._internal_gains is not None:
+ self._internal_gains.append(_internal_gain)
+ else:
+ self._internal_gains = [_internal_gain]
+ return self._internal_gains
diff --git a/city_model_structure/city_object.py b/city_model_structure/city_object.py
index 2147909b..d71e3cea 100644
--- a/city_model_structure/city_object.py
+++ b/city_model_structure/city_object.py
@@ -36,13 +36,21 @@ class CityObject:
self._beam = dict()
self._sensors = []
+ @property
+ def name(self):
+ """
+ Get building name
+ :return: str
+ """
+ return self._name
+
@property
def lod(self) -> int:
"""
Get city object level of detail 1, 2, 3 or 4
:return: int
"""
- lod = math.log(self._lod, 2) + 1
+ lod = int(math.log(self._lod, 2) + 1)
return lod
@property
diff --git a/city_model_structure/energy_systems/hvac_system.py b/city_model_structure/energy_systems/hvac_system.py
new file mode 100644
index 00000000..4bc6edf3
--- /dev/null
+++ b/city_model_structure/energy_systems/hvac_system.py
@@ -0,0 +1,32 @@
+"""
+HvacSystem module
+SPDX - License - Identifier: LGPL - 3.0 - or -later
+Copyright © 2022 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
+"""
+from typing import Union
+
+
+class HvacSystem:
+ """
+ HvacSystem class
+ """
+ def __init__(self):
+ self._type = None
+
+ @property
+ def type(self) -> Union[None, str]:
+ """
+ Get hvac system type a thermal zone
+ :return: None or str
+ """
+ return self._type
+
+ @type.setter
+ def type(self, value):
+ """
+ Set heating set point defined for a thermal zone
+ :param value: str
+ """
+ if value is not None:
+ self._type = str(value)
+
diff --git a/city_model_structure/iot/concordia_energy_sensor.py b/city_model_structure/iot/concordia_energy_sensor.py
new file mode 100644
index 00000000..afc7b930
--- /dev/null
+++ b/city_model_structure/iot/concordia_energy_sensor.py
@@ -0,0 +1,45 @@
+"""
+Energy Sensor module
+SPDX - License - Identifier: LGPL - 3.0 - or -later
+Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
+"""
+
+import pandas as pd
+from city_model_structure.iot.sensor import Sensor
+
+
+class ConcordiaEnergySensor(Sensor):
+ """
+ Concordia energy sensor.
+ """
+
+ def __init__(self, name):
+ super().__init__()
+ self._name = name
+ self._interval = 5
+ self._interval_units = 'minutes'
+ self._type = 'ConcordiaEnergySensor'
+ self._units = 'kW'
+ self._measures = pd.DataFrame(columns=["Date time", "Energy consumption"])
+
+ @property
+ def measures(self) -> pd.DataFrame:
+ """
+ Get sensor measures [yyyy-mm-dd, hh:mm:ss kW]
+ :return: DataFrame["Date time", "Energy consumption"]
+ """
+ return self._measures
+
+ @measures.deleter
+ def measures(self):
+ """
+ Delete sensor measures
+ """
+ self._measures.drop = None
+
+ def add_period(self, measures):
+ """
+ Add or update a period measures to the dataframe
+ """
+ measures = self._measures.append(measures, ignore_index=True)
+ self._measures = measures.drop_duplicates('Date time', keep='last')
diff --git a/city_model_structure/iot/concordia_gas_flow_sensor.py b/city_model_structure/iot/concordia_gas_flow_sensor.py
new file mode 100644
index 00000000..86da31bd
--- /dev/null
+++ b/city_model_structure/iot/concordia_gas_flow_sensor.py
@@ -0,0 +1,45 @@
+"""
+Gas Flow Sensor module
+SPDX - License - Identifier: LGPL - 3.0 - or -later
+Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
+"""
+
+import pandas as pd
+from city_model_structure.iot.sensor import Sensor
+
+
+class ConcordiaGasFlowSensor(Sensor):
+ """
+ Concordia gas flow sensor.
+ """
+
+ def __init__(self, name):
+ super().__init__()
+ self._name = name
+ self._interval = 5
+ self._interval_units = 'minutes'
+ self._type = 'ConcordiaGasFlowSensor'
+ self._units = 'm3'
+ self._measures = pd.DataFrame(columns=["Date time", "Gas Flow Cumulative Monthly"])
+
+ @property
+ def measures(self) -> pd.DataFrame:
+ """
+ Get sensor measures [yyyy-mm-dd, hh:mm:ss m3]
+ :return: DataFrame["Date time", "Gas Flow Cumulative Monthly"]
+ """
+ return self._measures
+
+ @measures.deleter
+ def measures(self):
+ """
+ Delete sensor measures
+ """
+ self._measures.drop = None
+
+ def add_period(self, measures):
+ """
+ Add or update a period measures to the dataframe
+ """
+ measures = self._measures.append(measures, ignore_index=True)
+ self._measures = measures.drop_duplicates('Date time', keep='last')
diff --git a/city_model_structure/iot/concordia_temperature_sensor.py b/city_model_structure/iot/concordia_temperature_sensor.py
new file mode 100644
index 00000000..9ae6079c
--- /dev/null
+++ b/city_model_structure/iot/concordia_temperature_sensor.py
@@ -0,0 +1,45 @@
+"""
+Temperature Sensor module
+SPDX - License - Identifier: LGPL - 3.0 - or -later
+Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
+"""
+
+import pandas as pd
+from city_model_structure.iot.sensor import Sensor
+
+
+class ConcordiaTemperatureSensor(Sensor):
+ """
+ Concordia temperature sensor.
+ """
+
+ def __init__(self, name):
+ super().__init__()
+ self._name = name
+ self._interval = 5
+ self._interval_units = 'minutes'
+ self._type = 'ConcordiaTemperatureSensor'
+ self._units = 'Celsius'
+ self._measures = pd.DataFrame(columns=["Date time", "Temperature"])
+
+ @property
+ def measures(self) -> pd.DataFrame:
+ """
+ Get sensor measures [yyyy-mm-dd, hh:mm:ss Celsius]
+ :return: DataFrame["Date time", "Temperature"]
+ """
+ return self._measures
+
+ @measures.deleter
+ def measures(self):
+ """
+ Delete sensor measures
+ """
+ self._measures.drop = None
+
+ def add_period(self, measures):
+ """
+ Add or update a period measures to the dataframe
+ """
+ measures = self._measures.append(measures, ignore_index=True)
+ self._measures = measures.drop_duplicates('Date time', keep='last')
diff --git a/city_model_structure/iot/sensor.py b/city_model_structure/iot/sensor.py
index c3801749..4eceafd1 100644
--- a/city_model_structure/iot/sensor.py
+++ b/city_model_structure/iot/sensor.py
@@ -1,73 +1,76 @@
"""
Sensor module
SPDX - License - Identifier: LGPL - 3.0 - or -later
-Copyright © 2022 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
+Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
-import uuid
-from city_model_structure.iot.sensor_measure import SensorMeasure
-from city_model_structure.iot.sensor_type import SensorType
+from helpers.location import Location
class Sensor:
"""
Sensor abstract class
"""
- def __init__(self, sensor_id=None, model=None, sensor_type=None, indoor=False, board=None):
- self._id = sensor_id
- self._model = model
- self._type = sensor_type
- self._indoor = indoor
- self._board = board
- self._measures = []
+ def __init__(self):
+ self._name = None
+ self._type = None
+ self._units = None
+ self._location = None
@property
- def id(self):
+ def name(self):
"""
- Get the sensor id a random uuid will be assigned if no ID was provided to the constructor
- :return: Id
+ Get sensor name
+ :return: str
"""
- if self._id is None:
- self._id = uuid.uuid4()
- return self._id
+ if self._name is None:
+ raise ValueError('Undefined sensor name')
+ return self._name
+
+ @name.setter
+ def name(self, value):
+ """
+ Set sensor name
+ :param value: str
+ """
+ if value is not None:
+ self._name = str(value)
@property
- def type(self) -> SensorType:
+ def type(self):
"""
Get sensor type
- :return: SensorTypeEnum or Error
+ :return: str
"""
- if self._type is None:
- raise ValueError('Unknown sensor type')
return self._type
@property
- def model(self):
+ def units(self):
"""
- Get sensor model is any
- :return: str or None
+ Get sensor units
+ :return: str
"""
- return self._model
+ return self._units
@property
- def board(self):
+ def location(self) -> Location:
"""
- Get sensor board if any
- :return: str or None
+ Get sensor location
+ :return: Location
"""
- return self._model
+ return self._location
+
+ @location.setter
+ def location(self, value):
+ """
+ Set sensor location
+ :param value: Location
+ """
+ self._location = value
@property
- def indoor(self):
- """
- Get is the sensor it's located indoor or outdoor
- :return: boolean
- """
- return self._indoor
-
- @property
- def measures(self) -> [SensorMeasure]:
+ def measures(self):
"""
Raises not implemented error
"""
- return self._measures
+ raise NotImplementedError
diff --git a/city_model_structure/iot/sensor_measure.py b/city_model_structure/iot/sensor_measure.py
deleted file mode 100644
index afdd40db..00000000
--- a/city_model_structure/iot/sensor_measure.py
+++ /dev/null
@@ -1,40 +0,0 @@
-"""
-Sensor measure module
-SPDX - License - Identifier: LGPL - 3.0 - or -later
-Copyright © 2022 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
-"""
-
-class SensorMeasure:
- def __init__(self, latitude, longitude, utc_timestamp, value):
- self._latitude = latitude
- self._longitude = longitude
- self._utc_timestamp = utc_timestamp
- self._value = value
-
- @property
- def latitude(self):
- """
- Get measure latitude
- """
- return self._latitude
-
- @property
- def longitude(self):
- """
- Get measure longitude
- """
- return self._longitude
-
- @property
- def utc_timestamp(self):
- """
- Get measure timestamp in utc
- """
- return self._utc_timestamp
-
- @property
- def value(self):
- """
- Get sensor measure value
- """
- return self._value
\ No newline at end of file
diff --git a/city_model_structure/iot/sensor_type.py b/city_model_structure/iot/sensor_type.py
deleted file mode 100644
index 414b6f60..00000000
--- a/city_model_structure/iot/sensor_type.py
+++ /dev/null
@@ -1,20 +0,0 @@
-"""
-Sensor type module
-SPDX - License - Identifier: LGPL - 3.0 - or -later
-Copyright © 2022 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
-"""
-
-from enum import Enum
-
-class SensorType(Enum):
- HUMIDITY = 0
- TEMPERATURE = 1
- CO2 = 2
- NOISE = 3
- PRESSURE = 4
- DIRECT_RADIATION = 5
- DIFFUSE_RADIATION = 6
- GLOBAL_RADIATION = 7
- AIR_QUALITY = 8
- GAS_FLOW = 9
- ENERGY = 10
diff --git a/city_model_structure/iot/station.py b/city_model_structure/iot/station.py
deleted file mode 100644
index 8a15e62d..00000000
--- a/city_model_structure/iot/station.py
+++ /dev/null
@@ -1,41 +0,0 @@
-"""
-Station
-SPDX - License - Identifier: LGPL - 3.0 - or -later
-Copyright © 2022 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
-"""
-import uuid
-
-from city_model_structure.iot.sensor import Sensor
-
-
-class Station:
- def __init__(self, station_id=None, _mobile=False):
- self._id = station_id
- self._mobile = _mobile
- self._sensors = []
-
- @property
- def id(self):
- """
- Get the station id a random uuid will be assigned if no ID was provided to the constructor
- :return: Id
- """
- if self._id is None:
- self._id = uuid.uuid4()
- return self._id
-
- @property
- def _mobile(self):
- """
- Get if the station is mobile or not
- :return: bool
- """
- return self._mobile
-
- @property
- def sensors(self) -> [Sensor]:
- """
- Get the sensors belonging to the station
- :return: [Sensor]
- """
- return self._sensors
diff --git a/city_model_structure/lca_material.py b/city_model_structure/lca_material.py
deleted file mode 100644
index 6d73384b..00000000
--- a/city_model_structure/lca_material.py
+++ /dev/null
@@ -1,242 +0,0 @@
-"""
-Material module
-SPDX - License - Identifier: LGPL - 3.0 - or -later
-Copyright © 2020 Project Author Atiya atiya.atiya@mail.concordia.ca
-Contributor Mohammad Reza mohammad.seyedabadi@mail.concordia.ca
-"""
-
-from typing import Union
-
-class LcaMaterial:
- def __init__(self):
- self._id = None
- self._type = None
- self._name = None
- self._density = None
- self._density_unit = None
- self._embodied_carbon = None
- self._embodied_carbon_unit = None
- self._recycling_ratio = None
- self._company_recycling_ratio = None
- self._onsite_recycling_ratio = None
- self._landfilling_ratio = None
- self._cost = None
- self._cost_unit = None
-
- @property
- def id(self):
- """
- Get material id
- :return: int
- """
- return self._id
-
- @id.setter
- def id(self, value):
- """
- Set material id
- :param value: int
- """
- self._id = int(value)
-
- @property
- def type(self):
- """
- Get material type
- :return: str
- """
- return self._type
-
- @type.setter
- def type(self, value):
- """
- Set material type
- :param value: string
- """
- self._type = str(value)
-
- @property
- def name(self):
- """
- Get material name
- :return: str
- """
- return self._name
-
- @name.setter
- def name(self, value):
- """
- Set material name
- :param value: string
- """
- self._name = str(value)
-
- @property
- def density(self) -> Union[None, float]:
- """
- Get material density in kg/m3
- :return: None or float
- """
- return self._density
-
- @density.setter
- def density(self, value):
- """
- Set material density
- :param value: float
- """
- if value is not None:
- self._density = float(value)
-
- @property
- def density_unit(self) -> Union[None, str]:
- """
- Get material density unit
- :return: None or string
- """
- return self._density_unit
-
- @density_unit.setter
- def density_unit(self, value):
- """
- Set material density unit
- :param value: string
- """
- if value is not None:
- self._density_unit = str(value)
-
- @property
- def embodied_carbon(self) -> Union[None, float]:
- """
- Get material embodied carbon
- :return: None or float
- """
- return self._embodied_carbon
-
- @embodied_carbon.setter
- def embodied_carbon(self, value):
- """
- Set material embodied carbon
- :param value: float
- """
- if value is not None:
- self._embodied_carbon = float(value)
-
- @property
- def embodied_carbon_unit(self) -> Union[None, str]:
- """
- Get material embodied carbon unit
- :return: None or string
- """
- return self._embodied_carbon
-
- @embodied_carbon_unit.setter
- def embodied_carbon_unit(self, value):
- """
- Set material embodied carbon unit
- :param value: string
- """
- if value is not None:
- self._embodied_carbon_unit = str(value)
-
- @property
- def recycling_ratio(self) -> Union[None, float]:
- """
- Get material recycling ratio
- :return: None or float
- """
- return self._recycling_ratio
-
- @recycling_ratio.setter
- def recycling_ratio(self, value):
- """
- Set material recycling ratio
- :param value: float
- """
- if value is not None:
- self._recycling_ratio = float(value)
-
- @property
- def onsite_recycling_ratio(self) -> Union[None, float]:
- """
- Get material onsite recycling ratio
- :return: None or float
- """
- return self._onsite_recycling_ratio
-
- @onsite_recycling_ratio.setter
- def onsite_recycling_ratio(self, value):
- """
- Set material onsite recycling ratio
- :param value: float
- """
- if value is not None:
- self._onsite_recycling_ratio = float(value)
-
- @property
- def company_recycling_ratio(self) -> Union[None, float]:
- """
- Get material company recycling ratio
- :return: None or float
- """
- return self._company_recycling_ratio
-
- @company_recycling_ratio.setter
- def company_recycling_ratio(self, value):
- """
- Set material company recycling ratio
- :param value: float
- """
- if value is not None:
- self._company_recycling_ratio = float(value)
-
- @property
- def landfilling_ratio(self) -> Union[None, float]:
- """
- Get material landfilling ratio
- :return: None or float
- """
- return self._landfilling_ratio
-
- @landfilling_ratio.setter
- def landfilling_ratio(self, value):
- """
- Set material landfilling ratio
- :param value: float
- """
- if value is not None:
- self._landfilling_ratio = float(value)
-
- @property
- def cost(self) -> Union[None, float]:
- """
- Get material cost
- :return: None or float
- """
- return self._cost
-
- @cost.setter
- def cost(self, value):
- """
- Set material cost
- :param value: float
- """
- if value is not None:
- self._cost = float(value)
-
- @property
- def cost_unit(self) -> Union[None, str]:
- """
- Get material cost unit
- :return: None or string
- """
- return self._cost_unit
-
- @cost_unit.setter
- def cost_unit(self, value):
- """
- Set material cost unit
- :param value: string
- """
- if value is not None:
- self._cost_unit = float(value)
\ No newline at end of file
diff --git a/config/configuration.ini b/config/configuration.ini
index b2ddfb04..399649f1 100644
--- a/config/configuration.ini
+++ b/config/configuration.ini
@@ -11,3 +11,5 @@ comnet_occupancy_sensible_radiant = 0.1
comnet_plugs_latent = 0
comnet_plugs_convective = 0.75
comnet_plugs_radiant = 0.25
+convective_heat_transfer_coefficient_interior = 3.5
+convective_heat_transfer_coefficient_exterior = 20
diff --git a/data/construction/ca_constructions_reduced.xml b/data/construction/ca_constructions_reduced.xml
index 38a217d9..42bf460e 100644
--- a/data/construction/ca_constructions_reduced.xml
+++ b/data/construction/ca_constructions_reduced.xml
@@ -105,84 +105,52 @@
#wall below grade
0.512
- 0
- 0
0.512
- 0
- 0
0.67
- 0
- 0
0.848
- 0
- 0
1.048
- 0
- 0
1.154
- 0
- 0
1.243
- 0
- 0
1.425
- 0
- 0
#slab on grade
0.512
- 0
- 0
0.67
- 0
- 0
0.67
- 0
- 0
0.848
- 0
- 0
0.848
- 0
- 0
1.05
- 0
- 0
1.154
- 0
- 0
1.154
- 0
- 0
diff --git a/data/construction/us_constructions.xml b/data/construction/us_constructions.xml
index 2d4a27f8..b7a2ab51 100644
--- a/data/construction/us_constructions.xml
+++ b/data/construction/us_constructions.xml
@@ -39,7 +39,7 @@
-
+
1.311
2240
836.8
@@ -47,14 +47,14 @@
0.7
0.7
-
+
true
0.21648
0.9
0.7
0.8
-
+
0.045
265
836.8
@@ -62,7 +62,7 @@
0.7
0.7
-
+
0.6918
1858
837
@@ -70,7 +70,7 @@
0.92
0.92
-
+
1.7296
2243
837
@@ -78,7 +78,7 @@
0.65
0.65
-
+
0.0432
91
837
@@ -86,7 +86,7 @@
0.5
0.5
-
+
0.16
784.9
830
@@ -94,7 +94,7 @@
0.92
0.92
-
+
0.11
544.62
1210
@@ -102,7 +102,7 @@
0.78
0.78
-
+
0.115
513
1255
@@ -110,7 +110,7 @@
0.78
0.78
-
+
0.1211
593
2510
@@ -118,7 +118,7 @@
0.78
0.78
-
+
0.049
265
836.8
@@ -126,14 +126,14 @@
0.7
0.7
-
+
true
0.36256
0.9
0.7
0.7
-
+
45.006
7680
418.4
@@ -141,7 +141,7 @@
0.7
0.3
-
+
0.16
1121.29
1460
@@ -149,28 +149,28 @@
0.7
0.7
-
+
true
0.21648
0.9
0.7
0.8
-
+
true
0.21648
0.9
0.7
0.8
-
+
true
0.21648
0.9
0.7
0.8
-
+
0.045
265
836.8
@@ -178,7 +178,7 @@
0.7
0.7
-
+
44.96
7688.86
410
diff --git a/data/greenery/ecore_greenery_catalog.xml b/data/greenery/ecore_greenery_catalog.xml
deleted file mode 100644
index 41b7801c..00000000
--- a/data/greenery/ecore_greenery_catalog.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/data/life_cycle_assessment/lca_data.xml b/data/life_cycle_assessment/lca_data.xml
index 6e231b60..29bb356d 100644
--- a/data/life_cycle_assessment/lca_data.xml
+++ b/data/life_cycle_assessment/lca_data.xml
@@ -1,394 +1,5 @@
-
-
- 1.8
- 560
- 0.8
- 0.3
- 0.7
- 0.2
- ....
-
-
- 1.2
- 310
- 0.8
- 0.3
- 0.7
- 0.2
- ....
-
-
- 2
- 3080
- 0.8
- 0.3
- 0.7
- 0.2
- ....
-
-
- 1.4
- 300
- 0.8
- 0.3
- 0.7
- 0.2
- ....
-
-
- 1.6
- 900
- 0.8
- 0
- 1
- 0.2
- ....
-
-
- 1.6
- 2340
- 0.8
- 0
- 1
- 0.2
- ....
-
-
- 1.6
- 1570
- 0.8
- 0
- 1
- 0.2
- ....
-
-
- 1.4
- 1840
- 0.8
- 0
- 1
- 0.2
- ....
-
-
- 1.3
- 410
- 0.8
- 0
- 1
- 0.2
- ....
-
-
- 2.3
- 160
- 0.8
- 0
- 1
- 0.2
- ....
-
-
- 2.3
- 170
- 0.8
- 0
- 1
- 0.2
- ....
-
-
- 2.3
- 230
- 0.8
- 0
- 1
- 0.2
- ....
-
-
- 2.4
- 240
- 0.8
- 0
- 1
- 0.2
- ....
-
-
- 2.4
- 280
- 0.8
- 0
- 1
- 0.2
- ....
-
-
- 2.3
- 170
- 0.8
- 0
- 1
- 0.2
- ....
-
-
- 1.2
- 440
- 0.8
- 0
- 1
- 0.2
- ....
-
-
- 2.58
- 2660
- 0.95
- 0
- 1
- 0.05
- ....
-
-
- 2.58
- 5260
- 0.95
- 0
- 1
- 0.05
- ....
-
-
- 0.06
- 1760
- 0.9
- 0
- 1
- 0.1
- ....
-
-
- 0.122
- 3080
- 0.9
- 0
- 1
- 0.1
- ....
-
-
- 0.028
- 3180
- 0.9
- 0
- 1
- 0.1
- ....
-
-
- 0.024
- 5140
- 0.9
- 0
- 1
- 0.1
- ....
-
-
- 0.1
- 6040
- 0.9
- 0
- 1
- 0.1
- ....
-
-
- 0.3
- 5380
- 0.9
- 0
- 1
- 0.1
- ....
-
-
- 0.032
- 2150
- 0.9
- 0
- 1
- 0.1
- ....
-
-
- 0.9
- 3420
- 0.6
- 0
- 1
- 0.4
- ....
-
-
- 0.7
- 1430
- 0.6
- 0
- 1
- 0.4
- ....
-
-
- 0.65
- 2780
- 0.6
- 0
- 1
- 0.4
- ....
-
-
- 0.72
- 2190
- 0.6
- 0
- 1
- 0.4
- ....
-
-
- 1.43
- 1070
- 0
- 0
- 0
- 1
- ....
-
-
- 1.43
- 240
- 0
- 0
- 0
- 1
- ....
-
-
- 1.43
- 430
- 0
- 0
- 0
- 1
- ....
-
-
- 1.43
- 340
- 0
- 0
- 0
- 1
- ....
-
-
- 1.2
- 440
- 0.8
- 0
- 1
- 0.2
- ....
-
-
- 2.1
- 1410
- 0.8
- 0
- 1
- 0.2
- ....
-
-
- 1.43
- 250
- 0
- 0
- 0
- 1
- ....
-
-
- 1.44
- 1480
- 0.8
- 0
- 1
- 0.2
- ....
-
-
- 1.44
- 2220
- 0.8
- 0
- 1
- 0.2
- ....
-
-
- 1.27
- 3960
- 0.8
- 0
- 1
- 0.2
- ....
-
-
- 1.15
- 760
- 0.8
- 0
- 1
- 0.2
- ....
-
-
- 8
- 3160
- 0.98
- 0
- 1
- 0.02
- ....
-
-
- 2.7
- 5370
- 0.98
- 0
- 1
- 0.02
- ....
-
-
- 7.85
- 3910
- 0.98
- 0
- 1
- 0.02
- ....
-
-
0.32
@@ -556,4 +167,393 @@
1.00000
+
+
+ 1.8
+ 560
+ 0.8
+ 0.3
+ 0.7
+ 0.2
+ ....
+
+
+ 1.2
+ 310
+ 0.8
+ 0.3
+ 0.7
+ 0.2
+ ....
+
+
+ 2
+ 3080
+ 0.8
+ 0.3
+ 0.7
+ 0.2
+ ....
+
+
+ 1.4
+ 300
+ 0.8
+ 0.3
+ 0.7
+ 0.2
+ ....
+
+
+ 1.6
+ 900
+ 0.8
+ 0
+ 1
+ 0.2
+ ....
+
+
+ 1.6
+ 2340
+ 0.8
+ 0
+ 1
+ 0.2
+ ....
+
+
+ 1.6
+ 1570
+ 0.8
+ 0
+ 1
+ 0.2
+ ....
+
+
+ 1.4
+ 1840
+ 0.8
+ 0
+ 1
+ 0.2
+ ....
+
+
+ 1.3
+ 410
+ 0.8
+ 0
+ 1
+ 0.2
+ ....
+
+
+ 2.3
+ 160
+ 0.8
+ 0
+ 1
+ 0.2
+ ....
+
+
+ 2.3
+ 170
+ 0.8
+ 0
+ 1
+ 0.2
+ ....
+
+
+ 2.3
+ 230
+ 0.8
+ 0
+ 1
+ 0.2
+ ....
+
+
+ 2.4
+ 240
+ 0.8
+ 0
+ 1
+ 0.2
+ ....
+
+
+ 2.4
+ 280
+ 0.8
+ 0
+ 1
+ 0.2
+ ....
+
+
+ 2.3
+ 170
+ 0.8
+ 0
+ 1
+ 0.2
+ ....
+
+
+ 1.2
+ 440
+ 0.8
+ 0
+ 1
+ 0.2
+ ....
+
+
+ 2.58
+ 2660
+ 0.95
+ 0
+ 1
+ 0.05
+ ....
+
+
+ 2.58
+ 5260
+ 0.95
+ 0
+ 1
+ 0.05
+ ....
+
+
+ 0.06
+ 1760
+ 0.9
+ 0
+ 1
+ 0.1
+ ....
+
+
+ 0.122
+ 3080
+ 0.9
+ 0
+ 1
+ 0.1
+ ....
+
+
+ 0.028
+ 3180
+ 0.9
+ 0
+ 1
+ 0.1
+ ....
+
+
+ 0.024
+ 5140
+ 0.9
+ 0
+ 1
+ 0.1
+ ....
+
+
+ 0.1
+ 6040
+ 0.9
+ 0
+ 1
+ 0.1
+ ....
+
+
+ 0.3
+ 5380
+ 0.9
+ 0
+ 1
+ 0.1
+ ....
+
+
+ 0.032
+ 2150
+ 0.9
+ 0
+ 1
+ 0.1
+ ....
+
+
+ 0.9
+ 3420
+ 0.6
+ 0
+ 1
+ 0.4
+ ....
+
+
+ 0.7
+ 1430
+ 0.6
+ 0
+ 1
+ 0.4
+ ....
+
+
+ 0.65
+ 2780
+ 0.6
+ 0
+ 1
+ 0.4
+ ....
+
+
+ 0.72
+ 2190
+ 0.6
+ 0
+ 1
+ 0.4
+ ....
+
+
+ 1.43
+ 1070
+ 0
+ 0
+ 0
+ 1
+ ....
+
+
+ 1.43
+ 240
+ 0
+ 0
+ 0
+ 1
+ ....
+
+
+ 1.43
+ 430
+ 0
+ 0
+ 0
+ 1
+ ....
+
+
+ 1.43
+ 340
+ 0
+ 0
+ 0
+ 1
+ ....
+
+
+ 1.2
+ 440
+ 0.8
+ 0
+ 1
+ 0.2
+ ....
+
+
+ 2.1
+ 1410
+ 0.8
+ 0
+ 1
+ 0.2
+ ....
+
+
+ 1.43
+ 250
+ 0
+ 0
+ 0
+ 1
+ ....
+
+
+ 1.44
+ 1480
+ 0.8
+ 0
+ 1
+ 0.2
+ ....
+
+
+ 1.44
+ 2220
+ 0.8
+ 0
+ 1
+ 0.2
+ ....
+
+
+ 1.27
+ 3960
+ 0.8
+ 0
+ 1
+ 0.2
+ ....
+
+
+ 1.15
+ 760
+ 0.8
+ 0
+ 1
+ 0.2
+ ....
+
+
+ 8
+ 3160
+ 0.98
+ 0
+ 1
+ 0.02
+ ....
+
+
+ 2.7
+ 5370
+ 0.98
+ 0
+ 1
+ 0.02
+ ....
+
+
+ 7.85
+ 3910
+ 0.98
+ 0
+ 1
+ 0.02
+ ....
+
+
\ No newline at end of file
diff --git a/exports/formats/energy_ade.py b/exports/formats/energy_ade.py
index 60c10af2..cf335397 100644
--- a/exports/formats/energy_ade.py
+++ b/exports/formats/energy_ade.py
@@ -329,7 +329,7 @@ class EnergyAde:
},
'energy:area': {
'@uom': 'm2',
- '#text': f'{thermal_boundary.area}'
+ '#text': f'{thermal_boundary.opaque_area}'
},
'energy:surfaceGeometry': {
'gml:MultiSurface': {
diff --git a/helpers/configuration_helper.py b/helpers/configuration_helper.py
index 85677ceb..51d9a3d8 100644
--- a/helpers/configuration_helper.py
+++ b/helpers/configuration_helper.py
@@ -105,3 +105,19 @@ class ConfigurationHelper:
:return: 0.1
"""
return self._config.getfloat('buildings', 'comnet_occupancy_sensible_radiant').real
+
+ @property
+ def convective_heat_transfer_coefficient_interior(self) -> float:
+ """
+ Get configured convective heat transfer coefficient for surfaces inside the building
+ :return: 3.5
+ """
+ return self._config.getfloat('buildings', 'convective_heat_transfer_coefficient_interior').real
+
+ @property
+ def convective_heat_transfer_coefficient_exterior(self) -> float:
+ """
+ Get configured convective heat transfer coefficient for surfaces outside the building
+ :return: 20
+ """
+ return self._config.getfloat('buildings', 'convective_heat_transfer_coefficient_exterior').real
diff --git a/helpers/constants.py b/helpers/constants.py
index 4ce228d4..b55cf955 100644
--- a/helpers/constants.py
+++ b/helpers/constants.py
@@ -85,11 +85,22 @@ EDUCATION = 'education'
LIGHTING = 'Lights'
OCCUPANCY = 'Occupancy'
-RECEPTACLE = 'Receptacle'
+APPLIANCES = 'Appliances'
HVAC_AVAILABILITY = 'HVAC Avail'
INFILTRATION = 'Infiltration'
COOLING_SET_POINT = 'ClgSetPt'
HEATING_SET_POINT = 'HtgSetPt'
-# todo: are any of these two the receptacle concept??
EQUIPMENT = 'Equipment'
ACTIVITY = 'Activity'
+
+# Geometry
+EPSILON = 0.0000001
+
+# HVAC types
+ONLY_HEATING = 'Heating'
+ONLY_COOLING = 'Colling'
+ONLY_VENTILATION = 'Ventilation'
+HEATING_AND_VENTILATION = 'Heating and ventilation'
+COOLING_AND_VENTILATION = 'Cooling and ventilation'
+HEATING_AND_COLLING = 'Heating and cooling'
+FULL_HVAC = 'Heating and cooling and ventilation'
diff --git a/helpers/monthly_to_hourly_demand.py b/helpers/monthly_to_hourly_demand.py
index 4251507f..029a42dd 100644
--- a/helpers/monthly_to_hourly_demand.py
+++ b/helpers/monthly_to_hourly_demand.py
@@ -5,7 +5,7 @@ Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.mons
"""
import calendar as cal
import pandas as pd
-from city_model_structure.building_demand.occupants import Occupants
+from city_model_structure.building_demand.occupant import Occupant
import helpers.constants as cte
@@ -34,7 +34,7 @@ class MonthlyToHourlyDemand:
# todo: if these are data frames, then they should be called as (Occupancy should be in low case):
# usage_zone.schedules.Occupancy
# self._conditioning_seasons.heating
- occupancy = Occupants().get_complete_year_schedule(usage_zone.schedules['Occupancy'])
+ occupancy = Occupant().get_complete_year_schedule(usage_zone.schedules['Occupancy'])
heating_schedule = self._conditioning_seasons['heating']
hourly_heating = []
@@ -92,7 +92,7 @@ class MonthlyToHourlyDemand:
for usage_zone in self._building.usage_zones:
temp_set = float(usage_zone.cooling_setpoint)
temp_back = 100
- occupancy = Occupants().get_complete_year_schedule(usage_zone.schedules['Occupancy'])
+ occupancy = Occupant().get_complete_year_schedule(usage_zone.schedules['Occupancy'])
cooling_schedule = self._conditioning_seasons['cooling']
hourly_cooling = []
diff --git a/imports/construction/ca_physics_parameters.py b/imports/construction/ca_physics_parameters.py
index 25f3e1de..d04c81ea 100644
--- a/imports/construction/ca_physics_parameters.py
+++ b/imports/construction/ca_physics_parameters.py
@@ -33,7 +33,14 @@ class CaPhysicsParameters(NrelPhysicsInterface):
f'and building year of construction: {building.year_of_construction}\n')
continue
- self._create_storeys(building, archetype)
+ # if building has no thermal zones defined from geometry, one thermal zone per storey is assigned
+ if building.thermal_zones is None:
+ self._create_storeys(building, archetype)
+ thermal_zones = []
+ for storey in building.storeys:
+ thermal_zones.append(storey.thermal_zone)
+ building.thermal_zones = thermal_zones
+
self._assign_values(building, archetype)
def _search_archetype(self, function, year_of_construction):
@@ -60,11 +67,15 @@ class CaPhysicsParameters(NrelPhysicsInterface):
thermal_boundary.u_value = thermal_boundary_archetype.overall_u_value
thermal_boundary.outside_solar_absorptance = thermal_boundary_archetype.outside_solar_absorptance
thermal_boundary.construction_name = thermal_boundary_archetype.construction_name
- thermal_boundary.window_ratio = thermal_boundary_archetype.window_ratio
+ try:
+ thermal_boundary.window_ratio = thermal_boundary_archetype.window_ratio
+ except ValueError:
+ # This is the normal operation way when the windows are defined in the geometry
+ continue
if thermal_boundary.thermal_openings is not None:
for thermal_opening in thermal_boundary.thermal_openings:
- if thermal_boundary_archetype.thermal_opening is not None:
- thermal_opening_archetype = thermal_boundary_archetype.thermal_opening
+ if thermal_boundary_archetype.thermal_opening_archetype is not None:
+ thermal_opening_archetype = thermal_boundary_archetype.thermal_opening_archetype
thermal_opening.frame_ratio = thermal_opening_archetype.frame_ratio
thermal_opening.g_value = thermal_opening_archetype.g_value
thermal_opening.overall_u_value = thermal_opening_archetype.overall_u_value
diff --git a/imports/construction/data_classes/nrel_building_achetype.py b/imports/construction/data_classes/building_achetype.py
similarity index 89%
rename from imports/construction/data_classes/nrel_building_achetype.py
rename to imports/construction/data_classes/building_achetype.py
index 3956fb98..fb35bad6 100644
--- a/imports/construction/data_classes/nrel_building_achetype.py
+++ b/imports/construction/data_classes/building_achetype.py
@@ -1,15 +1,15 @@
"""
-NrelBuildingArchetype stores construction information by building archetypes
+BuildingArchetype stores construction information by building archetypes
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from typing import List
-from imports.construction.data_classes.nrel_thermal_boundary_archetype import NrelThermalBoundaryArchetype
+from imports.construction.data_classes.thermal_boundary_archetype import ThermalBoundaryArchetype
-class NrelBuildingArchetype:
+class BuildingArchetype:
"""
- NrelBuildingArchetype class
+ BuildingArchetype class
"""
def __init__(self, archetype_keys, average_storey_height, storeys_above_ground, effective_thermal_capacity,
additional_thermal_bridge_u_value, indirectly_heated_area_ratio, infiltration_rate_system_off,
@@ -89,7 +89,7 @@ class NrelBuildingArchetype:
return self._infiltration_rate_system_on
@property
- def thermal_boundary_archetypes(self) -> List[NrelThermalBoundaryArchetype]:
+ def thermal_boundary_archetypes(self) -> List[ThermalBoundaryArchetype]:
"""
Get thermal boundary archetypes associated to the building archetype
:return: list of boundary archetypes
diff --git a/imports/construction/data_classes/nrel_layer_archetype.py b/imports/construction/data_classes/layer_archetype.py
similarity index 67%
rename from imports/construction/data_classes/nrel_layer_archetype.py
rename to imports/construction/data_classes/layer_archetype.py
index 46e0ea64..adad7439 100644
--- a/imports/construction/data_classes/nrel_layer_archetype.py
+++ b/imports/construction/data_classes/layer_archetype.py
@@ -1,17 +1,16 @@
"""
-NrelLayerArchetype stores layer and materials information, complementing the NrelBuildingArchetype class
+LayerArchetype stores layer and materials information, complementing the BuildingArchetype class
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
-class NrelLayerArchetype:
+class LayerArchetype:
"""
- NrelLayerArchetype class
+ LayerArchetype class
"""
def __init__(self, name, solar_absorptance, thermal_absorptance, visible_absorptance, thickness=None,
- conductivity=None, specific_heat=None, density=None, no_mass=False, thermal_resistance=None,
- lca_id=None):
+ conductivity=None, specific_heat=None, density=None, no_mass=False, thermal_resistance=None):
self._thickness = thickness
self._conductivity = conductivity
self._specific_heat = specific_heat
@@ -22,12 +21,11 @@ class NrelLayerArchetype:
self._no_mass = no_mass
self._name = name
self._thermal_resistance = thermal_resistance
- self._lca_id = lca_id
@property
def thickness(self):
"""
- Get nrel layer archetype thickness in meters
+ Get thickness in meters
:return: float
"""
return self._thickness
@@ -35,7 +33,7 @@ class NrelLayerArchetype:
@property
def conductivity(self):
"""
- Get nrel layer archetype conductivity in W/mK
+ Get conductivity in W/mK
:return: float
"""
return self._conductivity
@@ -43,7 +41,7 @@ class NrelLayerArchetype:
@property
def specific_heat(self):
"""
- Get nrel layer archetype conductivity in J/kgK
+ Get specific heat in J/kgK
:return: float
"""
return self._specific_heat
@@ -51,7 +49,7 @@ class NrelLayerArchetype:
@property
def density(self):
"""
- Get nrel layer archetype density in kg/m3
+ Get density in kg/m3
:return: float
"""
return self._density
@@ -59,7 +57,7 @@ class NrelLayerArchetype:
@property
def solar_absorptance(self):
"""
- Get nrel layer archetype solar absorptance
+ Get solar absorptance
:return: float
"""
return self._solar_absorptance
@@ -67,7 +65,7 @@ class NrelLayerArchetype:
@property
def thermal_absorptance(self):
"""
- Get nrel layer archetype thermal absorptance
+ Get thermal absorptance
:return: float
"""
return self._thermal_absorptance
@@ -75,7 +73,7 @@ class NrelLayerArchetype:
@property
def visible_absorptance(self):
"""
- Get nrel layer archetype visible absorptance
+ Get visible absorptance
:return: float
"""
return self._visible_absorptance
@@ -83,7 +81,7 @@ class NrelLayerArchetype:
@property
def no_mass(self) -> bool:
"""
- Get nrel layer archetype no mass flag
+ Get no mass flag
:return: Boolean
"""
return self._no_mass
@@ -91,7 +89,7 @@ class NrelLayerArchetype:
@property
def name(self):
"""
- Get nrel layer archetype name
+ Get name
:return: str
"""
return self._name
@@ -99,15 +97,7 @@ class NrelLayerArchetype:
@property
def thermal_resistance(self):
"""
- Get nrel layer archetype thermal resistance in m2K/W
+ Get thermal resistance in m2K/W
:return: float
"""
return self._thermal_resistance
-
- @property
- def lca_id(self):
- """
- Get nrel lca_id equivalent for the material
- :return: int
- """
- return self._lca_id
diff --git a/imports/construction/data_classes/nrel_thermal_boundary_archetype.py b/imports/construction/data_classes/nrel_thermal_boundary_archetype.py
deleted file mode 100644
index 78f78cb6..00000000
--- a/imports/construction/data_classes/nrel_thermal_boundary_archetype.py
+++ /dev/null
@@ -1,99 +0,0 @@
-"""
-NrelThermalBoundaryArchetype stores thermal boundaries information, complementing the NrelBuildingArchetype class
-SPDX - License - Identifier: LGPL - 3.0 - or -later
-Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
-"""
-from typing import List
-
-from imports.construction.data_classes.nrel_layer_archetype import NrelLayerArchetype
-from imports.construction.data_classes.nrel_thermal_opening_archetype import NrelThermalOpeningArchetype
-
-
-class NrelThermalBoundaryArchetype:
- """
- NrelThermalBoundaryArchetype class
- """
- def __init__(self, boundary_type, window_ratio, construction_name, layers, thermal_opening,
- outside_solar_absorptance=None, outside_thermal_absorptance=None, outside_visible_absorptance=None,
- overall_u_value=None):
- self._boundary_type = boundary_type
- self._outside_solar_absorptance = outside_solar_absorptance
- self._outside_thermal_absorptance = outside_thermal_absorptance
- self._outside_visible_absorptance = outside_visible_absorptance
- self._window_ratio = window_ratio
- self._construction_name = construction_name
- self._overall_u_value = overall_u_value
- self._layers = layers
- self._thermal_opening = thermal_opening
-
- @property
- def boundary_type(self):
- """
- Get nrel thermal boundary archetype type
- :return: str
- """
- return self._boundary_type
-
- @property
- def outside_solar_absorptance(self):
- """
- Get nrel thermal boundary archetype outside solar absorptance
- :return: float
- """
- return self._outside_solar_absorptance
-
- @property
- def outside_thermal_absorptance(self):
- """
- Get nrel thermal boundary archetype outside thermal absorptance
- :return: float
- """
- return self._outside_thermal_absorptance
-
- @property
- def outside_visible_absorptance(self):
- """
- Get nrel thermal boundary archetype outside visible absorptance
- :return: float
- """
- return self._outside_visible_absorptance
-
- @property
- def window_ratio(self):
- """
- Get nrel thermal boundary archetype window ratio
- :return: float
- """
- return self._window_ratio
-
- @property
- def construction_name(self):
- """
- Get nrel thermal boundary archetype construction name
- :return: str
- """
- return self._construction_name
-
- @property
- def layers(self) -> List[NrelLayerArchetype]:
- """
- Get nrel thermal boundary archetype layers
- :return: [NrelLayerArchetype]
- """
- return self._layers
-
- @property
- def thermal_opening(self) -> NrelThermalOpeningArchetype:
- """
- Get nrel thermal boundary archetype
- :return: NrelThermalOpeningArchetype
- """
- return self._thermal_opening
-
- @property
- def overall_u_value(self):
- """
- Get nrel thermal boundary archetype overall U-value in W/m2K
- :return: float
- """
- return self._overall_u_value
diff --git a/imports/construction/data_classes/thermal_boundary_archetype.py b/imports/construction/data_classes/thermal_boundary_archetype.py
new file mode 100644
index 00000000..11aecf44
--- /dev/null
+++ b/imports/construction/data_classes/thermal_boundary_archetype.py
@@ -0,0 +1,136 @@
+"""
+ThermalBoundaryArchetype stores thermal boundaries information, complementing the BuildingArchetype class
+SPDX - License - Identifier: LGPL - 3.0 - or -later
+Copyright © 2022 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
+"""
+from typing import List
+
+from imports.construction.data_classes.layer_archetype import LayerArchetype
+from imports.construction.data_classes.thermal_opening_archetype import ThermalOpeningArchetype
+
+
+class ThermalBoundaryArchetype:
+ """
+ ThermalBoundaryArchetype class
+ """
+ def __init__(self, boundary_type, window_ratio, construction_name, layers, thermal_opening,
+ outside_solar_absorptance=None, outside_thermal_absorptance=None, outside_visible_absorptance=None,
+ overall_u_value=None, shortwave_reflectance=None, inside_emissivity=None, alpha_coefficient=None,
+ radiative_coefficient=None):
+ self._boundary_type = boundary_type
+ self._outside_solar_absorptance = outside_solar_absorptance
+ self._outside_thermal_absorptance = outside_thermal_absorptance
+ self._outside_visible_absorptance = outside_visible_absorptance
+ self._window_ratio = window_ratio
+ self._construction_name = construction_name
+ self._overall_u_value = overall_u_value
+ self._layers = layers
+ self._thermal_opening_archetype = thermal_opening
+ self._shortwave_reflectance = shortwave_reflectance
+ self._inside_emissivity = inside_emissivity
+ self._alpha_coefficient = alpha_coefficient
+ self._radiative_coefficient = radiative_coefficient
+
+ @property
+ def boundary_type(self):
+ """
+ Get type
+ :return: str
+ """
+ return self._boundary_type
+
+ @property
+ def outside_solar_absorptance(self):
+ """
+ Get outside solar absorptance
+ :return: float
+ """
+ return self._outside_solar_absorptance
+
+ @property
+ def outside_thermal_absorptance(self):
+ """
+ Get outside thermal absorptance
+ :return: float
+ """
+ return self._outside_thermal_absorptance
+
+ @property
+ def outside_visible_absorptance(self):
+ """
+ Get outside visible absorptance
+ :return: float
+ """
+ return self._outside_visible_absorptance
+
+ @property
+ def window_ratio(self):
+ """
+ Get window ratio
+ :return: float
+ """
+ return self._window_ratio
+
+ @property
+ def construction_name(self):
+ """
+ Get construction name
+ :return: str
+ """
+ return self._construction_name
+
+ @property
+ def layers(self) -> List[LayerArchetype]:
+ """
+ Get layers
+ :return: [NrelLayerArchetype]
+ """
+ return self._layers
+
+ @property
+ def thermal_opening_archetype(self) -> ThermalOpeningArchetype:
+ """
+ Get thermal opening archetype
+ :return: ThermalOpeningArchetype
+ """
+ return self._thermal_opening_archetype
+
+ @property
+ def overall_u_value(self):
+ """
+ Get overall U-value in W/m2K
+ :return: float
+ """
+ return self._overall_u_value
+
+ @property
+ def shortwave_reflectance(self):
+ """
+ Get shortwave reflectance
+ :return: float
+ """
+ return self._shortwave_reflectance
+
+ @property
+ def inside_emissivity(self):
+ """
+ Get emissivity inside
+ :return: float
+ """
+ return self._inside_emissivity
+
+ @property
+ def alpha_coefficient(self):
+ """
+ Get alpha coefficient
+ :return: float
+ """
+ return self._alpha_coefficient
+
+ @property
+ def radiative_coefficient(self):
+ """
+ Get radiative coefficient
+ :return: float
+ """
+ return self._radiative_coefficient
diff --git a/imports/construction/data_classes/nrel_thermal_opening_archetype.py b/imports/construction/data_classes/thermal_opening_archetype.py
similarity index 56%
rename from imports/construction/data_classes/nrel_thermal_opening_archetype.py
rename to imports/construction/data_classes/thermal_opening_archetype.py
index 9f696116..e1966267 100644
--- a/imports/construction/data_classes/nrel_thermal_opening_archetype.py
+++ b/imports/construction/data_classes/thermal_opening_archetype.py
@@ -1,30 +1,34 @@
"""
-NrelThermalOpeningArchetype stores thermal openings information, complementing the NrelBuildingArchetype class
+ThermalOpeningArchetype stores thermal openings information, complementing the BuildingArchetype class
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
-class NrelThermalOpeningArchetype:
+class ThermalOpeningArchetype:
"""
- NrelThermalOpeningArchetype class
+ ThermalOpeningArchetype class
"""
def __init__(self, conductivity=None, frame_ratio=None, g_value=None, thickness=None,
back_side_solar_transmittance_at_normal_incidence=None,
- front_side_solar_transmittance_at_normal_incidence=None, shgc=None, overall_u_value=None):
+ front_side_solar_transmittance_at_normal_incidence=None, overall_u_value=None,
+ openable_ratio=None, inside_emissivity=None, alpha_coefficient=None, radiative_coefficient=None):
self._conductivity = conductivity
self._frame_ratio = frame_ratio
self._g_value = g_value
self._thickness = thickness
self._back_side_solar_transmittance_at_normal_incidence = back_side_solar_transmittance_at_normal_incidence
self._front_side_solar_transmittance_at_normal_incidence = front_side_solar_transmittance_at_normal_incidence
- self._shgc = shgc
self._overall_u_value = overall_u_value
+ self._openable_ratio = openable_ratio
+ self._inside_emissivity = inside_emissivity
+ self._alpha_coefficient = alpha_coefficient
+ self._radiative_coefficient = radiative_coefficient
@property
def conductivity(self):
"""
- Get nrel thermal opening archetype conductivity in W/mK
+ Get conductivity in W/mK
:return: float
"""
return self._conductivity
@@ -32,7 +36,7 @@ class NrelThermalOpeningArchetype:
@property
def frame_ratio(self):
"""
- Get nrel thermal opening archetype frame ratio
+ Get frame ratio
:return: float
"""
return self._frame_ratio
@@ -40,7 +44,7 @@ class NrelThermalOpeningArchetype:
@property
def g_value(self):
"""
- Get nrel thermal opening archetype g-value
+ Get g-value, also called shgc
:return: float
"""
return self._g_value
@@ -48,7 +52,7 @@ class NrelThermalOpeningArchetype:
@property
def thickness(self):
"""
- Get nrel thermal opening archetype thickness in meters
+ Get thickness in meters
:return: float
"""
return self._thickness
@@ -56,7 +60,7 @@ class NrelThermalOpeningArchetype:
@property
def back_side_solar_transmittance_at_normal_incidence(self):
"""
- Get nrel thermal opening archetype back side solar transmittance at normal incidence
+ Get back side solar transmittance at normal incidence
:return: float
"""
return self._back_side_solar_transmittance_at_normal_incidence
@@ -64,23 +68,47 @@ class NrelThermalOpeningArchetype:
@property
def front_side_solar_transmittance_at_normal_incidence(self):
"""
- Get nrel thermal opening archetype front side solar transmittance at normal incidence
+ Get front side solar transmittance at normal incidence
:return: float
"""
return self._front_side_solar_transmittance_at_normal_incidence
- @property
- def shgc(self):
- """
- Get nrel thermal opening archetype shcg
- :return: float
- """
- return self._shgc
-
@property
def overall_u_value(self):
"""
- Get nrel thermal opening archetype overall U-value in W/m2K
- :param value: float
+ Get overall U-value in W/m2K
+ :return: float
"""
return self._overall_u_value
+
+ @property
+ def openable_ratio(self):
+ """
+ Get openable ratio
+ :return: float
+ """
+ return self._openable_ratio
+
+ @property
+ def inside_emissivity(self):
+ """
+ Get emissivity inside
+ :return: float
+ """
+ return self._inside_emissivity
+
+ @property
+ def alpha_coefficient(self):
+ """
+ Get alpha coefficient
+ :return: float
+ """
+ return self._alpha_coefficient
+
+ @property
+ def radiative_coefficient(self):
+ """
+ Get radiative coefficient
+ :return: float
+ """
+ return self._radiative_coefficient
diff --git a/imports/construction/helpers/storeys_generation.py b/imports/construction/helpers/storeys_generation.py
index ae89cebb..b67a820d 100644
--- a/imports/construction/helpers/storeys_generation.py
+++ b/imports/construction/helpers/storeys_generation.py
@@ -1,7 +1,7 @@
"""
Storeys generation helper
SPDX - License - Identifier: LGPL - 3.0 - or -later
-Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
+Copyright © 2022 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
import sys
import math
@@ -22,6 +22,9 @@ class StoreysGeneration:
self._building = building
self._divide_in_storeys = divide_in_storeys
self._storeys = None
+ self._floor_area = 0
+ for ground in building.grounds:
+ self._floor_area += ground.perimeter_polygon.area
@property
def storeys(self) -> [Storey]:
@@ -34,7 +37,7 @@ class StoreysGeneration:
self._building.storeys_above_ground)
number_of_storeys = 1
if not self._divide_in_storeys or number_of_storeys == 1:
- return [Storey('storey_0', self._building.surfaces, [None, None], self._building.volume)]
+ return [Storey('storey_0', self._building.surfaces, [None, None], self._building.volume, self._floor_area)]
if number_of_storeys == 0:
raise Exception('Number of storeys cannot be 0')
@@ -79,13 +82,13 @@ class StoreysGeneration:
surfaces_child.append(ceiling)
volume = ceiling.area_above_ground * height
total_volume += volume
- storeys.append(Storey(name, surfaces_child, neighbours, volume))
+ storeys.append(Storey(name, surfaces_child, neighbours, volume, self._floor_area))
name = 'storey_' + str(number_of_storeys - 1)
neighbours = ['storey_' + str(number_of_storeys - 2), None]
volume = self._building.volume - total_volume
if volume < 0:
raise Exception('Error in storeys creation, volume of last storey cannot be lower that 0')
- storeys.append(Storey(name, surfaces_child_last_storey, neighbours, volume))
+ storeys.append(Storey(name, surfaces_child_last_storey, neighbours, volume, self._floor_area))
return storeys
@staticmethod
@@ -144,12 +147,12 @@ class StoreysGeneration:
"""
for storey in self._building.storeys:
for thermal_boundary in storey.thermal_boundaries:
- if thermal_boundary.surface.type != cte.INTERIOR_WALL or thermal_boundary.surface.type != cte.INTERIOR_SLAB:
+ if thermal_boundary.type != cte.INTERIOR_WALL or thermal_boundary.type != cte.INTERIOR_SLAB:
# external thermal boundary -> only one thermal zone
thermal_zones = [storey.thermal_zone]
else:
# internal thermal boundary -> two thermal zones
- grad = np.rad2deg(thermal_boundary.surface.inclination)
+ grad = np.rad2deg(thermal_boundary.inclination)
if grad >= 170:
thermal_zones = [storey.thermal_zone, storey.neighbours[0]]
else:
diff --git a/imports/construction/nrel_physics_interface.py b/imports/construction/nrel_physics_interface.py
index f01297b8..9065ca9e 100644
--- a/imports/construction/nrel_physics_interface.py
+++ b/imports/construction/nrel_physics_interface.py
@@ -6,10 +6,10 @@ Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.mons
"""
import xmltodict
-from imports.construction.data_classes.nrel_building_achetype import NrelBuildingArchetype as nba
-from imports.construction.data_classes.nrel_thermal_boundary_archetype import NrelThermalBoundaryArchetype as ntba
-from imports.construction.data_classes.nrel_thermal_opening_archetype import NrelThermalOpeningArchetype as ntoa
-from imports.construction.data_classes.nrel_layer_archetype import NrelLayerArchetype as nla
+from imports.construction.data_classes.building_achetype import BuildingArchetype as nba
+from imports.construction.data_classes.thermal_boundary_archetype import ThermalBoundaryArchetype as ntba
+from imports.construction.data_classes.thermal_opening_archetype import ThermalOpeningArchetype as ntoa
+from imports.construction.data_classes.layer_archetype import LayerArchetype as nla
class NrelPhysicsInterface:
@@ -71,7 +71,6 @@ class NrelPhysicsInterface:
for current_layer in c_lib['layers']['layer']:
material_lib = self._search_construction_type('material', current_layer['material'])
name = material_lib['@name']
- lca_id = material_lib['@lca_id']
solar_absorptance = material_lib['solar_absorptance']['#text']
thermal_absorptance = material_lib['thermal_absorptance']['#text']
visible_absorptance = material_lib['visible_absorptance']['#text']
@@ -82,7 +81,7 @@ class NrelPhysicsInterface:
if units != 'm2 K/W':
raise Exception(f'thermal resistance units = {units}, expected m2 K/W')
layer = nla(name, solar_absorptance, thermal_absorptance, visible_absorptance, no_mass=no_mass,
- thermal_resistance=thermal_resistance, lca_id=lca_id)
+ thermal_resistance=thermal_resistance)
else:
thickness = current_layer['thickness']['#text']
units = current_layer['thickness']['@units']
@@ -101,7 +100,7 @@ class NrelPhysicsInterface:
if units != 'kg/m3':
raise Exception(f'density units = {units}, expected kg/m3')
layer = nla(name, solar_absorptance, thermal_absorptance, visible_absorptance, thickness=thickness,
- conductivity=conductivity, specific_heat=specific_heat, density=density, lca_id=lca_id)
+ conductivity=conductivity, specific_heat=specific_heat, density=density)
layers.append(layer)
thermal_opening = None
@@ -150,10 +149,14 @@ class NrelPhysicsInterface:
units = c_lib['overall_u_value']['@units']
if units != 'W/m2 K':
raise Exception(f'overall U-value units = {units}, expected W/m2 K')
- outside_solar_absorptance = c_lib['outside_solar_absorptance']['#text']
- thermal_boundary_archetype = ntba(construction_type, window_ratio, construction_name, layers,
- thermal_opening, outside_solar_absorptance=outside_solar_absorptance,
- overall_u_value=overall_u_value)
+ if 'outside_solar_absorptance' in c_lib:
+ outside_solar_absorptance = c_lib['outside_solar_absorptance']['#text']
+ thermal_boundary_archetype = ntba(construction_type, window_ratio, construction_name, layers,
+ thermal_opening, outside_solar_absorptance=outside_solar_absorptance,
+ overall_u_value=overall_u_value)
+ else:
+ thermal_boundary_archetype = ntba(construction_type, window_ratio, construction_name, layers,
+ thermal_opening, overall_u_value=overall_u_value)
else:
thermal_boundary_archetype = ntba(construction_type, window_ratio, construction_name, layers,
thermal_opening)
diff --git a/imports/construction/us_physics_parameters.py b/imports/construction/us_physics_parameters.py
index 76d7b0f4..6e19e96d 100644
--- a/imports/construction/us_physics_parameters.py
+++ b/imports/construction/us_physics_parameters.py
@@ -41,7 +41,14 @@ class UsPhysicsParameters(NrelPhysicsInterface):
f'and building year of construction: {building.year_of_construction}\n')
continue
- self._create_storeys(building, archetype)
+ # if building has no thermal zones defined from geometry, one thermal zone per storey is assigned
+ if building.thermal_zones is None:
+ self._create_storeys(building, archetype)
+ thermal_zones = []
+ for storey in building.storeys:
+ thermal_zones.append(storey.thermal_zone)
+ building.thermal_zones = thermal_zones
+
self._assign_values(building, archetype)
def _search_archetype(self, building_type, standard, climate_zone):
@@ -63,12 +70,15 @@ class UsPhysicsParameters(NrelPhysicsInterface):
for thermal_boundary in thermal_zone.thermal_boundaries:
construction_type = ConstructionHelper.nrel_construction_types[thermal_boundary.type]
thermal_boundary_archetype = self._search_construction_in_archetype(archetype, construction_type)
- if thermal_boundary_archetype.outside_solar_absorptance is not None:
- thermal_boundary.outside_solar_absorptance = thermal_boundary_archetype.outside_solar_absorptance
+ thermal_boundary.outside_solar_absorptance = thermal_boundary_archetype.outside_solar_absorptance
thermal_boundary.outside_thermal_absorptance = thermal_boundary_archetype.outside_thermal_absorptance
thermal_boundary.outside_visible_absorptance = thermal_boundary_archetype.outside_visible_absorptance
thermal_boundary.construction_name = thermal_boundary_archetype.construction_name
- thermal_boundary.window_ratio = thermal_boundary_archetype.window_ratio
+ try:
+ thermal_boundary.window_ratio = thermal_boundary_archetype.window_ratio
+ except ValueError:
+ # This is the normal operation way when the windows are defined in the geometry
+ continue
thermal_boundary.layers = []
for layer_archetype in thermal_boundary_archetype.layers:
layer = Layer()
@@ -83,13 +93,11 @@ class UsPhysicsParameters(NrelPhysicsInterface):
material.thermal_absorptance = layer_archetype.thermal_absorptance
material.visible_absorptance = layer_archetype.visible_absorptance
material.thermal_resistance = layer_archetype.thermal_resistance
- if layer_archetype.lca_id is not None:
- material.lca_id = layer_archetype.lca_id
layer.material = material
thermal_boundary.layers.append(layer)
for thermal_opening in thermal_boundary.thermal_openings:
- if thermal_boundary_archetype.thermal_opening is not None:
- thermal_opening_archetype = thermal_boundary_archetype.thermal_opening
+ if thermal_boundary_archetype.thermal_opening_archetype is not None:
+ thermal_opening_archetype = thermal_boundary_archetype.thermal_opening_archetype
thermal_opening.frame_ratio = thermal_opening_archetype.frame_ratio
thermal_opening.g_value = thermal_opening_archetype.g_value
thermal_opening.conductivity = thermal_opening_archetype.conductivity
diff --git a/imports/customized_imports/sanam_customized_usage_parameters.py b/imports/customized_imports/sanam_customized_usage_parameters.py
index 6604a8e5..442ec9d3 100644
--- a/imports/customized_imports/sanam_customized_usage_parameters.py
+++ b/imports/customized_imports/sanam_customized_usage_parameters.py
@@ -7,7 +7,7 @@ Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.mons
import sys
import xmltodict
from imports.geometry.helpers.geometry_helper import GeometryHelper as gh
-from imports.usage.data_classes.hft_usage_zone_archetype import HftUsageZoneArchetype as huza
+from imports.usage.data_classes.usage_zone_archetype import UsageZoneArchetype as huza
import helpers.constants as cte
diff --git a/imports/geometry/citygml.py b/imports/geometry/citygml.py
index 847c2bef..09803d55 100644
--- a/imports/geometry/citygml.py
+++ b/imports/geometry/citygml.py
@@ -83,7 +83,7 @@ class CityGml:
surfaces = CityGmlLod2(city_object).surfaces
else:
raise NotImplementedError("Not supported level of detail")
- return Building(name, lod, surfaces, year_of_construction, function, self._lower_corner, [])
+ return Building(name, lod, surfaces, year_of_construction, function, self._lower_corner, terrains=None)
def _create_parts_consisting_building(self, city_object):
name = city_object['@id']
diff --git a/imports/geometry/obj.py b/imports/geometry/obj.py
index 6349921b..3ce1fae3 100644
--- a/imports/geometry/obj.py
+++ b/imports/geometry/obj.py
@@ -76,6 +76,6 @@ class Obj:
perimeter_polygon = solid_polygon
surface = Surface(solid_polygon, perimeter_polygon)
surfaces.append(surface)
- building = Building(name, lod, surfaces, year_of_construction, function, self._lower_corner)
+ building = Building(name, lod, surfaces, year_of_construction, function, self._lower_corner, terrains=None)
self._city.add_city_object(building)
return self._city
diff --git a/imports/geometry/rhino.py b/imports/geometry/rhino.py
index 38c350f0..b9007ecb 100644
--- a/imports/geometry/rhino.py
+++ b/imports/geometry/rhino.py
@@ -3,16 +3,18 @@ Rhino module parses rhino files and import the geometry into the city model stru
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
-import numpy as np
from numpy import inf
-from rhino3dm import *
-from rhino3dm._rhino3dm import MeshType
+#from rhino3dm import *
+#from rhino3dm._rhino3dm import MeshType
+from city_model_structure.attributes.point import Point
+import numpy as np
+from helpers.configuration_helper import ConfigurationHelper
from city_model_structure.attributes.polygon import Polygon
from city_model_structure.building import Building
-from city_model_structure.building_demand.surface import Surface as LibsSurface
from city_model_structure.city import City
-from helpers.configuration_helper import ConfigurationHelper
+from city_model_structure.building_demand.surface import Surface as LibsSurface
+from helpers.constants import EPSILON
from imports.geometry.helpers.geometry_helper import GeometryHelper
@@ -25,7 +27,19 @@ class Rhino:
self._max_x = self._max_y = self._max_z = min_float
@staticmethod
- def _solid_points(coordinates):
+ def _in_perimeter(wall, corner):
+ res = wall.contains_point(Point(corner))
+ print(f'belong: {res} wall:({wall.coordinates}) corner: ({corner})')
+ return res
+
+ @staticmethod
+ def _add_hole(solid_polygon, hole):
+ first = solid_polygon.points[0]
+ points = first + hole.points + solid_polygon.points
+ return Polygon(points)
+
+ @staticmethod
+ def _solid_points(coordinates) -> np.ndarray:
solid_points = np.fromstring(coordinates, dtype=float, sep=' ')
solid_points = GeometryHelper.to_points_matrix(solid_points)
return solid_points
@@ -82,6 +96,7 @@ class Rhino:
windows.append(Polygon(surface.perimeter_polygon.inverse))
else:
buildings.append(rhino_object)
+ print(f'windows: {len(windows)}')
# todo: this method will be pretty inefficient
for hole in windows:
corner = hole.coordinates[0]
diff --git a/imports/life_cycle_assessment/lca_machine.py b/imports/life_cycle_assessment/lca_machine.py
index dbfe1760..87c35c5c 100644
--- a/imports/life_cycle_assessment/lca_machine.py
+++ b/imports/life_cycle_assessment/lca_machine.py
@@ -8,7 +8,6 @@ import xmltodict
from pathlib import Path
from city_model_structure.machine import Machine
-
class LcaMachine:
def __init__(self, city, base_path):
self._city = city
@@ -17,15 +16,12 @@ class LcaMachine:
def enrich(self):
self._city.machines = []
- # print(self._base_path)
path = Path(self._base_path / 'lca_data.xml').resolve()
with open(path) as xml:
self._lca = xmltodict.parse(xml.read())
- for machine in self._lca["library"]["machines"]['machine']:
- self._city.machines.append(Machine(machine['@id'], machine['@name'], machine['work_efficiency']['#text'],
- machine['work_efficiency']['@unit'],
- machine['energy_consumption_rate']['#text'],
- machine['energy_consumption_rate']['@unit'],
- machine['carbon_emission_factor']['#text'],
- machine['carbon_emission_factor']['@unit']))
+ for machine in self._lca["library"]["machines"]['machine']:
+ self._city.machines.append(Machine(machine['@id'], machine['@name'], machine['work_efficiency']['#text'],
+ machine['work_efficiency']['@unit'], machine['energy_consumption_rate']['#text'],
+ machine['energy_consumption_rate']['@unit'], machine['carbon_emission_factor']['#text'],
+ machine['carbon_emission_factor']['@unit']))
diff --git a/imports/life_cycle_assessment/lca_material.py b/imports/life_cycle_assessment/lca_material.py
index e0081b7c..5044b315 100644
--- a/imports/life_cycle_assessment/lca_material.py
+++ b/imports/life_cycle_assessment/lca_material.py
@@ -7,8 +7,6 @@ Contributor Mohammad Reza mohammad.seyedabadi@mail.concordia.ca
import xmltodict
from pathlib import Path
from city_model_structure.building_demand.material import Material
-from city_model_structure.lca_material import LcaMaterial as LcaMat
-
class LcaMaterial:
def __init__(self, city, base_path):
@@ -17,25 +15,26 @@ class LcaMaterial:
self._lca = None
def enrich(self):
- self._city.lca_materials = []
+ self._city.materials = []
path = Path(self._base_path / 'lca_data.xml').resolve()
with open(path) as xml:
self._lca = xmltodict.parse(xml.read())
for material in self._lca["library"]["building_materials"]['material']:
- _lca_material = LcaMat()
- _lca_material.type = material['@type']
- _lca_material.id = material['@id']
- _lca_material.name = material['@name']
- _lca_material.density = material['density']['#text']
- _lca_material.density_unit = material['density']['@unit']
- _lca_material.embodied_carbon = material['embodied_carbon']['#text']
- _lca_material.embodied_carbon_unit = material['embodied_carbon']['@unit']
- _lca_material.recycling_ratio = material['recycling_ratio']
- _lca_material.onsite_recycling_ratio = material['onsite_recycling_ratio']
- _lca_material.company_recycling_ratio = material['company_recycling_ratio']
- _lca_material.landfilling_ratio = material['landfilling_ratio']
- _lca_material.cost = 10 # todo: change this into material['cost']['#text']
- _lca_material._cost_unit = material['cost']['@unit']
- self._city.lca_materials.append(_lca_material)
+ _material = Material()
+ _material.type = material['@type']
+ _material.id = material['@id']
+ _material.name = material['@name']
+ _material.density=material['density']['#text']
+ _material.density_unit=material['density']['@unit']
+ _material.embodied_carbon=material['embodied_carbon']['#text']
+ _material.embodied_carbon_unit=material['embodied_carbon']['@unit']
+ _material.recycling_ratio=material['recycling_ratio']
+ _material.onsite_recycling_ratio=material['onsite_recycling_ratio']
+ _material.company_recycling_ratio=material['company_recycling_ratio']
+ _material.landfilling_ratio=material['landfilling_ratio']
+ _material.cost=material['cost']['#text']
+ _material._cost_unit=material['cost']['@unit']
+
+ self._city.materials.append(_material)
diff --git a/imports/sensors/concordia_energy_consumption.py b/imports/sensors/concordia_energy_consumption.py
index 686a4dd3..4f3c7d63 100644
--- a/imports/sensors/concordia_energy_consumption.py
+++ b/imports/sensors/concordia_energy_consumption.py
@@ -5,6 +5,7 @@ Copyright © 2021 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc
"""
import pandas as pd
from imports.sensors.concordia_file_report import ConcordiaFileReport
+from city_model_structure.iot.concordia_energy_sensor import ConcordiaEnergySensor
class ConcordiaEnergyConsumption(ConcordiaFileReport):
diff --git a/imports/sensors/concordia_gas_flow.py b/imports/sensors/concordia_gas_flow.py
index 11dec2e2..e9176fc7 100644
--- a/imports/sensors/concordia_gas_flow.py
+++ b/imports/sensors/concordia_gas_flow.py
@@ -5,7 +5,7 @@ Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.mons
"""
import pandas as pd
from imports.sensors.concordia_file_report import ConcordiaFileReport
-
+from city_model_structure.iot.concordia_gas_flow_sensor import ConcordiaGasFlowSensor
class ConcordiaGasFlow(ConcordiaFileReport):
@@ -24,7 +24,6 @@ class ConcordiaGasFlow(ConcordiaFileReport):
building_measures = [self._measures["Date time"], self._measures[self._sensor_point[self._sensors[i]]]]
building_headers = ["Date time", "Gas Flow Cumulative Monthly"]
building_energy_consumption = pd.concat(building_measures, keys=building_headers, axis=1)
- """
sensor = ConcordiaGasFlowSensor(self._sensors[i])
sensor_exist = False
for j in range(len(obj.sensors)):
@@ -35,4 +34,3 @@ class ConcordiaGasFlow(ConcordiaFileReport):
if not sensor_exist:
sensor.add_period(building_energy_consumption)
obj.sensors.append(sensor)
- """
\ No newline at end of file
diff --git a/imports/sensors/concordia_temperature.py b/imports/sensors/concordia_temperature.py
index e1f1cb32..f4b937a8 100644
--- a/imports/sensors/concordia_temperature.py
+++ b/imports/sensors/concordia_temperature.py
@@ -5,6 +5,7 @@ Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.mons
"""
import pandas as pd
from imports.sensors.concordia_file_report import ConcordiaFileReport
+from city_model_structure.iot.concordia_temperature_sensor import ConcordiaTemperatureSensor
class ConcordiaTemperature(ConcordiaFileReport):
@@ -22,7 +23,6 @@ class ConcordiaTemperature(ConcordiaFileReport):
building_measures = [self._measures["Date time"], self._measures[self._sensor_point[self._sensors[i]]]]
building_headers = ["Date time", "Temperature"]
building_energy_consumption = pd.concat(building_measures, keys=building_headers, axis=1)
- """
sensor = ConcordiaTemperatureSensor(self._sensors[i])
sensor_exist = False
for j in range(len(obj.sensors)):
@@ -33,4 +33,3 @@ class ConcordiaTemperature(ConcordiaFileReport):
if not sensor_exist:
sensor.add_period(building_energy_consumption)
obj.sensors.append(sensor)
- """
\ No newline at end of file
diff --git a/imports/usage/ca_usage_parameters.py b/imports/usage/ca_usage_parameters.py
index 01cc85a5..cd921332 100644
--- a/imports/usage/ca_usage_parameters.py
+++ b/imports/usage/ca_usage_parameters.py
@@ -58,14 +58,14 @@ class CaUsageParameters(HftUsageInterface):
# usage_zone.internal_gains when writing usage_zone.internal_gains = archetype.internal_gains.
# Therefore, this walk around has been done.
internal_gains = []
- for archetype_internal_gain in archetype.internal_gains:
+ for archetype_internal_gain in archetype.not_detailed_source_mean_annual_internal_gains:
internal_gain = InternalGains()
internal_gain.average_internal_gain = archetype_internal_gain.average_internal_gain
internal_gain.convective_fraction = archetype_internal_gain.convective_fraction
internal_gain.radiative_fraction = archetype_internal_gain.radiative_fraction
internal_gain.latent_fraction = archetype_internal_gain.latent_fraction
internal_gains.append(internal_gain)
- usage_zone.internal_gains = internal_gains
+ usage_zone.not_detailed_source_mean_annual_internal_gains = internal_gains
usage_zone.heating_setpoint = archetype.heating_setpoint
usage_zone.heating_setback = archetype.heating_setback
usage_zone.cooling_setpoint = archetype.cooling_setpoint
diff --git a/imports/usage/comnet_usage_parameters.py b/imports/usage/comnet_usage_parameters.py
index 5d10312c..0d0a835d 100644
--- a/imports/usage/comnet_usage_parameters.py
+++ b/imports/usage/comnet_usage_parameters.py
@@ -12,9 +12,10 @@ from helpers.configuration_helper import ConfigurationHelper as ch
from imports.geometry.helpers.geometry_helper import GeometryHelper
from imports.usage.helpers.usage_helper import UsageHelper
from city_model_structure.building_demand.usage_zone import UsageZone
+from city_model_structure.building_demand.lighting import Lighting
+from city_model_structure.building_demand.occupancy import Occupancy
+from city_model_structure.building_demand.appliances import Appliances
from city_model_structure.building_demand.internal_gains import InternalGains
-from imports.usage.data_classes.hft_usage_zone_archetype import HftUsageZoneArchetype as huza
-from imports.usage.data_classes.hft_internal_gains_archetype import HftInternalGainsArchetype as higa
class ComnetUsageParameters:
@@ -68,45 +69,44 @@ class ComnetUsageParameters:
'process': process_data}
@staticmethod
- def _parse_zone_usage_type(usage, data):
- if data['occupancy'][usage][0] <= 0:
- occupancy_density = 0
- else:
- occupancy_density = 1 / data['occupancy'][usage][0]
- mechanical_air_change = data['ventilation rate'][usage][0]
- internal_gains = []
- # lighting
- latent_fraction = ch().comnet_lighting_latent
- convective_fraction = ch().comnet_lighting_convective
- radiative_fraction = ch().comnet_lighting_radiant
- average_internal_gain = data['lighting'][usage][4]
- internal_gains.append(higa(internal_gains_type=cte.LIGHTING, average_internal_gain=average_internal_gain,
- convective_fraction=convective_fraction, radiative_fraction=radiative_fraction,
- latent_fraction=latent_fraction))
- # occupancy
- latent_fraction = data['occupancy'][usage][2] / (data['occupancy'][usage][1] + data['occupancy'][usage][2])
- sensible_fraction = float(1 - latent_fraction)
- convective_fraction = sensible_fraction * ch().comnet_occupancy_sensible_convective
- radiative_fraction = sensible_fraction * ch().comnet_occupancy_sensible_radiant
- average_internal_gain = (data['occupancy'][usage][1] + data['occupancy'][usage][2]) \
- * occupancy_density * cte.BTU_H_TO_WATTS
- internal_gains.append(higa(internal_gains_type=cte.OCCUPANCY, average_internal_gain=average_internal_gain,
- convective_fraction=convective_fraction, radiative_fraction=radiative_fraction,
- latent_fraction=latent_fraction))
- # plug loads
- if data['plug loads'][usage][0] != 'n.a.':
- latent_fraction = ch().comnet_plugs_latent
- convective_fraction = ch().comnet_plugs_convective
- radiative_fraction = ch().comnet_plugs_radiant
- average_internal_gain = data['plug loads'][usage][0]
- internal_gains.append(higa(internal_gains_type=cte.RECEPTACLE, average_internal_gain=average_internal_gain,
- convective_fraction=convective_fraction, radiative_fraction=radiative_fraction,
- latent_fraction=latent_fraction))
+ def _parse_zone_usage_type(usage, height, data):
+ _usage_zone = UsageZone()
+ _usage_zone.usage = usage
- usage_zone_archetype = huza(usage=usage, internal_gains=internal_gains,
- occupancy_density=occupancy_density,
- mechanical_air_change=mechanical_air_change)
- return usage_zone_archetype
+ # lighting
+ _lighting = Lighting()
+ _lighting.latent_fraction = ch().comnet_lighting_latent
+ _lighting.convective_fraction = ch().comnet_lighting_convective
+ _lighting.radiative_fraction = ch().comnet_lighting_radiant
+ _lighting.average_internal_gain = data['lighting'][usage][4]
+
+ # plug loads
+ _appliances = None
+ if data['plug loads'][usage][0] != 'n.a.':
+ _appliances = Appliances()
+ _appliances.latent_fraction = ch().comnet_plugs_latent
+ _appliances.convective_fraction = ch().comnet_plugs_convective
+ _appliances.radiative_fraction = ch().comnet_plugs_radiant
+ _appliances.average_internal_gain = data['plug loads'][usage][0]
+
+ # occupancy
+ _occupancy = Occupancy()
+ _occupancy.occupancy_density = data['occupancy'][usage][0] * cte.METERS_TO_FEET**2
+ _occupancy.sensible_convective_internal_gain = data['occupancy'][usage][1] \
+ * ch().comnet_occupancy_sensible_convective
+ _occupancy.sensible_radiant_internal_gain = data['occupancy'][usage][1] * ch().comnet_occupancy_sensible_radiant
+ _occupancy.latent_internal_gain = data['occupancy'][usage][2]
+
+ if _occupancy.occupancy_density <= 0:
+ _usage_zone.mechanical_air_change = 0
+ else:
+ _usage_zone.mechanical_air_change = data['ventilation rate'][usage][0] / _occupancy.occupancy_density \
+ * cte.HOUR_TO_MINUTES / cte.METERS_TO_FEET**3 / height
+
+ _usage_zone.occupancy = _occupancy
+ _usage_zone.lighting = _lighting
+ _usage_zone.appliances = _appliances
+ return _usage_zone
def enrich_buildings(self):
"""
@@ -151,12 +151,11 @@ class ComnetUsageParameters:
for archetype_internal_gain in archetype.internal_gains:
internal_gain = InternalGains()
internal_gain.type = archetype_internal_gain.type
- internal_gain.average_internal_gain = archetype_internal_gain.average_internal_gain * cte.METERS_TO_FEET**2
+ internal_gain.average_internal_gain = archetype_internal_gain.average_internal_gain
internal_gain.convective_fraction = archetype_internal_gain.convective_fraction
internal_gain.radiative_fraction = archetype_internal_gain.radiative_fraction
internal_gain.latent_fraction = archetype_internal_gain.latent_fraction
internal_gains.append(internal_gain)
usage_zone.internal_gains = internal_gains
- usage_zone.occupancy_density = archetype.occupancy_density * cte.METERS_TO_FEET**2
- usage_zone.mechanical_air_change = archetype.mechanical_air_change * usage_zone.occupancy_density \
- * cte.HOUR_TO_MINUTES / cte.METERS_TO_FEET**3 / height
+ usage_zone.occupancy_density = archetype.occupancy_density
+ usage_zone.mechanical_air_change = archetype.mechanical_air_change
\ No newline at end of file
diff --git a/imports/usage/data_classes/hft_usage_zone_archetype.py b/imports/usage/data_classes/hft_usage_zone_archetype.py
deleted file mode 100644
index b945e9c9..00000000
--- a/imports/usage/data_classes/hft_usage_zone_archetype.py
+++ /dev/null
@@ -1,144 +0,0 @@
-"""
-HftUsageZoneArchetype stores usage information by building archetypes
-SPDX - License - Identifier: LGPL - 3.0 - or -later
-Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
-"""
-from typing import List
-from imports.usage.data_classes.hft_internal_gains_archetype import HftInternalGainsArchetype
-
-
-class HftUsageZoneArchetype:
- """
- HftUsageZoneArchetype class
- """
- def __init__(self, usage=None, internal_gains=None, heating_set_point=None, heating_set_back=None,
- cooling_set_point=None, occupancy_density=None, hours_day=None, days_year=None,
- dhw_average_volume_pers_day=None, dhw_preparation_temperature=None,
- electrical_app_average_consumption_sqm_year=None, mechanical_air_change=None,
- occupancy=None, schedules=None):
- self._usage = usage
- self._internal_gains = internal_gains
- self._heating_setpoint = heating_set_point
- self._heating_setback = heating_set_back
- self._cooling_setpoint = cooling_set_point
- self._occupancy_density = occupancy_density
- self._hours_day = hours_day
- self._days_year = days_year
- self._dhw_average_volume_pers_day = dhw_average_volume_pers_day
- self._dhw_preparation_temperature = dhw_preparation_temperature
- self._electrical_app_average_consumption_sqm_year = electrical_app_average_consumption_sqm_year
- self._mechanical_air_change = mechanical_air_change
- self._occupancy = occupancy
- self._schedules = schedules
-
- @property
- def internal_gains(self) -> List[HftInternalGainsArchetype]:
- """
- Get usage zone internal gains
- :return: [InternalGains]
- """
- return self._internal_gains
-
- @property
- def heating_setpoint(self):
- """
- Get usage zone heating set point in celsius grads
- :return: float
- """
- return self._heating_setpoint
-
- @property
- def heating_setback(self):
- """
- Get usage zone heating setback in celsius grads
- :return: float
- """
- return self._heating_setback
-
- @property
- def cooling_setpoint(self):
- """
- Get usage zone cooling setpoint in celsius grads
- :return: float
- """
- return self._cooling_setpoint
-
- @property
- def hours_day(self):
- """
- Get usage zone usage hours per day
- :return: float
- """
- return self._hours_day
-
- @property
- def days_year(self):
- """
- Get usage zone usage days per year
- :return: float
- """
- return self._days_year
-
- @property
- def mechanical_air_change(self):
- """
- Set usage zone mechanical air change in air change per hour (ACH)
- :return: float
- """
- return self._mechanical_air_change
-
- @property
- def usage(self):
- """
- Get usage zone usage
- :return: str
- """
- return self._usage
-
- @property
- def occupancy(self):
- """
- Get schedules data
- :return: [Occupancy]
- """
- return self._occupancy
-
- @property
- def schedules(self):
- """
- Get schedules
- :return: [Schedule_Values]
- """
- return self._schedules
-
- @property
- def occupancy_density(self):
- """
- Get schedules density in persons per m2
- :return: float
- """
- return self._occupancy_density
-
- @property
- def dhw_average_volume_pers_day(self):
- """
- Get average DHW consumption in m3 per person per day
- :return: float
- """
- return self._dhw_average_volume_pers_day
-
- @property
- def dhw_preparation_temperature(self):
- """
- Get preparation temperature of the DHW in degree Celsius
- :return: float
- """
- return self._dhw_preparation_temperature
-
- @property
- def electrical_app_average_consumption_sqm_year(self):
- """
- Get average consumption of electrical appliances in Joules per m2 and year (J/m2yr)
- :return: float
- """
- return self._electrical_app_average_consumption_sqm_year
diff --git a/imports/usage/data_classes/usage_zone_archetype.py b/imports/usage/data_classes/usage_zone_archetype.py
new file mode 100644
index 00000000..67d1f964
--- /dev/null
+++ b/imports/usage/data_classes/usage_zone_archetype.py
@@ -0,0 +1,98 @@
+"""
+UsageZoneArchetype stores usage information by usage type
+SPDX - License - Identifier: LGPL - 3.0 - or -later
+Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
+"""
+
+from typing import List
+from imports.usage.data_classes.hft_internal_gains_archetype import HftInternalGainsArchetype
+
+
+class UsageZoneArchetype:
+ """
+ UsageZoneArchetype class
+ """
+ def __init__(self, usage=None, not_detailed_source_mean_annual_internal_gains=None, hours_day=None, days_year=None,
+ electrical_app_average_consumption_sqm_year=None, mechanical_air_change=None, occupancy=None,
+ lighting=None, appliances=None):
+ self._usage = usage
+ self._not_detailed_source_mean_annual_internal_gains = not_detailed_source_mean_annual_internal_gains
+ self._hours_day = hours_day
+ self._days_year = days_year
+ self._electrical_app_average_consumption_sqm_year = electrical_app_average_consumption_sqm_year
+ self._mechanical_air_change = mechanical_air_change
+ self._occupancy = occupancy
+ self._lighting = lighting
+ self._appliances = appliances
+
+ @property
+ def not_detailed_source_mean_annual_internal_gains(self) -> List[HftInternalGainsArchetype]:
+ """
+ Get usage zone internal gains from not detailed heating source in W/m2
+ :return: [InternalGains]
+ """
+ return self._not_detailed_source_mean_annual_internal_gains
+
+ @property
+ def hours_day(self):
+ """
+ Get usage zone usage hours per day
+ :return: float
+ """
+ return self._hours_day
+
+ @property
+ def days_year(self):
+ """
+ Get usage zone usage days per year
+ :return: float
+ """
+ return self._days_year
+
+ @property
+ def mechanical_air_change(self):
+ """
+ Set usage zone mechanical air change in air change per hour (ACH)
+ :return: float
+ """
+ return self._mechanical_air_change
+
+ @property
+ def usage(self):
+ """
+ Get usage zone usage
+ :return: str
+ """
+ return self._usage
+
+ @property
+ def electrical_app_average_consumption_sqm_year(self):
+ """
+ Get average consumption of electrical appliances in Joules per m2 and year (J/m2yr)
+ :return: float
+ """
+ return self._electrical_app_average_consumption_sqm_year
+
+ @property
+ def occupancy(self):
+ """
+ Get occupancy data
+ :return: Occupancy
+ """
+ return self._occupancy
+
+ @property
+ def lighting(self):
+ """
+ Get lighting data
+ :return: Lighting
+ """
+ return self._lighting
+
+ @property
+ def appliances(self):
+ """
+ Get appliances data
+ :return: Appliances
+ """
+ return self._appliances
diff --git a/imports/usage/hft_usage_interface.py b/imports/usage/hft_usage_interface.py
index 3da6457d..ed6dfefc 100644
--- a/imports/usage/hft_usage_interface.py
+++ b/imports/usage/hft_usage_interface.py
@@ -5,7 +5,7 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
import xmltodict
-from imports.usage.data_classes.hft_usage_zone_archetype import HftUsageZoneArchetype as huza
+from imports.usage.data_classes.usage_zone_archetype import UsageZoneArchetype as huza
from imports.usage.data_classes.hft_internal_gains_archetype import HftInternalGainsArchetype as higa
@@ -96,7 +96,7 @@ class HftUsageInterface:
# todo: for internal_gain in usage_zone_variant['schedules']['internGains']:????????????????
# There are no more internal gains? How is it saved when more than one???
# for internal_gain in usage_zone.internal_gains:
- internal_gains = usage_zone.internal_gains[0]
+ internal_gains = usage_zone.not_detailed_source_mean_annual_internal_gains[0]
latent_fraction = internal_gains.latent_fraction
convective_fraction = internal_gains.convective_fraction
average_internal_gain = internal_gains.average_internal_gain
diff --git a/imports/usage/hft_usage_parameters.py b/imports/usage/hft_usage_parameters.py
index fc254aa2..d2deeeae 100644
--- a/imports/usage/hft_usage_parameters.py
+++ b/imports/usage/hft_usage_parameters.py
@@ -58,14 +58,14 @@ class HftUsageParameters(HftUsageInterface):
# usage_zone.internal_gains when writing usage_zone.internal_gains = archetype.internal_gains.
# Therefore, this walk around has been done.
internal_gains = []
- for archetype_internal_gain in archetype.internal_gains:
+ for archetype_internal_gain in archetype.not_detailed_source_mean_annual_internal_gains:
internal_gain = InternalGains()
internal_gain.average_internal_gain = archetype_internal_gain.average_internal_gain
internal_gain.convective_fraction = archetype_internal_gain.convective_fraction
internal_gain.radiative_fraction = archetype_internal_gain.radiative_fraction
internal_gain.latent_fraction = archetype_internal_gain.latent_fraction
internal_gains.append(internal_gain)
- usage_zone.internal_gains = internal_gains
+ usage_zone.not_detailed_source_mean_annual_internal_gains = internal_gains
usage_zone.heating_setpoint = archetype.heating_setpoint
usage_zone.heating_setback = archetype.heating_setback
usage_zone.cooling_setpoint = archetype.cooling_setpoint
diff --git a/requirements.txt b/requirements.txt
index 5329b459..cce571d9 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -13,7 +13,7 @@ openpyxl~=3.0.7
networkx~=2.5.1
parseidf~=1.0.0
ply~=3.11
-rhino3dm~=7.11.1
+rhino3dm~=7.7.0
scipy==1.7.1
PyYAML==6.0
yaml~=0.2.5
diff --git a/unittests/test_construction_factory.py b/unittests/test_construction_factory.py
index 03266f95..e0ece40a 100644
--- a/unittests/test_construction_factory.py
+++ b/unittests/test_construction_factory.py
@@ -1,15 +1,16 @@
"""
TestConstructionFactory test and validate the city model structure construction parameters
SPDX - License - Identifier: LGPL - 3.0 - or -later
-Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
+Copyright © 2022 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from pathlib import Path
from unittest import TestCase
+import helpers.constants as cte
from imports.geometry_factory import GeometryFactory
from imports.construction_factory import ConstructionFactory
from imports.geometry.helpers.geometry_helper import GeometryHelper
-from imports.life_cycle_assessment_factory import LifeCycleAssessment
+
class TestConstructionFactory(TestCase):
"""
@@ -23,46 +24,138 @@ class TestConstructionFactory(TestCase):
self._city = None
self._example_path = (Path(__file__).parent / 'tests_data').resolve()
- def _get_citygml(self, file=None):
- if file is None:
- file = 'pluto_building.gml'
+ def _get_citygml(self, file):
file_path = (self._example_path / file).resolve()
self._city = GeometryFactory('citygml', file_path).city
- LifeCycleAssessment('material', self._city).enrich()
self.assertIsNotNone(self._city, 'city is none')
return self._city
- def test_city_with_construction_extended_library(self):
- """
- Enrich the city with the physic information and verify it
- :return: None
- """
-
- city = self._get_citygml()
+ def _check_buildings(self, city):
for building in city.buildings:
- building.function = GeometryHelper.pluto_to_function[building.function]
- self.assertIsNotNone(building.surfaces, 'Building has no surfaces')
+ self.assertIsNotNone(building.name, 'building name is none')
+ self.assertIsNotNone(building.lod, 'building lod is none')
+ self.assertIsNotNone(building.type, 'building type is none')
+ self.assertIsNotNone(building.volume, 'building volume is none')
+ self.assertIsNotNone(building.detailed_polyhedron, 'building detailed polyhedron is none')
+ self.assertIsNotNone(building.simplified_polyhedron, 'building simplified polyhedron is none')
+ self.assertIsNotNone(building.surfaces, 'building surfaces is none')
+ self.assertIsNotNone(building.centroid, 'building centroid is none')
+ self.assertIsNotNone(building.max_height, 'building max_height is none')
+ self.assertEqual(len(building.external_temperature), 0, 'building external temperature is calculated')
+ self.assertEqual(len(building.global_horizontal), 0, 'building global horizontal is calculated')
+ self.assertEqual(len(building.diffuse), 0, 'building diffuse is calculated')
+ self.assertEqual(len(building.beam), 0, 'building beam is calculated')
+ self.assertIsNotNone(building.lower_corner, 'building lower corner is none')
+ self.assertEqual(len(building.sensors), 0, 'building sensors are assigned')
+ self.assertIsNotNone(building.geometrical_zones, 'no geometrical zones created')
+ self.assertIsNotNone(building.grounds, 'building grounds is none')
+ self.assertIsNotNone(building.walls, 'building walls is none')
+ self.assertIsNotNone(building.roofs, 'building roofs is none')
+ self.assertIsNone(building.usage_zones, 'usage zones are defined')
+ self.assertTrue(len(building.thermal_zones) > 0, 'thermal zones are not defined')
+ self.assertIsNone(building.basement_heated, 'building basement_heated is not none')
+ self.assertIsNone(building.attic_heated, 'building attic_heated is not none')
+ self.assertIsNone(building.terrains, 'building terrains is not none')
+ self.assertIsNotNone(building.year_of_construction, 'building year_of_construction is none')
+ self.assertIsNotNone(building.function, 'building function is none')
+ self.assertIsNotNone(building.average_storey_height, 'building average_storey_height is none')
+ self.assertIsNotNone(building.storeys_above_ground, 'building storeys_above_ground is none')
+ self.assertEqual(len(building.heating), 0, 'building heating is not none')
+ self.assertEqual(len(building.cooling), 0, 'building cooling is not none')
+ self.assertIsNotNone(building.eave_height, 'building eave height is none')
+ self.assertIsNotNone(building.storeys, 'building storeys are not defined')
+ self.assertIsNotNone(building.roof_type, 'building roof type is none')
+ self.assertIsNotNone(building.floor_area, 'building floor_area is none')
+ self.assertIsNone(building.households, 'building households is not none')
+ self.assertFalse(building.is_conditioned, 'building is conditioned')
- # case 1: NREL
- ConstructionFactory('nrel', city).enrich()
+ def _check_thermal_zones(self, building):
+ for thermal_zone in building.thermal_zones:
+ self.assertIsNotNone(thermal_zone.id, 'thermal_zone id is none')
+ self.assertIsNotNone(thermal_zone.floor_area, 'thermal_zone floor area is none')
+ self.assertTrue(len(thermal_zone.thermal_boundaries) > 0, 'thermal_zone thermal_boundaries not defined')
+ self.assertIsNotNone(thermal_zone.additional_thermal_bridge_u_value, 'additional_thermal_bridge_u_value is none')
+ self.assertIsNotNone(thermal_zone.effective_thermal_capacity, 'thermal_zone effective_thermal_capacity is none')
+ self.assertIsNotNone(thermal_zone.indirectly_heated_area_ratio,
+ 'thermal_zone indirectly_heated_area_ratio is none')
+ self.assertIsNotNone(thermal_zone.infiltration_rate_system_off,
+ 'thermal_zone infiltration_rate_system_off is none')
+ self.assertIsNotNone(thermal_zone.infiltration_rate_system_on, 'thermal_zone infiltration_rate_system_on is none')
+ self.assertIsNone(thermal_zone.usage_zones, 'thermal_zone usage_zones is not none')
+ self.assertIsNotNone(thermal_zone.volume, 'thermal_zone volume is none')
+ self.assertIsNone(thermal_zone.ordinate_number, 'thermal_zone ordinate number is not none')
+ self.assertIsNone(thermal_zone.thermal_control, 'thermal_zone thermal_control is not none')
+ self.assertIsNone(thermal_zone.hvac_system, 'thermal_zone hvac_system is not none')
+
+ def _check_thermal_boundaries(self, thermal_zone):
+ for thermal_boundary in thermal_zone.thermal_boundaries:
+ self.assertIsNotNone(thermal_boundary.id, 'thermal_boundary id is none')
+ self.assertIsNotNone(thermal_boundary.parent_surface, 'thermal_boundary surface is none')
+ self.assertIsNotNone(thermal_boundary.thermal_zones, 'thermal_boundary delimits is none')
+ self.assertIsNotNone(thermal_boundary.opaque_area, 'thermal_boundary area is none')
+ self.assertIsNotNone(thermal_boundary.azimuth, 'thermal_boundary azimuth is none')
+ self.assertIsNotNone(thermal_boundary.inclination, 'thermal_boundary inclination is none')
+ self.assertIsNotNone(thermal_boundary.thickness, 'thermal_boundary thickness is none')
+ self.assertIsNotNone(thermal_boundary.type, 'thermal_boundary type is none')
+ if thermal_boundary.type is not cte.GROUND:
+ self.assertIsNotNone(thermal_boundary.outside_solar_absorptance, 'outside_solar_absorptance is none')
+ self.assertIsNotNone(thermal_boundary.shortwave_reflectance, 'shortwave_reflectance is none')
+ else:
+ self.assertIsNone(thermal_boundary.outside_solar_absorptance, 'outside_solar_absorptance is not none')
+ self.assertIsNone(thermal_boundary.shortwave_reflectance, 'shortwave_reflectance is not none')
+ self.assertIsNotNone(thermal_boundary.thermal_openings, 'thermal_openings is none')
+ self.assertIsNotNone(thermal_boundary.construction_name, 'construction_name is none')
+ self.assertIsNotNone(thermal_boundary.window_ratio, 'window_ratio is none')
+ self.assertIsNone(thermal_boundary.windows_areas, 'windows_areas is not none')
+ self.assertIsNotNone(thermal_boundary.u_value, 'u_value is none')
+ self.assertIsNotNone(thermal_boundary.hi, 'hi is none')
+ self.assertIsNotNone(thermal_boundary.he, 'he is none')
+ self.assertIsNotNone(thermal_boundary.virtual_internal_surface, 'virtual_internal_surface is none')
+ self.assertIsNone(thermal_boundary.inside_emissivity, 'inside_emissivity is not none')
+ self.assertIsNone(thermal_boundary.alpha_coefficient, 'alpha_coefficient is not none')
+ self.assertIsNone(thermal_boundary.radiative_coefficient, 'radiative_coefficient is not none')
+
+ def _check_thermal_openings(self, thermal_boundary):
+ for thermal_opening in thermal_boundary.thermal_openings:
+ self.assertIsNotNone(thermal_opening.id, 'thermal opening id is not none')
+ self.assertIsNotNone(thermal_opening.area, 'thermal opening area is not none')
+ self.assertRaises(Exception, lambda: thermal_opening.openable_ratio,
+ 'thermal_opening openable_ratio is not raising an exception')
+ self.assertIsNotNone(thermal_opening.frame_ratio, 'thermal opening frame_ratio is not none')
+ self.assertIsNotNone(thermal_opening.g_value, 'thermal opening g_value is not none')
+ self.assertIsNotNone(thermal_opening.overall_u_value, 'thermal opening overall_u_value is not none')
+ self.assertIsNotNone(thermal_opening.hi, 'thermal opening hi is not none')
+ self.assertIsNotNone(thermal_opening.he, 'thermal opening he is not none')
+ self.assertIsNone(thermal_opening.inside_emissivity, 'thermal opening inside_emissivity is not none')
+ self.assertIsNone(thermal_opening.alpha_coefficient, 'thermal opening alpha_coefficient is not none')
+ self.assertIsNone(thermal_opening.radiative_coefficient, 'thermal opening radiative_coefficient is not none')
+
+ def test_citygml_function(self):
+ """
+ Test city objects' functions in the city
+ """
+ # case 1: hft
+ file = 'one_building_in_kelowna.gml'
+ function_format = 'hft'
+ city = self._get_citygml(file)
for building in city.buildings:
- self.assertIsNotNone(building.average_storey_height, 'average_storey_height is none')
- self.assertIsNotNone(building.storeys_above_ground, 'storeys_above_ground is none')
- self.assertIsNot(len(building.thermal_zones), 0, 'no building thermal_zones defined')
- for thermal_zone in building.thermal_zones:
- self.assertIsNotNone(thermal_zone.effective_thermal_capacity, 'effective_thermal_capacity is none')
- self.assertIsNotNone(thermal_zone.additional_thermal_bridge_u_value,
- 'additional_thermal_bridge_u_value is none')
- self.assertIsNotNone(thermal_zone.indirectly_heated_area_ratio, 'indirectly_heated_area_ratio is none')
- self.assertIsNotNone(thermal_zone.infiltration_rate_system_on, 'infiltration_rate_system_on is none')
- self.assertIsNotNone(thermal_zone.infiltration_rate_system_off, 'infiltration_rate_system_off is none')
- self.assertIsNotNone(thermal_zone.thermal_boundaries, 'thermal_boundaries is none')
- for thermal_boundary in thermal_zone.thermal_boundaries:
- if thermal_boundary.surface.type != 'Ground':
- self.assertIsNotNone(thermal_boundary.outside_solar_absorptance, 'outside_solar_absorptance is none')
- self.assertIsNotNone(thermal_boundary.window_ratio, 'window_ratio is none')
- for layer in thermal_boundary.layers:
- self.assertTrue(city.lca_material(layer.material.lca_id) is not None)
+ building.function = self._internal_function(function_format, building.function)
+ self.assertEqual(building.function, 'residential', 'format hft')
+
+ # case 2: Pluto
+ file = 'pluto_building.gml'
+ function_format = 'pluto'
+ city = self._get_citygml(file)
+ for building in city.buildings:
+ building.function = self._internal_function(function_format, building.function)
+ self.assertEqual(building.function, 'secondary school', 'format pluto')
+
+ # case 3: Alkis
+ file = 'one_building_in_kelowna_alkis.gml'
+ function_format = 'alkis'
+ city = self._get_citygml(file)
+ for building in city.buildings:
+ self.assertRaises(Exception, lambda: self._internal_function(function_format, building.function))
def test_city_with_construction_reduced_library(self):
"""
@@ -72,21 +165,69 @@ class TestConstructionFactory(TestCase):
city = self._get_citygml(file)
for building in city.buildings:
building.function = GeometryHelper.hft_to_function[building.function]
- # case 2: NRCAN
ConstructionFactory('nrcan', city).enrich()
+
+ self._check_buildings(city)
for building in city.buildings:
- self.assertIsNotNone(building.average_storey_height, 'average_storey_height is none')
- self.assertIsNotNone(building.storeys_above_ground, 'storeys_above_ground is none')
- self.assertIsNot(len(building.thermal_zones), 0, 'no building thermal_zones defined')
+ self._check_thermal_zones(building)
for thermal_zone in building.thermal_zones:
- self.assertIsNotNone(thermal_zone.effective_thermal_capacity, 'effective_thermal_capacity is none')
- self.assertIsNotNone(thermal_zone.additional_thermal_bridge_u_value,
- 'additional_thermal_bridge_u_value is none')
- self.assertIsNotNone(thermal_zone.indirectly_heated_area_ratio, 'indirectly_heated_area_ratio is none')
- self.assertIsNotNone(thermal_zone.infiltration_rate_system_on, 'infiltration_rate_system_on is none')
- self.assertIsNotNone(thermal_zone.infiltration_rate_system_off, 'infiltration_rate_system_off is none')
- self.assertIsNotNone(thermal_zone.thermal_boundaries, 'thermal_boundaries is none')
- self.assertIsNot(len(thermal_zone.thermal_boundaries), 0, 'no boundaries of thermal_zone defined')
+ self._check_thermal_boundaries(thermal_zone)
for thermal_boundary in thermal_zone.thermal_boundaries:
- self.assertIsNotNone(thermal_boundary.outside_solar_absorptance, 'outside_solar_absorptance is none')
- self.assertIsNotNone(thermal_boundary.window_ratio, 'window_ratio is none')
+ self.assertIsNone(thermal_boundary.outside_thermal_absorptance, 'outside_thermal_absorptance is not none')
+ self.assertIsNone(thermal_boundary.outside_visible_absorptance, 'outside_visible_absorptance is not none')
+ self.assertIsNone(thermal_boundary.layers, 'layers is not none')
+
+ self._check_thermal_openings(thermal_boundary)
+ for thermal_opening in thermal_boundary.thermal_openings:
+ self.assertIsNone(thermal_opening.conductivity, 'thermal_opening conductivity is not none')
+ self.assertIsNone(thermal_opening.thickness, 'thermal opening thickness is not none')
+ self.assertIsNone(thermal_opening.front_side_solar_transmittance_at_normal_incidence,
+ 'thermal opening front_side_solar_transmittance_at_normal_incidence is not none')
+ self.assertIsNone(thermal_opening.back_side_solar_transmittance_at_normal_incidence,
+ 'thermal opening back_side_solar_transmittance_at_normal_incidence is not none')
+
+ def test_city_with_construction_extended_library(self):
+ """
+ Enrich the city with the construction information and verify it
+ """
+ file = 'pluto_building.gml'
+ city = self._get_citygml(file)
+ for building in city.buildings:
+ building.function = GeometryHelper.pluto_to_function[building.function]
+ ConstructionFactory('nrel', city).enrich()
+
+ self._check_buildings(city)
+ for building in city.buildings:
+ self._check_thermal_zones(building)
+ for thermal_zone in building.thermal_zones:
+ self._check_thermal_boundaries(thermal_zone)
+ for thermal_boundary in thermal_zone.thermal_boundaries:
+ if thermal_boundary.type is not cte.GROUND:
+ self.assertIsNotNone(thermal_boundary.outside_thermal_absorptance, 'outside_thermal_absorptance is none')
+ self.assertIsNotNone(thermal_boundary.outside_visible_absorptance, 'outside_visible_absorptance is none')
+ else:
+ self.assertIsNone(thermal_boundary.outside_thermal_absorptance, 'outside_thermal_absorptance is not none')
+ self.assertIsNone(thermal_boundary.outside_visible_absorptance, 'outside_visible_absorptance is not none')
+ self.assertIsNotNone(thermal_boundary.layers, 'layers is none')
+
+ self._check_thermal_openings(thermal_boundary)
+ for thermal_opening in thermal_boundary.thermal_openings:
+ self.assertIsNotNone(thermal_opening.conductivity, 'thermal_opening conductivity is none')
+ self.assertIsNotNone(thermal_opening.thickness, 'thermal opening thickness is none')
+ self.assertIsNotNone(thermal_opening.front_side_solar_transmittance_at_normal_incidence,
+ 'thermal opening front_side_solar_transmittance_at_normal_incidence is none')
+ self.assertIsNotNone(thermal_opening.back_side_solar_transmittance_at_normal_incidence,
+ 'thermal opening back_side_solar_transmittance_at_normal_incidence is none')
+
+ @staticmethod
+ def _internal_function(function_format, original_function):
+ if function_format == 'hft':
+ new_function = GeometryHelper.hft_to_function[original_function]
+ elif function_format == 'pluto':
+ new_function = GeometryHelper.pluto_to_function[original_function]
+ elif function_format == 'alkis':
+ # todo: not implemented yet!!
+ raise NotImplementedError
+ else:
+ raise Exception('Function key not recognized. Implemented only "hft" and "pluto"')
+ return new_function
diff --git a/unittests/test_geometry_factory.py b/unittests/test_geometry_factory.py
index 77d17256..b9142591 100644
--- a/unittests/test_geometry_factory.py
+++ b/unittests/test_geometry_factory.py
@@ -6,8 +6,6 @@ Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.mons
from pathlib import Path
from unittest import TestCase
from imports.geometry_factory import GeometryFactory
-from imports.geometry.helpers.geometry_helper import GeometryHelper
-from city_model_structure.building_demand.thermal_opening import ThermalOpening
class TestGeometryFactory(TestCase):
@@ -36,20 +34,65 @@ class TestGeometryFactory(TestCase):
self.assertIsNotNone(self._city, 'city is none')
return self._city
- @staticmethod
- def _internal_function(function_format, original_function):
- new_function = original_function
- if function_format == 'hft':
- new_function = GeometryHelper.hft_to_function[original_function]
- elif function_format == 'pluto':
- new_function = GeometryHelper.pluto_to_function[original_function]
- elif function_format == 'alkis':
- # todo: not implemented yet!!
- raise NotImplementedError
- return new_function
+ def _check_buildings(self, city):
+ for building in city.buildings:
+ self.assertIsNotNone(building.name, 'building name is none')
+ self.assertIsNotNone(building.lod, 'building lod is none')
+ self.assertIsNotNone(building.type, 'building type is none')
+ self.assertIsNotNone(building.volume, 'building volume is none')
+ self.assertIsNotNone(building.detailed_polyhedron, 'building detailed polyhedron is none')
+ self.assertIsNotNone(building.simplified_polyhedron, 'building simplified polyhedron is none')
+ self.assertIsNotNone(building.surfaces, 'building surfaces is none')
+ self.assertIsNotNone(building.centroid, 'building centroid is none')
+ self.assertIsNotNone(building.max_height, 'building max_height is none')
+ self.assertEqual(len(building.external_temperature), 0, 'building external temperature is calculated')
+ self.assertEqual(len(building.global_horizontal), 0, 'building global horizontal is calculated')
+ self.assertEqual(len(building.diffuse), 0, 'building diffuse is calculated')
+ self.assertEqual(len(building.beam), 0, 'building beam is calculated')
+ self.assertIsNotNone(building.lower_corner, 'building lower corner is none')
+ self.assertEqual(len(building.sensors), 0, 'building sensors are assigned')
+ self.assertIsNotNone(building.geometrical_zones, 'no geometrical zones created')
+ self.assertIsNotNone(building.grounds, 'building grounds is none')
+ self.assertIsNotNone(building.walls, 'building walls is none')
+ self.assertIsNotNone(building.roofs, 'building roofs is none')
+ self.assertIsNone(building.usage_zones, 'usage zones are defined')
+ self.assertIsNone(building.thermal_zones, 'thermal zones are defined')
+ self.assertIsNone(building.basement_heated, 'building basement_heated is not none')
+ self.assertIsNone(building.attic_heated, 'building attic_heated is not none')
+ self.assertIsNone(building.terrains, 'building terrains is not none')
+ self.assertIsNotNone(building.year_of_construction, 'building year_of_construction is none')
+ self.assertIsNotNone(building.function, 'building function is none')
+ self.assertIsNone(building.average_storey_height, 'building average_storey_height is not none')
+ self.assertIsNone(building.storeys_above_ground, 'building storeys_above_ground is not none')
+ self.assertEqual(len(building.heating), 0, 'building heating is not none')
+ self.assertEqual(len(building.cooling), 0, 'building cooling is not none')
+ self.assertIsNotNone(building.eave_height, 'building eave height is none')
+ self.assertIsNone(building.storeys, 'building storeys are defined')
+ self.assertIsNotNone(building.roof_type, 'building roof type is none')
+ self.assertIsNotNone(building.floor_area, 'building floor_area is none')
+ self.assertIsNone(building.households, 'building households is not none')
+ self.assertFalse(building.is_conditioned, 'building is_conditioned is conditioned')
+
+ def _check_surfaces(self, building):
+ for surface in building.surfaces:
+ self.assertIsNotNone(surface.name, 'surface name is none')
+ self.assertIsNotNone(surface.id, 'surface id is none')
+ self.assertIsNone(surface.swr, 'surface swr is not none')
+ self.assertIsNotNone(surface.lower_corner, 'surface envelope_lower_corner is none')
+ self.assertIsNotNone(surface.upper_corner, 'surface envelope_upper_corner is none')
+ self.assertIsNotNone(surface.area_below_ground, 'surface area_below_ground is none')
+ self.assertIsNotNone(surface.area_above_ground, 'surface area_above_ground is none')
+ self.assertIsNotNone(surface.azimuth, 'surface azimuth is none')
+ self.assertIsNotNone(surface.type, 'surface type is none')
+ self.assertIsNotNone(surface.inclination, 'surface inclination is none')
+ self.assertEqual(len(surface.global_irradiance), 0, 'global irradiance is calculated')
+ self.assertIsNotNone(surface.perimeter_polygon, 'surface perimeter_polygon is none')
+ self.assertIsNone(surface.holes_polygons, 'surface hole_polygons is not none')
+ self.assertIsNotNone(surface.solid_polygon, 'surface solid_polygon is none')
+ self.assertIsNone(surface.pv_system_installed, 'surface PV system installed is not none')
# citygml_classes
- def test_citygml_buildings(self):
+ def test_import_citygml(self):
"""
Test city objects in the city
:return: None
@@ -57,175 +100,9 @@ class TestGeometryFactory(TestCase):
file = 'one_building_in_kelowna.gml'
city = self._get_citygml(file)
self.assertTrue(len(city.buildings) == 1)
+ self._check_buildings(city)
for building in city.buildings:
- self.assertIsNotNone(building.name, 'building name is none')
- self.assertIsNotNone(building.lod, 'building lod is none')
- self.assertIsNotNone(building.centroid, 'building centroid is none')
- self.assertIsNotNone(building.year_of_construction, 'building year_of_construction is none')
- self.assertIsNotNone(building.function, 'building function is none')
- self.assertIsNotNone(building.volume, 'building volume is none')
- self.assertIsNotNone(building.surfaces, 'building surfaces is none')
- self.assertIsNotNone(building.surfaces[0].name, 'surface not found')
- self.assertIsNone(building.basement_heated, 'building basement_heated is not none')
- self.assertIsNone(building.attic_heated, 'building attic_heated is not none')
- self.assertIsNotNone(building.terrains, 'building terrains is none')
- self.assertIsNone(building.average_storey_height, 'building average_storey_height is not none')
- self.assertIsNotNone(building.type, 'building type is none')
- self.assertIsNotNone(building.max_height, 'building max_height is none')
- self.assertIsNotNone(building.floor_area, 'building floor_area is none')
-
- def test_citygml_surfaces(self):
- """
- Test surfaces in city objects
- :return: None
- """
- file = 'one_building_in_kelowna.gml'
- city = self._get_citygml(file)
- for building in city.buildings:
- for surface in building.surfaces:
- self.assertIsNotNone(surface.name, 'surface name is none')
- self.assertIsNotNone(surface.type, 'surface type is none')
- self.assertIsNotNone(surface.azimuth, 'surface azimuth is none')
- self.assertIsNotNone(surface.inclination, 'surface inclination is none')
- self.assertIsNotNone(surface.area_below_ground, 'surface area_below_ground is none')
- self.assertIsNotNone(surface.area_above_ground, 'surface area_above_ground is none')
- self.assertIsNotNone(surface.global_irradiance, 'monthly irradiance is none')
- self.assertIsNone(surface.swr, 'surface swr is not none')
- self.assertIsNotNone(surface.lower_corner, 'surface envelope_lower_corner is none')
- self.assertIsNotNone(surface.upper_corner, 'surface envelope_upper_corner is none')
- self.assertIsNotNone(surface.area_above_ground, 'surface area_above_ground is none')
- self.assertIsNotNone(surface.perimeter_polygon, 'surface perimeter_polygon is none')
- self.assertIsNone(surface.holes_polygons, 'surface hole_polygons is not none')
- self.assertIsNotNone(surface.solid_polygon, 'surface solid_polygon is none')
-
- def test_citygml_thermal_zone(self):
- """
- Test thermal zones in city objects
- :return: None
- """
- file = 'one_building_in_kelowna.gml'
- city = self._get_citygml(file)
- for building in city.buildings:
- self.assertIsNot(len(building.thermal_zones), 0, 'no building thermal_zones defined')
- for thermal_zone in building.thermal_zones:
- self.assertIsNotNone(thermal_zone.thermal_boundaries, 'thermal_zone thermal_boundaries is none')
- self.assertIsNotNone(thermal_zone.floor_area, 'thermal_zone floor_area is none')
- self.assertIsNone(thermal_zone.additional_thermal_bridge_u_value,
- 'thermal_zone additional_thermal_bridge_u_value is not none')
- self.assertIsNone(thermal_zone.effective_thermal_capacity,
- 'thermal_zone effective_thermal_capacity is not none')
- self.assertIsNone(thermal_zone.indirectly_heated_area_ratio,
- 'thermal_zone indirectly_heated_area_ratio is not none')
- self.assertIsNone(thermal_zone.infiltration_rate_system_off,
- 'thermal_zone infiltration_rate_system_off is not none')
- self.assertIsNone(thermal_zone.infiltration_rate_system_on,
- 'thermal_zone infiltration_rate_system_on is not none')
- self.assertIsNone(thermal_zone.usage_zones,
- 'thermal_zone usage_zones are not none')
-
- def test_citygml_thermal_boundary(self):
- """
- Test thermal boundaries in thermal zones
- :return: None
- """
- file = 'one_building_in_kelowna.gml'
- city = self._get_citygml(file)
- for building in city.buildings:
- self.assertIsNot(len(building.thermal_zones), 0, 'no building thermal_zones defined')
- for thermal_zone in building.thermal_zones:
- self.assertIsNot(len(thermal_zone.thermal_boundaries), 0, 'no building thermal_boundaries defined')
- for thermal_boundary in thermal_zone.thermal_boundaries:
- print(thermal_boundary.surface.type)
- print(thermal_boundary.surface.area_above_ground)
- self.assertIsNotNone(thermal_boundary.surface, 'thermal_boundary surface is none')
- self.assertIsNotNone(thermal_boundary.type, 'thermal_boundary type is none')
- self.assertIsNotNone(thermal_boundary.area, 'thermal_boundary area is none')
- self.assertIsNotNone(thermal_boundary.azimuth, 'thermal_boundary azimuth is none')
- self.assertIsNotNone(thermal_boundary.thermal_zones, 'thermal_boundary delimits is none')
- self.assertIsNotNone(thermal_boundary.inclination, 'thermal_boundary inclination is none')
-
- def test_citygml_thermal_opening(self):
- """
- Test thermal openings in thermal zones
- :return: None
- """
- file = 'one_building_in_kelowna.gml'
- city = self._get_citygml(file)
- for building in city.buildings:
- self.assertIsNot(len(building.thermal_zones), 0, 'no building thermal_zones defined')
- for thermal_zone in building.thermal_zones:
- self.assertIsNot(len(thermal_zone.thermal_boundaries), 0, 'no building thermal_boundaries defined')
- for thermal_boundary in thermal_zone.thermal_boundaries:
- thermal_opening: ThermalOpening
- for thermal_opening in thermal_boundary.thermal_openings:
- self.assertIsNone(thermal_opening.frame_ratio, 'thermal_opening frame_ratio was initialized')
- self.assertIsNone(thermal_opening.g_value, 'thermal_opening g_value was initialized')
- self.assertIsNone(thermal_opening.conductivity, 'thermal_opening conductivity_w_mk was initialized')
- self.assertIsNone(thermal_opening.back_side_solar_transmittance_at_normal_incidence,
- 'thermal_opening back_side_solar_transmittance_at_normal_incidence was initialized')
- self.assertRaises(Exception, lambda: thermal_opening.openable_ratio,
- 'thermal_opening openable_ratio is not raising an exception')
- self.assertIsNone(thermal_opening.front_side_solar_transmittance_at_normal_incidence,
- 'thermal_opening front_side_solar_transmittance_at_normal_incidence was initialized')
- self.assertIsNone(thermal_opening.thickness, 'thermal_opening thickness_m was initialized')
- self.assertRaises(Exception, lambda: thermal_opening.overall_u_value,
- 'thermal_opening u_value was initialized')
- self.assertIsNone(thermal_opening.overall_u_value, 'thermal_opening overall_u_value was initialized')
- self.assertIsNone(thermal_opening.hi, 'thermal_opening hi was initialized')
- self.assertIsNone(thermal_opening.he, 'thermal_opening he was initialized')
-
- def test_citygml_function(self):
- """
- Test city objects' functions in the city
- :return: None
- """
- # case 1: hft
- file = 'one_building_in_kelowna.gml'
- function_format = 'hft'
- city = self._get_citygml(file)
- for building in city.buildings:
- building.function = self._internal_function(function_format, building.function)
- self.assertEqual(building.function, 'residential', 'format hft')
-
- # case 2: Pluto
- file = 'pluto_building.gml'
- function_format = 'pluto'
- city = self._get_citygml(file)
- for building in city.buildings:
- building.function = self._internal_function(function_format, building.function)
- self.assertEqual(building.function, 'secondary school', 'format pluto')
-
- # case 3: Alkis
- file = 'one_building_in_kelowna_alkis.gml'
- function_format = 'alkis'
- city = self._get_citygml(file)
- for building in city.buildings:
- self.assertRaises(Exception, lambda: self._internal_function(function_format, building.function))
-
- def test_citygml_storeys(self):
- """
- Test division by storeys of buildings
- :return: None
- """
-
- file = 'one_building_in_kelowna.gml'
- city = self._get_citygml(file)
- for building in city.buildings:
- print('building')
- for surface in building.surfaces:
- print(surface.name)
- print(surface.type)
- print(surface.perimeter_polygon.area)
- building.average_storey_height = 1.5
- building.storeys_above_ground = 2
- storeys = building.storeys
- for storey in storeys:
- print(storey.name)
- print(storey.neighbours)
- for surface in storey.surfaces:
- print(surface.name)
- print(surface.type)
- print(surface.perimeter_polygon.area)
+ self._check_surfaces(building)
# obj
def test_import_obj(self):
@@ -235,8 +112,10 @@ class TestGeometryFactory(TestCase):
file = 'kelowna.obj'
city = self._get_obj(file)
self.assertIsNotNone(city, 'city is none')
+ self.assertTrue(len(city.buildings) == 1)
+ self._check_buildings(city)
for building in city.buildings:
- self.assertIsNotNone(building, 'building is none')
+ self._check_surfaces(building)
# osm
def test_subway(self):
diff --git a/unittests/test_life_cycle_assessment_factory.py b/unittests/test_life_cycle_assessment_factory.py
index 9e2e3e62..f7134f20 100644
--- a/unittests/test_life_cycle_assessment_factory.py
+++ b/unittests/test_life_cycle_assessment_factory.py
@@ -47,10 +47,8 @@ class TestLifeCycleAssessment(TestCase):
city_file = "../unittests/tests_data/C40_Final.gml"
city = GeometryFactory('citygml', city_file).city
LifeCycleAssessment('material', city).enrich()
- self.assertTrue(len(city.lca_materials) > 0)
- for lca_material in city.lca_materials:
- lca_mat = city.lca_material(lca_material.id)
- self.assertTrue(lca_mat is not None)
+ for material in city.materials:
+ self.assertTrue(len(city.materials) > 0)
diff --git a/unittests/test_schedules_factory.py b/unittests/test_schedules_factory.py
index 349c5c08..0506abae 100644
--- a/unittests/test_schedules_factory.py
+++ b/unittests/test_schedules_factory.py
@@ -67,3 +67,4 @@ class TestSchedulesFactory(TestCase):
for usage_zone in building.usage_zones:
for schedule in usage_zone.schedules:
print(schedule)
+ print(usage_zone.schedules[schedule])
diff --git a/unittests/test_sensors_factory.py b/unittests/test_sensors_factory.py
index f0de55c6..55b13e01 100644
--- a/unittests/test_sensors_factory.py
+++ b/unittests/test_sensors_factory.py
@@ -50,8 +50,8 @@ class TestSensorsFactory(TestCase):
Load concordia sensors and verify it
"""
SensorsFactory('cec', self._city, self._end_point).enrich()
- # SensorsFactory('cgf', self._city, self._end_point).enrich()
- # SensorsFactory('ct', self._city, self._end_point).enrich()
+ SensorsFactory('cgf', self._city, self._end_point).enrich()
+ SensorsFactory('ct', self._city, self._end_point).enrich()
for city_object in self._city.city_objects:
print(city_object.name, len(city_object.sensors))
for sensor in city_object.sensors:
diff --git a/unittests/test_usage_factory.py b/unittests/test_usage_factory.py
index abb434f9..3f8f38ea 100644
--- a/unittests/test_usage_factory.py
+++ b/unittests/test_usage_factory.py
@@ -7,8 +7,8 @@ from pathlib import Path
from unittest import TestCase
from imports.geometry_factory import GeometryFactory
-from imports.construction_factory import ConstructionFactory
from imports.usage_factory import UsageFactory
+from imports.schedules_factory import SchedulesFactory
from imports.construction_factory import ConstructionFactory
from imports.geometry.helpers.geometry_helper import GeometryHelper
from imports.construction_factory import ConstructionFactory
@@ -23,29 +23,119 @@ class TestUsageFactory(TestCase):
Configure test environment
:return:
"""
+ self._city = None
self._example_path = (Path(__file__).parent / 'tests_data').resolve()
def _get_citygml(self, file):
file_path = (self._example_path / file).resolve()
- _city = GeometryFactory('citygml', file_path).city
- self.assertIsNotNone(_city, 'city is none')
- return _city
+ self._city = GeometryFactory('citygml', file_path).city
+ self.assertIsNotNone(self._city, 'city is none')
+ return self._city
- def test_comnet(self):
+ def _check_buildings(self, city):
+ for building in city.buildings:
+ self.assertIsNotNone(building.name, 'building name is none')
+ self.assertIsNotNone(building.lod, 'building lod is none')
+ self.assertIsNotNone(building.type, 'building type is none')
+ self.assertIsNotNone(building.volume, 'building volume is none')
+ self.assertIsNotNone(building.detailed_polyhedron, 'building detailed polyhedron is none')
+ self.assertIsNotNone(building.simplified_polyhedron, 'building simplified polyhedron is none')
+ self.assertIsNotNone(building.surfaces, 'building surfaces is none')
+ self.assertIsNotNone(building.centroid, 'building centroid is none')
+ self.assertIsNotNone(building.max_height, 'building max_height is none')
+ self.assertEqual(len(building.external_temperature), 0, 'building external temperature is calculated')
+ self.assertEqual(len(building.global_horizontal), 0, 'building global horizontal is calculated')
+ self.assertEqual(len(building.diffuse), 0, 'building diffuse is calculated')
+ self.assertEqual(len(building.beam), 0, 'building beam is calculated')
+ self.assertIsNotNone(building.lower_corner, 'building lower corner is none')
+ self.assertEqual(len(building.sensors), 0, 'building sensors are assigned')
+ self.assertIsNotNone(building.geometrical_zones, 'no geometrical zones created')
+ self.assertIsNotNone(building.grounds, 'building grounds is none')
+ self.assertIsNotNone(building.walls, 'building walls is none')
+ self.assertIsNotNone(building.roofs, 'building roofs is none')
+ self.assertTrue(len(building.usage_zones) > 0, 'usage zones are not defined')
+ self.assertTrue(len(building.thermal_zones) > 0, 'thermal zones are not defined')
+ self.assertIsNotNone(building.basement_heated, 'building basement_heated is none')
+ self.assertIsNotNone(building.attic_heated, 'building attic_heated is none')
+ self.assertIsNone(building.terrains, 'building terrains is not none')
+ self.assertIsNotNone(building.year_of_construction, 'building year_of_construction is none')
+ self.assertIsNotNone(building.function, 'building function is none')
+ self.assertIsNotNone(building.average_storey_height, 'building average_storey_height is none')
+ self.assertIsNotNone(building.storeys_above_ground, 'building storeys_above_ground is none')
+ self.assertEqual(len(building.heating), 0, 'building heating is not none')
+ self.assertEqual(len(building.cooling), 0, 'building cooling is not none')
+ self.assertIsNotNone(building.eave_height, 'building eave height is none')
+ self.assertIsNotNone(building.storeys, 'building storeys are not defined')
+ self.assertIsNotNone(building.roof_type, 'building roof type is none')
+ self.assertIsNotNone(building.floor_area, 'building floor_area is none')
+ self.assertIsNone(building.households, 'building households is not none')
+ self.assertTrue(building.is_conditioned, 'building is not conditioned')
+
+ def _check_thermal_zones(self, building):
+ for thermal_zone in building.thermal_zones:
+ self.assertIsNotNone(thermal_zone.id, 'thermal_zone id is none')
+ self.assertIsNotNone(thermal_zone.floor_area, 'thermal_zone floor area is none')
+ self.assertTrue(len(thermal_zone.thermal_boundaries) > 0, 'thermal_zone thermal_boundaries not defined')
+ self.assertIsNotNone(thermal_zone.additional_thermal_bridge_u_value, 'additional_thermal_bridge_u_value is none')
+ self.assertIsNotNone(thermal_zone.effective_thermal_capacity, 'thermal_zone effective_thermal_capacity is none')
+ self.assertIsNotNone(thermal_zone.indirectly_heated_area_ratio,
+ 'thermal_zone indirectly_heated_area_ratio is none')
+ self.assertIsNotNone(thermal_zone.infiltration_rate_system_off,
+ 'thermal_zone infiltration_rate_system_off is none')
+ self.assertIsNotNone(thermal_zone.infiltration_rate_system_on, 'thermal_zone infiltration_rate_system_on is none')
+ self.assertIsNotNone(thermal_zone.usage_zones, 'thermal_zone usage_zones is none')
+ self.assertIsNotNone(thermal_zone.volume, 'thermal_zone volume is none')
+ self.assertIsNone(thermal_zone.ordinate_number, 'thermal_zone ordinate number is not none')
+ self.assertIsNotNone(thermal_zone.thermal_control, 'thermal_zone thermal_control is not none')
+ self.assertIsNotNone(thermal_zone.hvac_system, 'thermal_zone hvac_system is not none')
+
+ def _check_usage_zones(self, building):
+ for usage_zone in building.usage_zones:
+ self.assertIsNotNone(usage_zone.usage, 'usage is none')
+ self.assertIsNotNone(usage_zone.not_detailed_source_mean_annual_internal_gains, 'usage is none')
+ self.assertIsNotNone(usage_zone.cooling_setpoint, 'usage is none')
+ self.assertIsNotNone(usage_zone.heating_setback, 'usage is none')
+ self.assertIsNotNone(usage_zone.heating_setpoint, 'usage is none')
+ self.assertIsNotNone(usage_zone.occupancy_density, 'usage is none')
+ self.assertIsNotNone(usage_zone.hours_day, 'usage is none')
+ self.assertIsNotNone(usage_zone.days_year, 'usage is none')
+ self.assertIsNotNone(usage_zone.dhw_average_volume_pers_day, 'usage is none')
+ self.assertIsNotNone(usage_zone.dhw_preparation_temperature, 'usage is none')
+ self.assertIsNotNone(usage_zone.electrical_app_average_consumption_sqm_year, 'usage is none')
+ self.assertIsNotNone(usage_zone.is_heated, 'thermal_zone heated is none')
+ self.assertIsNotNone(usage_zone.is_cooled, 'thermal_zone cooled is none')
+
+ def _check_hvac(self, thermal_zone):
+ self.assertIsNotNone(None, 'hvac')
+
+ def _check_control(self, thermal_zone):
+ self.assertIsNotNone(None, 'control')
+
+ def test_import_comnet(self):
+ """
+ Enrich the city with the usage information from comnet and verify it
+ """
file = 'pluto_building.gml'
city = self._get_citygml(file)
for building in city.buildings:
building.function = GeometryHelper.pluto_to_function[building.function]
-
ConstructionFactory('nrel', city).enrich()
- UsageFactory('comnet', city).enrich()
- def test_city_with_usage(self):
+ UsageFactory('comnet', city).enrich()
+ SchedulesFactory('comnet', city).enrich()
+ self._check_buildings(city)
+ for building in city.buildings:
+ self._check_thermal_zones(building)
+ for thermal_zone in building.thermal_zones:
+ self._check_hvac(thermal_zone)
+ self._check_control(thermal_zone)
+ self.assertIsNotNone(building.usage_zones, 'building usage zones is none')
+ self._check_usage_zones(building)
+
+ def test_import_hft(self):
"""
- Enrich the city with the usage information and verify it
- :return: None
+ Enrich the city with the usage information from hft and verify it
"""
- # case 1: HFT
file = 'pluto_building.gml'
city = self._get_citygml(file)
for building in city.buildings:
@@ -56,7 +146,7 @@ class TestUsageFactory(TestCase):
self.assertIsNot(len(building.usage_zones), 0, 'no building usage_zones defined')
for usage_zone in building.usage_zones:
self.assertIsNotNone(usage_zone.usage, 'usage is none')
- self.assertIsNotNone(usage_zone.internal_gains, 'usage is none')
+ self.assertIsNotNone(usage_zone.not_detailed_source_mean_annual_internal_gains, 'usage is none')
self.assertIsNotNone(usage_zone.cooling_setpoint, 'usage is none')
self.assertIsNotNone(usage_zone.heating_setback, 'usage is none')
self.assertIsNotNone(usage_zone.heating_setpoint, 'usage is none')
@@ -69,8 +159,10 @@ class TestUsageFactory(TestCase):
self.assertIsNotNone(usage_zone.is_heated, 'thermal_zone heated is none')
self.assertIsNotNone(usage_zone.is_cooled, 'thermal_zone cooled is none')
-
- # case 2: CA
+ def test_import_ca(self):
+ """
+ Enrich the city with the usage information from hft and verify it
+ """
file = 'one_building_in_kelowna.gml'
city = self._get_citygml(file)
ConstructionFactory('nrel', city).enrich()
@@ -79,7 +171,7 @@ class TestUsageFactory(TestCase):
self.assertIsNot(len(building.usage_zones), 0, 'no building usage_zones defined')
for usage_zone in building.usage_zones:
self.assertIsNotNone(usage_zone.usage, 'usage is none')
- self.assertIsNotNone(usage_zone.internal_gains, 'usage is none')
+ self.assertIsNotNone(usage_zone.not_detailed_source_mean_annual_internal_gains, 'usage is none')
self.assertIsNotNone(usage_zone.cooling_setpoint, 'usage is none')
self.assertIsNotNone(usage_zone.heating_setback, 'usage is none')
self.assertIsNotNone(usage_zone.heating_setpoint, 'usage is none')