diff --git a/city_model_structure/building.py b/city_model_structure/building.py
index 9e144141..423d5ab8 100644
--- a/city_model_structure/building.py
+++ b/city_model_structure/building.py
@@ -174,6 +174,8 @@ class Building(CityObject):
: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
@@ -194,6 +196,15 @@ class Building(CityObject):
"""
return self._year_of_construction
+ @year_of_construction.setter
+ def year_of_construction(self, value):
+ """
+ Set building year of construction
+ :param value: int
+ """
+ if value is not None:
+ self._year_of_construction = value
+
@property
def function(self) -> Union[None, str]:
"""
diff --git a/data/customized_imports/ashrae_archetypes.xml b/data/customized_imports/ashrae_archetypes.xml
index 17569df2..df281f3a 100644
--- a/data/customized_imports/ashrae_archetypes.xml
+++ b/data/customized_imports/ashrae_archetypes.xml
@@ -5,99 +5,99 @@
assembly
- 0.15
+ 0.15
- 5
+ 5
health
- 0.1
+ 0.1
- 20
+ 20
hotel
- 0.11
+ 0.11
- 5
+ 5
manufacturing
- 0.07
+ 0.07
- 10
+ 10
office
- 0.05
+ 0.05
- 5
+ 5
restaurant
- 0.7
+ 0.7
- 7.5
+ 7.5
retail
- 0.2
+ 0.2
- 7.5
+ 7.5
school
- 0.25
+ 0.25
- 10
+ 10
lab
- 0.25
+ 0.25
- 10
+ 10
@@ -105,18 +105,18 @@
residential
- 5
+ 5
gymnasium
- 0.3
+ 0.3
- 7.5
+ 7.5
diff --git a/helpers/constants.py b/helpers/constants.py
index 6fdd14e0..8445fdfb 100644
--- a/helpers/constants.py
+++ b/helpers/constants.py
@@ -7,6 +7,10 @@ Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.mons
# universal constants
KELVIN = 273.15
+# converters
+HOUR_TO_MINUTES = 60
+METERS_TO_FEET = 3.28084
+
# time
SECOND = 'second'
MINUTE = 'minute'
diff --git a/helpers/enrich_city.py b/helpers/enrich_city.py
index e19bcf41..cc5067a1 100644
--- a/helpers/enrich_city.py
+++ b/helpers/enrich_city.py
@@ -55,10 +55,13 @@ class EnrichCity:
def _construction(self, construction_format):
ConstructionFactory(construction_format, self._city).enrich()
+
for building in self._city.buildings:
# infiltration_rate_system_off is a mandatory parameter.
# If it is not returned, extract the building from the calculation list
- if building.thermal_zones[0].infiltration_rate_system_off is None:
+ if len(building.thermal_zones) == 0:
+ self._city.remove_city_object(building)
+ elif building.thermal_zones[0].infiltration_rate_system_off is None:
self._city.remove_city_object(building)
if self._city.buildings is None:
self._errors.append('no archetype found per construction')
diff --git a/imports/construction/us_physics_parameters.py b/imports/construction/us_physics_parameters.py
index 50b0b8ca..f45e0f29 100644
--- a/imports/construction/us_physics_parameters.py
+++ b/imports/construction/us_physics_parameters.py
@@ -31,6 +31,7 @@ class UsPhysicsParameters(NrelPhysicsInterface):
# it is assumed that all buildings have the same archetypes' keys
for building in city.buildings:
building_type = ConstructionHelper.nrel_from_function(building.function)
+ print(building_type)
if building_type is None:
return
archetype = self._search_archetype(building_type,
diff --git a/imports/customized_imports/helpers/sanam_customized_usage_helper.py b/imports/customized_imports/helpers/sanam_customized_usage_helper.py
new file mode 100644
index 00000000..a0d981b8
--- /dev/null
+++ b/imports/customized_imports/helpers/sanam_customized_usage_helper.py
@@ -0,0 +1,38 @@
+"""
+Sanam's customized importer Usage helper
+SPDX - License - Identifier: LGPL - 3.0 - or -later
+Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
+"""
+import sys
+import helpers.constants as cte
+
+
+class SanamCustomizedUsageHelper:
+ """
+ SanamCustomizedUsage class
+ """
+ usage_to_customized = {
+ cte.RESIDENTIAL: 'residential',
+ cte.INDUSTRY: 'manufacturing',
+ cte.OFFICE_ADMINISTRATION: 'office',
+ cte.HOTEL: 'hotel',
+ cte.HEALTH_CARE: 'health',
+ cte.RETAIL: 'retail',
+ cte.HALL: 'assembly',
+ cte.RESTAURANT: 'restaurant',
+ cte.EDUCATION: 'school'
+ }
+ customized_default_value = 'residential'
+
+ @staticmethod
+ def customized_from_usage(usage):
+ """
+ Get customized usage from the given internal usage key
+ :param usage: str
+ :return: str
+ """
+ try:
+ return SanamCustomizedUsageHelper.usage_to_customized[usage]
+ except KeyError:
+ sys.stderr.write('Error: keyword not found. Returned default HfT usage "residential"\n')
+ return SanamCustomizedUsageHelper.customized_default_value
diff --git a/imports/customized_imports/sanam_customized_usage_parameters.py b/imports/customized_imports/sanam_customized_usage_parameters.py
index 80d2e063..6604a8e5 100644
--- a/imports/customized_imports/sanam_customized_usage_parameters.py
+++ b/imports/customized_imports/sanam_customized_usage_parameters.py
@@ -8,7 +8,7 @@ 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 city_model_structure.building_demand.usage_zone import UsageZone
+import helpers.constants as cte
class SanamCustomizedUsageParameters:
@@ -36,6 +36,11 @@ class SanamCustomizedUsageParameters:
city = self._city
for building in city.buildings:
archetype = self._search_archetype(building.function) # todo: building.function or other translation???????
+ height = building.average_storey_height
+ if height is None:
+ raise Exception('Average storey height not defined, ACH cannot be calculated')
+ if height <= 0:
+ raise Exception('Average storey height is zero, ACH cannot be calculated')
if archetype is None:
sys.stderr.write(f'Building {building.name} has unknown archetype for building function:'
f' {building.function}, that assigns building usage as '
@@ -44,11 +49,8 @@ class SanamCustomizedUsageParameters:
mix_usage = False
if not mix_usage:
# just one usage_zone
- for thermal_zone in building.thermal_zones:
- usage_zone = UsageZone()
- self._assign_values(usage_zone, archetype)
- usage_zone.volume = thermal_zone.volume
- thermal_zone.usage_zones = [usage_zone]
+ for usage_zone in building.usage_zones:
+ self._assign_values(usage_zone, archetype, height)
def _search_archetype(self, building_usage):
for building_archetype in self._usage_archetypes:
@@ -57,20 +59,24 @@ class SanamCustomizedUsageParameters:
return None
@staticmethod
- def _assign_values(usage_zone, archetype):
+ def _assign_values(usage_zone, archetype, height):
usage_zone.usage = archetype.usage
# Due to the fact that python is not a typed language, the wrong object type is assigned to
# usage_zone.internal_gains when writing usage_zone.internal_gains = archetype.internal_gains.
# Therefore, this walk around has been done.
- usage_zone.occupancy_density = archetype.occupancy_density
- usage_zone.mechanical_air_change = archetype.mechanical_air_change
+ if archetype.occupancy_density is not None:
+ usage_zone.occupancy_density = archetype.occupancy_density
+ archetype_mechanical_air_change = float(archetype.mechanical_air_change) * float(usage_zone.occupancy_density) \
+ * cte.HOUR_TO_MINUTES / cte.METERS_TO_FEET**3 / height
+ usage_zone.mechanical_air_change = archetype_mechanical_air_change
@staticmethod
def _parse_zone_usage_type(usage, zone_usage_type):
- occupancy_density = None
+ mechanical_air_change = zone_usage_type['endUses']['ventilation']['minimumVentilationRate']['#text']
if 'occupancy' in zone_usage_type:
- occupancy_density = zone_usage_type['occupancy']['occupancyDensity']
- mechanical_air_change = zone_usage_type['endUses']['ventilation']['minimumVentilationRate']
- usage_zone_archetype = huza(usage=usage, occupancy_density=occupancy_density,
- mechanical_air_change=mechanical_air_change)
+ occupancy_density = zone_usage_type['occupancy']['occupancyDensity']['#text']
+ usage_zone_archetype = huza(usage=usage, occupancy_density=occupancy_density,
+ mechanical_air_change=mechanical_air_change)
+ else:
+ usage_zone_archetype = huza(usage=usage, mechanical_air_change=mechanical_air_change)
return usage_zone_archetype
diff --git a/imports/geometry_factory.py b/imports/geometry_factory.py
index d24199bf..da35c334 100644
--- a/imports/geometry_factory.py
+++ b/imports/geometry_factory.py
@@ -8,7 +8,7 @@ from city_model_structure.city import City
from imports.geometry.citygml import CityGml
from imports.geometry.obj import Obj
from imports.geometry.osm_subway import OsmSubway
-from imports.geometry.rhino import Rhino
+#from imports.geometry.rhino import Rhino
class GeometryFactory:
@@ -43,13 +43,13 @@ class GeometryFactory:
"""
return OsmSubway(self._path).city
- @property
- def _rhino(self) -> City:
- """
- Enrich the city by using OpenStreetMap information as data source
- :return: City
- """
- return Rhino(self._path).city
+# @property
+# def _rhino(self) -> City:
+# """
+# Enrich the city by using OpenStreetMap information as data source
+# :return: City
+# """
+# return Rhino(self._path).city
@property
def city(self) -> City:
@@ -59,10 +59,10 @@ class GeometryFactory:
"""
return getattr(self, self._file_type, lambda: None)
- @property
- def city_debug(self) -> City:
- """
- Enrich the city given to the class using the class given handler
- :return: City
- """
- return Rhino(self._path).city
+# @property
+# def city_debug(self) -> City:
+# """
+# Enrich the city given to the class using the class given handler
+# :return: City
+# """
+# return Rhino(self._path).city
diff --git a/unittests/test_customized_imports_factory.py b/unittests/test_customized_imports_factory.py
index 7b98784a..80373480 100644
--- a/unittests/test_customized_imports_factory.py
+++ b/unittests/test_customized_imports_factory.py
@@ -8,6 +8,7 @@ from unittest import TestCase
from imports.geometry_factory import GeometryFactory
from imports.construction_factory import ConstructionFactory
+from imports.usage_factory import UsageFactory
from imports.customized_imports_factory import CustomizedImportsFactory
from imports.customized_imports.sanam_customized_usage_parameters import SanamCustomizedUsageParameters as scp
@@ -28,6 +29,7 @@ class TestCustomizedImportsFactory(TestCase):
_city = GeometryFactory('citygml', file_path).city
self.assertIsNotNone(_city, 'city is none')
ConstructionFactory('nrel', _city).enrich()
+ UsageFactory('hft', _city).enrich()
return _city