From 9013dda26bf133cfe2cb9a5dc7d0d919add68b8f Mon Sep 17 00:00:00 2001 From: Pilar Date: Mon, 16 May 2022 10:19:03 -0400 Subject: [PATCH] modified usage zone class to hide not_detailed_source_mean_annual_internal_gains --- city_model_structure/attributes/edge.py | 2 +- city_model_structure/attributes/node.py | 2 +- city_model_structure/attributes/schedule.py | 2 +- .../building_demand/internal_zone.py | 2 +- city_model_structure/building_demand/layer.py | 2 +- .../building_demand/thermal_opening.py | 2 +- .../building_demand/thermal_zone.py | 2 +- .../building_demand/usage_zone.py | 102 +++++++++++------- city_model_structure/network.py | 2 +- imports/usage/ca_usage_parameters.py | 50 +-------- imports/usage/hft_usage_interface.py | 41 +++++-- imports/usage/hft_usage_parameters.py | 55 +--------- imports/usage_factory.py | 8 -- unittests/test_usage_factory.py | 17 --- 14 files changed, 113 insertions(+), 176 deletions(-) diff --git a/city_model_structure/attributes/edge.py b/city_model_structure/attributes/edge.py index 9fd6d75b..5a3f048b 100644 --- a/city_model_structure/attributes/edge.py +++ b/city_model_structure/attributes/edge.py @@ -32,7 +32,7 @@ class Edge: @property def id(self): """ - Get edge id, an universally unique identifier randomly generated + Get edge id, a universally unique identifier randomly generated :return: str """ if self._id is None: diff --git a/city_model_structure/attributes/node.py b/city_model_structure/attributes/node.py index 32b82466..df450570 100644 --- a/city_model_structure/attributes/node.py +++ b/city_model_structure/attributes/node.py @@ -34,7 +34,7 @@ class Node: @property def id(self): """ - Get node id, an universally unique identifier randomly generated + Get node id, a universally unique identifier randomly generated :return: str """ if self._id is None: diff --git a/city_model_structure/attributes/schedule.py b/city_model_structure/attributes/schedule.py index 4881fce8..638cd7ab 100644 --- a/city_model_structure/attributes/schedule.py +++ b/city_model_structure/attributes/schedule.py @@ -26,7 +26,7 @@ class Schedule: @property def id(self): """ - Get schedule id, an universally unique identifier randomly generated + Get schedule id, a universally unique identifier randomly generated :return: str """ if self._id is None: diff --git a/city_model_structure/building_demand/internal_zone.py b/city_model_structure/building_demand/internal_zone.py index a4def3d0..8c1d3376 100644 --- a/city_model_structure/building_demand/internal_zone.py +++ b/city_model_structure/building_demand/internal_zone.py @@ -30,7 +30,7 @@ class InternalZone: @property def id(self): """ - Get internal zone id, an universally unique identifier randomly generated + Get internal zone id, a universally unique identifier randomly generated :return: str """ if self._id is None: diff --git a/city_model_structure/building_demand/layer.py b/city_model_structure/building_demand/layer.py index 04dd7596..b7b25983 100644 --- a/city_model_structure/building_demand/layer.py +++ b/city_model_structure/building_demand/layer.py @@ -21,7 +21,7 @@ class Layer: @property def id(self): """ - Get layer id, an universally unique identifier randomly generated + Get layer id, a universally unique identifier randomly generated :return: str """ if self._id is None: diff --git a/city_model_structure/building_demand/thermal_opening.py b/city_model_structure/building_demand/thermal_opening.py index ad3cfd01..84002a08 100644 --- a/city_model_structure/building_demand/thermal_opening.py +++ b/city_model_structure/building_demand/thermal_opening.py @@ -37,7 +37,7 @@ class ThermalOpening: @property def id(self): """ - Get thermal zone id, an universally unique identifier randomly generated + Get thermal zone id, a universally unique identifier randomly generated :return: str """ if self._id is None: diff --git a/city_model_structure/building_demand/thermal_zone.py b/city_model_structure/building_demand/thermal_zone.py index c062d3c3..c93675cb 100644 --- a/city_model_structure/building_demand/thermal_zone.py +++ b/city_model_structure/building_demand/thermal_zone.py @@ -55,7 +55,7 @@ class ThermalZone: @property def id(self): """ - Get thermal zone id, an universally unique identifier randomly generated + Get thermal zone id, a universally unique identifier randomly generated :return: str """ if self._id is None: diff --git a/city_model_structure/building_demand/usage_zone.py b/city_model_structure/building_demand/usage_zone.py index eff1afe8..74ea89f5 100644 --- a/city_model_structure/building_demand/usage_zone.py +++ b/city_model_structure/building_demand/usage_zone.py @@ -12,17 +12,18 @@ 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 from city_model_structure.building_demand.thermal_control import ThermalControl +import helpers.constants as cte class UsageZone: """ UsageZone class """ - def __init__(self): + def __init__(self, not_detailed_source_mean_annual_internal_gains=None): self._id = None self._usage = None self._percentage = None - self._not_detailed_source_mean_annual_internal_gains = None + self._not_detailed_source_mean_annual_internal_gains = not_detailed_source_mean_annual_internal_gains self._hours_day = None self._days_year = None # self._electrical_app_average_consumption_sqm_year = None @@ -31,11 +32,12 @@ class UsageZone: self._lighting = None self._appliances = None self._thermal_control = None + self._internal_gains = None @property def id(self): """ - Get usage zone id, an universally unique identifier randomly generated + Get usage zone id, a universally unique identifier randomly generated :return: str """ if self._id is None: @@ -76,22 +78,6 @@ class UsageZone: if value is not None: self._percentage = float(value) - @property - def not_detailed_source_mean_annual_internal_gains(self) -> List[InternalGain]: - """ - Get usage zone internal gains with unknown energy source - :return: [InternalGain] - """ - return self._not_detailed_source_mean_annual_internal_gains - - @not_detailed_source_mean_annual_internal_gains.setter - def not_detailed_source_mean_annual_internal_gains(self, value): - """ - Set usage zone internal gains with unknown energy source - :param value: [InternalGain] - """ - self._not_detailed_source_mean_annual_internal_gains = value - @property def hours_day(self) -> Union[None, float]: """ @@ -143,23 +129,6 @@ class UsageZone: if value is not None: self._mechanical_air_change = float(value) - @property - def electrical_app_average_consumption_sqm_year(self) -> Union[None, float]: - """ - Get average consumption of electrical appliances in Joules hour per square meter and year (J/m2yr) - :return: None or float - """ - return self._electrical_app_average_consumption_sqm_year - - @electrical_app_average_consumption_sqm_year.setter - def electrical_app_average_consumption_sqm_year(self, value): - """ - Set average consumption of electrical appliances in Joules per square meter and year (J/m2yr) - :param value: float - """ - if value is not None: - self._electrical_app_average_consumption_sqm_year = float(value) - @property def occupancy(self) -> Union[None, Occupancy]: """ @@ -223,3 +192,64 @@ class UsageZone: :param value: ThermalControl """ self._thermal_control = value + + @property + def internal_gains(self) -> List[InternalGain]: + """ + Calculates and returns the list of all internal gains defined + :return: [InternalGain] + """ + if self._not_detailed_source_mean_annual_internal_gains is not None: + self._internal_gains = [] + for _internal_gain in self._not_detailed_source_mean_annual_internal_gains: + self._internal_gains.append(_internal_gain) + + if self.occupancy is not None: + if self.occupancy.latent_internal_gain is not None: + _internal_gain = InternalGain() + _internal_gain.type = cte.OCCUPANCY + _total_heat_gain = (self.occupancy.sensible_convective_internal_gain + + self.occupancy.sensible_radiative_internal_gain + + self.occupancy.latent_internal_gain) + _internal_gain.average_internal_gain = _total_heat_gain + if _total_heat_gain > 0: + _internal_gain.latent_fraction = self.occupancy.latent_internal_gain / _total_heat_gain + _internal_gain.radiative_fraction = self.occupancy.sensible_radiative_internal_gain / _total_heat_gain + _internal_gain.convective_fraction = self.occupancy.sensible_convective_internal_gain / _total_heat_gain + else: + _internal_gain.latent_fraction = 0 + _internal_gain.radiative_fraction = 0 + _internal_gain.convective_fraction = 0 + _internal_gain.schedules = self.occupancy.occupancy_schedules + if self._internal_gains is not None: + self._internal_gains.append(_internal_gain) + else: + self._internal_gains = [_internal_gain] + + if self.lighting is not None: + _internal_gain = InternalGain() + _internal_gain.type = cte.LIGHTING + self._add_internal_gain(self.lighting, _internal_gain) + 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 = InternalGain() + _internal_gain.type = cte.APPLIANCES + self._add_internal_gain(self.appliances, _internal_gain) + if self._internal_gains is not None: + self._internal_gains.append(_internal_gain) + else: + self._internal_gains = [_internal_gain] + + return self._internal_gains + + @staticmethod + def _add_internal_gain(internal_gain_type, _internal_gain): + _internal_gain.average_internal_gain = internal_gain_type.density + _internal_gain.latent_fraction = internal_gain_type.latent_fraction + _internal_gain.radiative_fraction = internal_gain_type.radiative_fraction + _internal_gain.convective_fraction = internal_gain_type.convective_fraction + _internal_gain.schedules = internal_gain_type.schedules diff --git a/city_model_structure/network.py b/city_model_structure/network.py index 77efc492..9c88bb0b 100644 --- a/city_model_structure/network.py +++ b/city_model_structure/network.py @@ -30,7 +30,7 @@ class Network(CityObject): @property def id(self): """ - Get network id, an universally unique identifier randomly generated + Get network id, a universally unique identifier randomly generated :return: str """ if self._id is None: diff --git a/imports/usage/ca_usage_parameters.py b/imports/usage/ca_usage_parameters.py index 56c006a0..cc5c4f41 100644 --- a/imports/usage/ca_usage_parameters.py +++ b/imports/usage/ca_usage_parameters.py @@ -9,14 +9,9 @@ import sys from imports.geometry.helpers.geometry_helper import GeometryHelper from imports.usage.hft_usage_interface import HftUsageInterface from imports.usage.helpers.usage_helper import UsageHelper -from city_model_structure.building_demand.usage_zone import UsageZone -from city_model_structure.building_demand.internal_gain import InternalGain -from city_model_structure.building_demand.occupancy import Occupancy -from city_model_structure.building_demand.appliances import Appliances -from city_model_structure.building_demand.thermal_control import ThermalControl -class CaUsageParameters(HftUsageInterface): +class HftUsageParameters(HftUsageInterface): """ CaUsageParameters class """ @@ -36,48 +31,11 @@ class CaUsageParameters(HftUsageInterface): archetype = self._search_archetype(usage) except KeyError: sys.stderr.write(f'Building {building.name} has unknown archetype for building function:' - f' {building.function}\n') + f' {building.function}, that assigns building usage as ' + f'{GeometryHelper().libs_usage_from_libs_function(building.function)}\n') return for internal_zone in building.internal_zones: - usage_zone = UsageZone() - usage_zone.usage = building.function + usage_zone = self._assign_values(building.function, archetype) usage_zone.percentage = 1 - self._assign_values_usage_zone(usage_zone, archetype) internal_zone.usage_zones = [usage_zone] - - def _search_archetype(self, libs_usage): - building_usage = UsageHelper().hft_from_libs_usage(libs_usage) - for building_archetype in self._usage_archetypes: - if building_archetype.usage == building_usage: - return building_archetype - return None - - @staticmethod - def _assign_values_usage_zone(usage_zone, archetype): - # 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.mechanical_air_change = archetype.mechanical_air_change - _occupancy = Occupancy() - _occupancy.occupancy_density = archetype.occupancy.occupancy_density - usage_zone.occupancy = _occupancy - usage_zone.hours_day = archetype.hours_day - usage_zone.days_year = archetype.days_year - _appliances = Appliances() - _appliances.density = archetype.appliances.density - usage_zone.appliances = _appliances - _control = ThermalControl() - _control.mean_heating_set_point = archetype.thermal_control.mean_heating_set_point - _control.heating_set_back = archetype.thermal_control.heating_set_back - _control.mean_cooling_set_point = archetype.thermal_control.mean_cooling_set_point - usage_zone.thermal_control = _control - _internal_gains = [] - for archetype_internal_gain in archetype.not_detailed_source_mean_annual_internal_gains: - _internal_gain = InternalGain() - _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.not_detailed_source_mean_annual_internal_gains = _internal_gains diff --git a/imports/usage/hft_usage_interface.py b/imports/usage/hft_usage_interface.py index 09593ba7..241cd5a7 100644 --- a/imports/usage/hft_usage_interface.py +++ b/imports/usage/hft_usage_interface.py @@ -14,6 +14,7 @@ from city_model_structure.building_demand.appliances import Appliances from city_model_structure.building_demand.thermal_control import ThermalControl from city_model_structure.attributes.schedule import Schedule import helpers.constants as cte +from imports.usage.helpers.usage_helper import UsageHelper class HftUsageInterface: @@ -38,15 +39,9 @@ class HftUsageInterface: @staticmethod def _parse_zone_usage_type(usage, zone_usage_type): - usage_zone_archetype = UsageZone() - usage_zone_archetype.usage = usage - if 'occupancy' in zone_usage_type: _occupancy = Occupancy() _occupancy.occupancy_density = zone_usage_type['occupancy']['occupancyDensity'] #todo: check units - usage_zone_archetype.hours_day = zone_usage_type['occupancy']['usageHoursPerDay'] - usage_zone_archetype.days_year = zone_usage_type['occupancy']['usageDaysPerYear'] - usage_zone_archetype.occupancy = _occupancy if 'internGains' in zone_usage_type['occupancy']: _internal_gain = InternalGain() @@ -72,7 +67,18 @@ class HftUsageInterface: _schedule.values = _values_float _internal_gain.schedules = [_schedule] - usage_zone_archetype.not_detailed_source_mean_annual_internal_gains = [_internal_gain] + _not_detailed_source_mean_annual_internal_gains = [_internal_gain] + usage_zone_archetype = UsageZone(_not_detailed_source_mean_annual_internal_gains) + else: + usage_zone_archetype = UsageZone() + + usage_zone_archetype.hours_day = zone_usage_type['occupancy']['usageHoursPerDay'] + usage_zone_archetype.days_year = zone_usage_type['occupancy']['usageDaysPerYear'] + usage_zone_archetype.occupancy = _occupancy + + else: + usage_zone_archetype = UsageZone() + usage_zone_archetype.usage = usage if 'endUses' in zone_usage_type: _thermal_control = ThermalControl() @@ -256,3 +262,24 @@ class HftUsageInterface: usage_zone_archetype.appliances = _appliances return usage_zone_archetype + + def _search_archetype(self, libs_usage): + building_usage = UsageHelper().hft_from_libs_usage(libs_usage) + for building_archetype in self._usage_archetypes: + if building_archetype.usage == building_usage: + return building_archetype + return None + + @staticmethod + def _assign_values(usage, archetype): + _not_detailed_source_mean_annual_internal_gains = \ + copy.deepcopy(archetype.internal_gains) + usage_zone = UsageZone(_not_detailed_source_mean_annual_internal_gains) + usage_zone.usage = usage + usage_zone.mechanical_air_change = archetype.mechanical_air_change + usage_zone.occupancy = copy.deepcopy(archetype.occupancy) + usage_zone.appliances = copy.deepcopy(archetype.appliances) + usage_zone.thermal_control = copy.deepcopy(archetype.thermal_control) + usage_zone.days_year = archetype.days_year + usage_zone.hours_day = archetype.hours_day + return usage_zone diff --git a/imports/usage/hft_usage_parameters.py b/imports/usage/hft_usage_parameters.py index c5faee4d..afe7e300 100644 --- a/imports/usage/hft_usage_parameters.py +++ b/imports/usage/hft_usage_parameters.py @@ -5,12 +5,10 @@ Copyright © 2022 Concordia CERC group Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ import sys -import copy from imports.geometry.helpers.geometry_helper import GeometryHelper from imports.usage.hft_usage_interface import HftUsageInterface from imports.usage.helpers.usage_helper import UsageHelper -from city_model_structure.building_demand.usage_zone import UsageZone class HftUsageParameters(HftUsageInterface): @@ -38,58 +36,7 @@ class HftUsageParameters(HftUsageInterface): return for internal_zone in building.internal_zones: - usage_zone = UsageZone() libs_usage = GeometryHelper().libs_usage_from_libs_function(building.function) - usage_zone.usage = UsageHelper().hft_from_libs_usage(libs_usage) - self._assign_values(usage_zone, archetype) + usage_zone = self._assign_values(UsageHelper().hft_from_libs_usage(libs_usage), archetype) usage_zone.percentage = 1 internal_zone.usage_zones = [usage_zone] - - def _search_archetype(self, libs_usage): - building_usage = UsageHelper().hft_from_libs_usage(libs_usage) - for building_archetype in self._usage_archetypes: - if building_archetype.usage == building_usage: - return building_archetype - return None - - @staticmethod - def _assign_values(usage_zone, archetype): - """ # 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. - # Due to the fact that python is not a typed language, the wrong object type is assigned to - # usage_zone.occupancy when writing usage_zone.occupancy = archetype.occupancy. - # Same happens for lighting and appliances. Therefore, this walk around has been done. - usage_zone.mechanical_air_change = archetype.mechanical_air_change - _occupancy = Occupancy() - _occupancy.occupancy_density = archetype.occupancy.occupancy_density - usage_zone.occupancy = _occupancy - _appliances = Appliances() - _appliances.appliances_density = archetype.appliances.appliances_density - usage_zone.appliances = _appliances - _control = ThermalControl() - _control.mean_heating_set_point = archetype.thermal_control.mean_heating_set_point - _control.heating_set_back = archetype.thermal_control.heating_set_back - _control.mean_cooling_set_point = archetype.thermal_control.mean_cooling_set_point - _control.cooling_set_point_schedules = archetype.thermal_control.cooling_set_point_schedules - _control.heating_set_point_schedules = archetype.thermal_control.heating_set_point_schedules - usage_zone.thermal_control = _control - _internal_gains = [] - for archetype_internal_gain in archetype.not_detailed_source_mean_annual_internal_gains: - _internal_gain = InternalGain() - _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_gain.schedules = archetype_internal_gain.schedules - _internal_gains.append(_internal_gain) - usage_zone.not_detailed_source_mean_annual_internal_gains = _internal_gains - """ - usage_zone.mechanical_air_change = archetype.mechanical_air_change - usage_zone.occupancy = copy.deepcopy(archetype.occupancy) - usage_zone.appliances = copy.deepcopy(archetype.appliances) - usage_zone.thermal_control = copy.deepcopy(archetype.thermal_control) - usage_zone.not_detailed_source_mean_annual_internal_gains = \ - copy.deepcopy(archetype.not_detailed_source_mean_annual_internal_gains) - usage_zone.days_year = archetype.days_year - usage_zone.hours_day = archetype.hours_day diff --git a/imports/usage_factory.py b/imports/usage_factory.py index ad446857..69ec4922 100644 --- a/imports/usage_factory.py +++ b/imports/usage_factory.py @@ -8,9 +8,7 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord """ from pathlib import Path from imports.usage.hft_usage_parameters import HftUsageParameters -from imports.usage.ca_usage_parameters import CaUsageParameters from imports.usage.comnet_usage_parameters import ComnetUsageParameters -# todo: handle missing lambda and rise error. class UsageFactory: @@ -30,12 +28,6 @@ class UsageFactory: """ return HftUsageParameters(self._city, self._base_path).enrich_buildings() - def _ca(self): - """ - Enrich the city with Canada usage library - """ - return CaUsageParameters(self._city, self._base_path).enrich_buildings() - def _comnet(self): """ Enrich the city with COMNET usage library diff --git a/unittests/test_usage_factory.py b/unittests/test_usage_factory.py index 93408819..15138579 100644 --- a/unittests/test_usage_factory.py +++ b/unittests/test_usage_factory.py @@ -127,23 +127,6 @@ class TestUsageFactory(TestCase): self.assertIsNotNone(usage_zone.thermal_control.hvac_availability_schedules, 'control hvac availability is none') - def test_import_ca(self): - """ - Enrich the city with the usage information from canada and verify it - """ - file = 'one_building_in_kelowna.gml' - city = self._get_citygml(file) - UsageFactory('ca', city).enrich() - self._check_buildings(city) - for building in city.buildings: - for internal_zone in building.internal_zones: - self.assertIsNot(len(internal_zone.usage_zones), 0, 'no building usage_zones defined') - for usage_zone in internal_zone.usage_zones: - self._check_usage_zone(usage_zone) - self.assertIsNotNone(usage_zone.mechanical_air_change, 'mechanical air change is none') - self.assertIsNotNone(usage_zone.not_detailed_source_mean_annual_internal_gains, - 'not detailed internal gains is none') - def test_import_hft(self): """ Enrich the city with the usage information from hft and verify it