From 684238a3ff1551ec6795bb505647e931bc39d007 Mon Sep 17 00:00:00 2001 From: Pilar Date: Wed, 6 Apr 2022 16:06:55 -0400 Subject: [PATCH 1/2] Thermal zone concept modified. Now it includes all usage parameters associated to that thermal zone --- city_model_structure/building.py | 1 - .../building_demand/appliances.py | 14 +- .../building_demand/internal_gains.py | 2 + .../building_demand/internal_zone.py | 1 - .../building_demand/lighting.py | 14 +- .../building_demand/occupancy.py | 6 +- .../building_demand/storey.py | 5 +- .../building_demand/thermal_control.py | 6 + .../building_demand/thermal_zone.py | 518 ++++++++++++++++-- .../building_demand/usage_zone.py | 46 -- exports/exports_factory.py | 9 + exports/formats/idf.py | 115 ++-- .../helpers/storeys_generation.py | 10 +- .../construction/nrel_physics_interface.py | 2 +- .../sanam_customized_usage_parameters.py | 4 +- imports/schedules/doe_idf.py | 5 + imports/usage/ca_usage_parameters.py | 2 +- imports/usage/comnet_usage_parameters.py | 33 +- imports/usage/hft_usage_interface.py | 4 +- requirements.txt | 2 - unittests/test_doe_idf.py | 6 +- unittests/test_usage_factory.py | 5 +- 22 files changed, 618 insertions(+), 192 deletions(-) diff --git a/city_model_structure/building.py b/city_model_structure/building.py index bab214a1..b688ce57 100644 --- a/city_model_structure/building.py +++ b/city_model_structure/building.py @@ -30,7 +30,6 @@ class Building(CityObject): self._storeys_above_ground = None self._floor_area = None self._roof_type = None - self._storeys = None self._internal_zones = None self._shell = None self._type = 'building' diff --git a/city_model_structure/building_demand/appliances.py b/city_model_structure/building_demand/appliances.py index 8fd4f0f6..387c95ca 100644 --- a/city_model_structure/building_demand/appliances.py +++ b/city_model_structure/building_demand/appliances.py @@ -12,28 +12,28 @@ class Appliances: Appliances class """ def __init__(self): - self._appliances_density = None + self._density = None self._convective_fraction = None self._radiative_fraction = None self._latent_fraction = None self._schedules = None @property - def appliances_density(self) -> Union[None, float]: + def density(self) -> Union[None, float]: """ Get appliances density in Watts per m2 :return: None or float """ - return self._appliances_density + return self._density - @appliances_density.setter - def appliances_density(self, value): + @density.setter + def density(self, value): """ Set appliances density in Watts per m2 :param value: float """ if value is not None: - self._appliances_density = float(value) + self._density = float(value) @property def convective_fraction(self) -> Union[None, float]: @@ -90,6 +90,7 @@ class Appliances: def schedules(self) -> Union[None, List[Schedule]]: """ Get schedules + dataType = fraction :return: None or [Schedule] """ return self._schedules @@ -98,6 +99,7 @@ class Appliances: def schedules(self, value): """ Set schedules + dataType = fraction :param value: [Schedule] """ self._schedules = value diff --git a/city_model_structure/building_demand/internal_gains.py b/city_model_structure/building_demand/internal_gains.py index 9384cf21..ef8e671f 100644 --- a/city_model_structure/building_demand/internal_gains.py +++ b/city_model_structure/building_demand/internal_gains.py @@ -110,6 +110,7 @@ class InternalGains: def schedules(self) -> Union[None, List[Schedule]]: """ Get internal gain schedule + dataType = fraction :return: [Schedule] """ return self._schedules @@ -118,6 +119,7 @@ class InternalGains: def schedules(self, value): """ Set internal gain schedule + dataType = fraction :param value: [Schedule] """ self._schedules = value diff --git a/city_model_structure/building_demand/internal_zone.py b/city_model_structure/building_demand/internal_zone.py index 74678473..95a427b5 100644 --- a/city_model_structure/building_demand/internal_zone.py +++ b/city_model_structure/building_demand/internal_zone.py @@ -120,4 +120,3 @@ class InternalZone: :param value: [ThermalZone] """ self._thermal_zones = value - diff --git a/city_model_structure/building_demand/lighting.py b/city_model_structure/building_demand/lighting.py index 0e72249f..6ba7916a 100644 --- a/city_model_structure/building_demand/lighting.py +++ b/city_model_structure/building_demand/lighting.py @@ -12,28 +12,28 @@ class Lighting: Lighting class """ def __init__(self): - self._lighting_density = None + self._density = None self._convective_fraction = None self._radiative_fraction = None self._latent_fraction = None self._schedules = None @property - def lighting_density(self) -> Union[None, float]: + def density(self) -> Union[None, float]: """ Get lighting density in Watts per m2 :return: None or float """ - return self._lighting_density + return self._density - @lighting_density.setter - def lighting_density(self, value): + @density.setter + def density(self, value): """ Set lighting density in Watts per m2 :param value: float """ if value is not None: - self._lighting_density = float(value) + self._density = float(value) @property def convective_fraction(self) -> Union[None, float]: @@ -90,6 +90,7 @@ class Lighting: def schedules(self) -> Union[None, List[Schedule]]: """ Get schedules + dataType = fraction :return: None or [Schedule] """ return self._schedules @@ -98,6 +99,7 @@ class Lighting: def schedules(self, value): """ Set schedules + dataType = fraction :param value: [Schedule] """ self._schedules = value diff --git a/city_model_structure/building_demand/occupancy.py b/city_model_structure/building_demand/occupancy.py index ec9ebf1e..0b613404 100644 --- a/city_model_structure/building_demand/occupancy.py +++ b/city_model_structure/building_demand/occupancy.py @@ -23,7 +23,7 @@ class Occupancy: @property def occupancy_density(self) -> Union[None, float]: """ - Get density in m2 per person + Get density in persons per m2 :return: None or float """ return self._occupancy_density @@ -31,7 +31,7 @@ class Occupancy: @occupancy_density.setter def occupancy_density(self, value): """ - Set density in m2 per persons + Set density in persons per m2 :param value: float """ if value is not None: @@ -92,6 +92,7 @@ class Occupancy: def occupancy_schedules(self) -> Union[None, List[Schedule]]: """ Get occupancy schedules + dataType = fraction :return: None or [Schedule] """ return self._occupancy_schedules @@ -100,6 +101,7 @@ class Occupancy: def occupancy_schedules(self, value): """ Set occupancy schedules + dataType = fraction :param value: [Schedule] """ self._occupancy_schedules = value diff --git a/city_model_structure/building_demand/storey.py b/city_model_structure/building_demand/storey.py index 143eec29..a99a7304 100644 --- a/city_model_structure/building_demand/storey.py +++ b/city_model_structure/building_demand/storey.py @@ -15,7 +15,7 @@ class Storey: """ Storey class """ - def __init__(self, name, storey_surfaces, neighbours, volume, floor_area): + def __init__(self, name, storey_surfaces, neighbours, volume, internal_zone, floor_area): self._name = name self._storey_surfaces = storey_surfaces self._thermal_boundaries = None @@ -23,6 +23,7 @@ class Storey: self._thermal_zone = None self._neighbours = neighbours self._volume = volume + self._internal_zone = internal_zone self._floor_area = floor_area @property @@ -86,7 +87,7 @@ class Storey: :return: ThermalZone """ if self._thermal_zone is None: - self._thermal_zone = ThermalZone(self.thermal_boundaries, self.volume, self.floor_area) + self._thermal_zone = ThermalZone(self.thermal_boundaries, self._internal_zone, self.volume, self.floor_area) return self._thermal_zone @property diff --git a/city_model_structure/building_demand/thermal_control.py b/city_model_structure/building_demand/thermal_control.py index 0c4c8a62..41f88bb6 100644 --- a/city_model_structure/building_demand/thermal_control.py +++ b/city_model_structure/building_demand/thermal_control.py @@ -99,6 +99,7 @@ class ThermalControl: def hvac_availability_schedules(self) -> Union[None, List[Schedule]]: """ Get the availability of the conditioning system defined for a thermal zone + dataType = on/off :return: None or [Schedule] """ return self._hvac_availability_schedules @@ -107,6 +108,7 @@ class ThermalControl: def hvac_availability_schedules(self, value): """ Set the availability of the conditioning system defined for a thermal zone + dataType = on/off :param value: [Schedule] """ self._hvac_availability_schedules = value @@ -115,6 +117,7 @@ class ThermalControl: def heating_set_point_schedules(self) -> Union[None, List[Schedule]]: """ Get heating set point schedule defined for a thermal zone in Celsius + dataType = temperature :return: None or [Schedule] """ return self._heating_set_point_schedules @@ -123,6 +126,7 @@ class ThermalControl: def heating_set_point_schedules(self, value): """ Set heating set point schedule defined for a thermal zone in Celsius + dataType = temperature :param value: [Schedule] """ self._heating_set_point_schedules = value @@ -131,6 +135,7 @@ class ThermalControl: def cooling_set_point_schedules(self) -> Union[None, List[Schedule]]: """ Get cooling set point schedule defined for a thermal zone in Celsius + dataType = temperature :return: None or [Schedule] """ return self._cooling_set_point_schedules @@ -139,6 +144,7 @@ class ThermalControl: def cooling_set_point_schedules(self, value): """ Set cooling set point schedule defined for a thermal zone in Celsius + dataType = temperature :param value: [Schedule] """ self._cooling_set_point_schedules = value diff --git a/city_model_structure/building_demand/thermal_zone.py b/city_model_structure/building_demand/thermal_zone.py index 10f8ac0b..2b4b016e 100644 --- a/city_model_structure/building_demand/thermal_zone.py +++ b/city_model_structure/building_demand/thermal_zone.py @@ -5,21 +5,28 @@ Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc Contributors Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ import uuid +import copy from typing import List, Union, TypeVar -from city_model_structure.building_demand.usage_zone import UsageZone +from city_model_structure.building_demand.occupancy import Occupancy +from city_model_structure.building_demand.appliances import Appliances +from city_model_structure.building_demand.lighting import Lighting +from city_model_structure.building_demand.internal_gains import InternalGains from city_model_structure.attributes.schedule import Schedule from city_model_structure.building_demand.thermal_control import ThermalControl from city_model_structure.energy_systems.hvac_system import HvacSystem +import helpers.constants as cte ThermalBoundary = TypeVar('ThermalBoundary') +InternalZone = TypeVar('InternalZone') class ThermalZone: """ ThermalZone class """ - def __init__(self, thermal_boundaries, volume, floor_area): + def __init__(self, thermal_boundaries, parent_internal_zone, volume, floor_area): self._id = None + self._parent_internal_zone = parent_internal_zone self._floor_area = floor_area self._thermal_boundaries = thermal_boundaries self._additional_thermal_bridge_u_value = None @@ -29,11 +36,19 @@ class ThermalZone: self._infiltration_rate_system_off = None self._volume = volume self._ordinate_number = None - self._usage_zones = None - self._thermal_control = None - self._hvac_system = None self._view_factors_matrix = None + self._usage = None + self._not_detailed_source_mean_annual_internal_gains = None + self._hours_day = None + self._days_year = None + self._mechanical_air_change = None + self._occupancy = None + self._lighting = None + self._appliances = None + self._internal_gains = None + self._thermal_control = None + @property def id(self): """ @@ -168,42 +183,6 @@ class ThermalZone: if value is not None: self._ordinate_number = int(value) - @property - def usage_zones(self) -> [UsageZone]: - """ - Get list of usage zones and the percentage of thermal zone's volume affected by that usage - From internal_zone - :return: [UsageZone] - """ - return self._usage_zones - - @usage_zones.setter - def usage_zones(self, values): - """ - Set list of usage zones and the percentage of thermal zone's volume affected by that usage - From internal_zone - :param values: [UsageZone] - """ - self._usage_zones = values - - @property - def thermal_control(self) -> Union[None, ThermalControl]: - """ - Get thermal control of this thermal zone - From internal_zone - :return: None or ThermalControl - """ - return self._thermal_control - - @thermal_control.setter - def thermal_control(self, value): - """ - Set thermal control for this thermal zone - From internal_zone - :param value: ThermalControl - """ - self._thermal_control = value - @property def hvac_system(self) -> Union[None, HvacSystem]: """ @@ -211,16 +190,7 @@ class ThermalZone: From internal_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 - From internal_zone - :param value: HvacSystem - """ - self._hvac_system = value + return self._parent_internal_zone.hvac_system @property def view_factors_matrix(self): @@ -237,3 +207,449 @@ class ThermalZone: :param value: [[float]] """ self._view_factors_matrix = value + + @property + def usage(self) -> Union[None, str]: + """ + Get thermal zone usage + :return: None or str + """ + if self._parent_internal_zone.usage_zones is None: + return None + if self._usage is None: + self._usage = '' + for usage_zone in self._parent_internal_zone.usage_zones: + self._usage += str(round(usage_zone.percentage) * 100) + '_' + usage_zone.usage + '_' + self._usage = self._usage[:-1] + return self._usage + + @usage.setter + def usage(self, value): + """ + Set thermal zone usage + :param value: str + """ + if value is not None: + self._usage = str(value) + + @staticmethod + def _get_schedule_of_day(requested_day_type, schedules): + for schedule in schedules: + for day_type in schedule.day_types: + if day_type == requested_day_type: + return schedule + else: + return None + + @property + def not_detailed_source_mean_annual_internal_gains(self) -> Union[None, List[InternalGains]]: + """ + Get thermal zone internal gains with unknown energy source + :return: [InternalGains] + """ + if self._parent_internal_zone.usage_zones is None: + return None + if self._not_detailed_source_mean_annual_internal_gains is None: + _grouped_internal_gain = InternalGains() + _grouped_internal_gain.type = 'grouped internal gains for thermal zone' + _average_internal_gain = 0 + _convective_part = 0 + _radiative_part = 0 + _latent_part = 0 + for _usage_zone in self._parent_internal_zone.usage_zones: + if _usage_zone.internal_gains is None: + return None + for _internal_gain in _usage_zone.internal_gains: + _average_internal_gain += _internal_gain.average_internal_gain + _convective_part += _internal_gain.average_internal_gain * _internal_gain.convective_fraction + _radiative_part += _internal_gain.average_internal_gain * _internal_gain.radiative_fraction + _latent_part += _internal_gain.average_internal_gain * _internal_gain.latent_fraction + + _grouped_internal_gain.average_internal_gain = _average_internal_gain + if _average_internal_gain > 0: + _grouped_internal_gain.convective_fraction = _convective_part / _average_internal_gain + _grouped_internal_gain.radiative_fraction = _radiative_part / _average_internal_gain + _grouped_internal_gain.latent_fraction = _latent_part / _average_internal_gain + _internal_gains_reference = self._parent_internal_zone.usage_zones[0].internal_gains[0] + day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY, cte.SATURDAY, cte.SUNDAY] + schedule_of_days = [] + for i_day in range(0, 7): + schedule_of_day = \ + copy.deepcopy(self._get_schedule_of_day(day_types[i_day], _internal_gains_reference.schedules)) + schedule_of_day.day_types = [day_types[i_day]] + new_values = [] + for i_value in range(0, len(_internal_gains_reference.schedules[0].values)): + _new_value = 0 + for _usage_zone in self._parent_internal_zone.usage_zones: + for _internal_gain in _usage_zone.internal_gains: + _value = self._get_schedule_of_day(day_types[i_day], _internal_gain.schedules)[i_value] + _new_value += _usage_zone.percentage * _value / len(_usage_zone.internal_gains) + new_values.append(_new_value) + schedule_of_day.values = new_values + schedule_of_days.append(schedule_of_day) + _grouped_internal_gain.schedules = schedule_of_days + else: + _grouped_internal_gain.convective_fraction = 0 + _grouped_internal_gain.radiative_fraction = 0 + _grouped_internal_gain.latent_fraction = 0 + + self._not_detailed_source_mean_annual_internal_gains = [_grouped_internal_gain] + 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 thermal zone internal gains with unknown energy source + :param value: [InternalGains] + """ + self._not_detailed_source_mean_annual_internal_gains = value + + @property + def hours_day(self) -> Union[None, float]: + """ + Get thermal zone usage hours per day + :return: None or float + """ + if self._parent_internal_zone.usage_zones is None: + return None + if self._hours_day is None: + self._hours_day = 0 + for usage_zone in self._parent_internal_zone.usage_zones: + self._hours_day += usage_zone.percentage * usage_zone.hours_day + return self._hours_day + + @hours_day.setter + def hours_day(self, value): + """ + Set thermal zone usage hours per day + :param value: float + """ + if value is not None: + self._hours_day = float(value) + + @property + def days_year(self) -> Union[None, float]: + """ + Get thermal zone usage days per year + :return: None or float + """ + if self._parent_internal_zone.usage_zones is None: + return None + if self._days_year is None: + self._days_year = 0 + for usage_zone in self._parent_internal_zone.usage_zones: + self._days_year += usage_zone.percentage * usage_zone.days_year + return self._days_year + + @days_year.setter + def days_year(self, value): + """ + Set thermal zone usage days per year + :param value: float + """ + if value is not None: + self._days_year = float(value) + + @property + def mechanical_air_change(self) -> Union[None, float]: + """ + Get thermal zone mechanical air change in air change per hour (ACH) + :return: None or float + """ + if self._parent_internal_zone.usage_zones is None: + return None + if self._mechanical_air_change is None: + self._mechanical_air_change = 0 + for usage_zone in self._parent_internal_zone.usage_zones: + self._mechanical_air_change += usage_zone.percentage * usage_zone.mechanical_air_change + return self._mechanical_air_change + + @mechanical_air_change.setter + def mechanical_air_change(self, value): + """ + Set thermal zone mechanical air change in air change per hour (ACH) + :param value: float + """ + if value is not None: + self._mechanical_air_change = float(value) + + @property + def occupancy(self) -> Union[None, Occupancy]: + """ + Get occupancy in the thermal zone + :return: None or Occupancy + """ + if self._parent_internal_zone.usage_zones is None: + return None + if self._occupancy is None: + self._occupancy = Occupancy() + _occupancy_density = 0 + _convective_part = 0 + _radiative_part = 0 + _latent_part = 0 + for usage_zone in self._parent_internal_zone.usage_zones: + if usage_zone.occupancy is None: + return None + _occupancy_density += usage_zone.percentage * usage_zone.occupancy.occupancy_density + _convective_part += usage_zone.percentage * usage_zone.occupancy.sensible_convective_internal_gain + _radiative_part += usage_zone.percentage * usage_zone.occupancy.sensible_radiative_internal_gain + _latent_part += usage_zone.percentage * usage_zone.occupancy.latent_internal_gain + self._occupancy.occupancy_density = _occupancy_density + self._occupancy.sensible_convective_internal_gain = _convective_part + self._occupancy.sensible_radiative_internal_gain = _radiative_part + self._occupancy.latent_internal_gain = _latent_part + + _occupancy_reference = self._parent_internal_zone.usage_zones[0].occupancy + _schedules = [] + for i_schedule in range(0, len(_occupancy_reference.occupancy_schedules)): + schedule = copy.deepcopy(_occupancy_reference.occupancy_schedules[i_schedule]) + new_values = [] + for i_value in range(0, len(_occupancy_reference.occupancy_schedules[i_schedule].values)): + _new_value = 0 + for usage_zone in self._parent_internal_zone.usage_zones: + _new_value += usage_zone.percentage * usage_zone.occupancy.occupancy_schedules[i_schedule].values[i_value] + new_values.append(_new_value) + schedule.values = new_values + _schedules.append(schedule) + self._occupancy.occupancy_schedules = _schedules + return self._occupancy + + @occupancy.setter + def occupancy(self, value): + """ + Set occupancy in the thermal zone + :param value: Occupancy + """ + self._occupancy = value + + @property + def lighting(self) -> Union[None, Lighting]: + """ + Get lighting information + :return: None or Lighting + """ + if self._parent_internal_zone.usage_zones is None: + return None + if self._lighting is None: + self._lighting = Lighting() + _lighting_density = 0 + _convective_part = 0 + _radiative_part = 0 + _latent_part = 0 + for usage_zone in self._parent_internal_zone.usage_zones: + if usage_zone.lighting is None: + return None + _lighting_density += usage_zone.percentage * usage_zone.lighting.density + _convective_part += usage_zone.percentage * usage_zone.lighting.density \ + * usage_zone.lighting.convective_fraction + _radiative_part += usage_zone.percentage * usage_zone.lighting.density \ + * usage_zone.lighting.radiative_fraction + _latent_part += usage_zone.percentage * usage_zone.lighting.density \ + * usage_zone.lighting.latent_fraction + self._lighting.density = _lighting_density + if _lighting_density > 0: + self._lighting.convective_fraction = _convective_part / _lighting_density + self._lighting.radiative_fraction = _radiative_part / _lighting_density + self._lighting.latent_fraction = _latent_part / _lighting_density + else: + self._lighting.convective_fraction = 0 + self._lighting.radiative_fraction = 0 + self._lighting.latent_fraction = 0 + + _lighting_reference = self._parent_internal_zone.usage_zones[0].lighting + _schedules = [] + for i_schedule in range(0, len(_lighting_reference.schedules)): + schedule = copy.deepcopy(_lighting_reference.schedules[i_schedule]) + new_values = [] + for i_value in range(0, len(_lighting_reference.schedules[i_schedule].values)): + _new_value = 0 + for usage_zone in self._parent_internal_zone.usage_zones: + _new_value += usage_zone.percentage * usage_zone.lighting.schedules[i_schedule].values[i_value] + new_values.append(_new_value) + schedule.values = new_values + _schedules.append(schedule) + self._lighting.schedules = _schedules + return self._lighting + + @lighting.setter + def lighting(self, value): + """ + Set lighting information + :param value: Lighting + """ + self._lighting = value + + @property + def appliances(self) -> Union[None, Appliances]: + """ + Get appliances information + :return: None or Appliances + """ + if self._parent_internal_zone.usage_zones is None: + return None + if self._appliances is None: + self._appliances = Appliances() + _appliances_density = 0 + _convective_part = 0 + _radiative_part = 0 + _latent_part = 0 + for usage_zone in self._parent_internal_zone.usage_zones: + if usage_zone.appliances is None: + return None + _appliances_density += usage_zone.percentage * usage_zone.appliances.density + _convective_part += usage_zone.percentage * usage_zone.appliances.density \ + * usage_zone.appliances.convective_fraction + _radiative_part += usage_zone.percentage * usage_zone.appliances.density \ + * usage_zone.appliances.radiative_fraction + _latent_part += usage_zone.percentage * usage_zone.appliances.density \ + * usage_zone.appliances.latent_fraction + self._appliances.density = _appliances_density + if _appliances_density > 0: + self._appliances.convective_fraction = _convective_part / _appliances_density + self._appliances.radiative_fraction = _radiative_part / _appliances_density + self._appliances.latent_fraction = _latent_part / _appliances_density + else: + self._appliances.convective_fraction = 0 + self._appliances.radiative_fraction = 0 + self._appliances.latent_fraction = 0 + + _appliances_reference = self._parent_internal_zone.usage_zones[0].appliances + _schedules = [] + for i_schedule in range(0, len(_appliances_reference.schedules)): + schedule = copy.deepcopy(_appliances_reference.schedules[i_schedule]) + new_values = [] + for i_value in range(0, len(_appliances_reference.schedules[i_schedule].values)): + _new_value = 0 + for usage_zone in self._parent_internal_zone.usage_zones: + _new_value += usage_zone.percentage * usage_zone.appliances.schedules[i_schedule].values[i_value] + new_values.append(_new_value) + schedule.values = new_values + _schedules.append(schedule) + self._appliances.schedules = _schedules + return self._appliances + + @appliances.setter + def appliances(self, value): + """ + Set appliances information + :param value: Appliances + """ + self._appliances = value + + @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 + + def get_internal_gains(self) -> [InternalGains]: + """ + Calculates and returns the list of all internal gains defined + :return: InternalGains + """ + 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 = InternalGains() + _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 + _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 + _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 = InternalGains() + _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 = InternalGains() + _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 + + @property + def thermal_control(self) -> Union[None, ThermalControl]: + """ + Get thermal control of this thermal zone + :return: None or ThermalControl + """ + if self._parent_internal_zone.usage_zones is None: + return None + if self._thermal_control is None: + self._thermal_control = ThermalControl() + _mean_heating_set_point = 0 + _heating_set_back = 0 + _mean_cooling_set_point = 0 + for usage_zone in self._parent_internal_zone.usage_zones: + _mean_heating_set_point += usage_zone.percentage * usage_zone.thermal_control.mean_heating_set_point + _heating_set_back += usage_zone.percentage * usage_zone.thermal_control.heating_set_back + _mean_cooling_set_point += usage_zone.percentage * usage_zone.thermal_control.mean_cooling_set_point + self._thermal_control.mean_heating_set_point = _mean_heating_set_point + self._thermal_control.heating_set_back = _heating_set_back + self._thermal_control.mean_cooling_set_point = _mean_cooling_set_point + + _thermal_control_reference = self._parent_internal_zone.usage_zones[0].thermal_control + _types_reference = [_thermal_control_reference.hvac_availability_schedules, + _thermal_control_reference.heating_set_point_schedules, + _thermal_control_reference.cooling_set_point_schedules] + for i_type in range(0, len(_types_reference)): + _schedules = [] + _schedule_type = _types_reference[i_type] + for i_schedule in range(0, len(_schedule_type)): + schedule = copy.deepcopy(_schedule_type[i_schedule]) + new_values = [] + for i_value in range(0, len(_schedule_type[i_schedule].values)): + _new_value = 0 + for usage_zone in self._parent_internal_zone.usage_zones: + if i_type == 0: + _new_value += usage_zone.percentage * \ + usage_zone.thermal_control.hvac_availability_schedules[i_schedule].values[i_value] + elif i_type == 1: + _new_value += usage_zone.percentage * \ + usage_zone.thermal_control.heating_set_point_schedules[i_schedule].values[i_value] + elif i_type == 2: + _new_value += usage_zone.percentage * \ + usage_zone.thermal_control.cooling_set_point_schedules[i_schedule].values[i_value] + new_values.append(_new_value) + schedule.values = new_values + _schedules.append(schedule) + if i_type == 0: + self._thermal_control.hvac_availability_schedules = _schedules + elif i_type == 1: + self._thermal_control.heating_set_point_schedules = _schedules + elif i_type == 2: + self._thermal_control.cooling_set_point_schedules = _schedules + + 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 diff --git a/city_model_structure/building_demand/usage_zone.py b/city_model_structure/building_demand/usage_zone.py index f8d77890..95780733 100644 --- a/city_model_structure/building_demand/usage_zone.py +++ b/city_model_structure/building_demand/usage_zone.py @@ -6,7 +6,6 @@ Contributors Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ import uuid 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 @@ -30,7 +29,6 @@ class UsageZone: self._occupancy = None self._lighting = None self._appliances = None - self._internal_gains = None self._thermal_control = None @property @@ -209,50 +207,6 @@ class UsageZone: """ self._appliances = value - def get_internal_gains(self) -> [InternalGains]: - """ - Calculates and returns the list of all internal gains defined - :return: InternalGains - """ - 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_radiative_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_radiative_internal_gain / _total_heat_gain - _internal_gain.convective_fraction = self.occupancy.sensible_convective_internal_gain / _total_heat_gain - _internal_gain.schedules = self.occupancy.occupancy_schedules - 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.radiative_fraction - _internal_gain.convective_fraction = self.lighting.convective_fraction - _internal_gain.schedules = self.lighting.schedules - 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.appliances_density - _internal_gain.latent_fraction = self.appliances.latent_fraction - _internal_gain.radiative_fraction = self.appliances.radiative_fraction - _internal_gain.convective_fraction = self.appliances.convective_fraction - _internal_gain.schedules = self.appliances.schedules - if self._internal_gains is not None: - self._internal_gains.append(_internal_gain) - else: - self._internal_gains = [_internal_gain] - return self._internal_gains - @property def thermal_control(self) -> Union[None, ThermalControl]: """ diff --git a/exports/exports_factory.py b/exports/exports_factory.py index 6ea6b18b..91eaa14d 100644 --- a/exports/exports_factory.py +++ b/exports/exports_factory.py @@ -19,6 +19,8 @@ class ExportsFactory: def __init__(self, export_type, city, path, target_buildings=None): self._city = city self._export_type = '_' + export_type.lower() + if isinstance(path, str): + path = Path(path) self._path = path self._target_buildings = target_buildings @@ -92,3 +94,10 @@ class ExportsFactory: :return: None """ return getattr(self, self._export_type, lambda: None) + + def export_debug(self): + """ + Export the city given to the class using the given export type handler + :return: None + """ + return getattr(self, self._export_type) diff --git a/exports/formats/idf.py b/exports/formats/idf.py index 54e899d3..3eaa009f 100644 --- a/exports/formats/idf.py +++ b/exports/formats/idf.py @@ -1,10 +1,13 @@ """ TestOccupancyFactory test and validate the city model structure schedules parameters SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Soroush Samareh Abolhassani - soroush.samarehabolhassani@mail.concordia.ca +Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca +Contributors Soroush Samareh Abolhassani - soroush.samarehabolhassani@mail.concordia.ca +Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ from geomeppy import IDF +import helpers.constants as cte class Idf: @@ -35,19 +38,19 @@ class Idf: idf_surfaces = { # todo: make an enum for all the surface types - 'Wall': 'wall', - 'Ground': 'floor', - 'Roof': 'roof' + cte.WALL: 'wall', + cte.GROUND: 'floor', + cte.ROOF: 'roof' } idf_usage = { # todo: make an enum for all the usage types - 'residential': 'residential_building' + cte.RESIDENTIAL: 'residential_building' } idf_type_limits = { - 'on_off': 'on/off', - 'fraction': 'Fraction', - 'any_number': 'Any Number', + cte.ON_OFF: 'on/off', + cte.FRACTION: 'Fraction', + cte.ANY_NUMBER: 'Any Number', 'continuous': 'Continuous', 'discrete': 'Discrete' } @@ -117,8 +120,8 @@ class Idf: Visible_Absorptance=layer.material.visible_absorptance ) - def _add_daily_schedule(self, usage_zone, schedule): - _schedule = self._idf.newidfobject(self._COMPACT_SCHEDULE, Name=f'{schedule.type} schedules {usage_zone.usage}') + def _add_daily_schedule(self, usage, schedule): + _schedule = self._idf.newidfobject(self._COMPACT_SCHEDULE, Name=f'{schedule.type} schedules {usage}') _val = schedule.values _schedule.Schedule_Type_Limits_Name = self.idf_type_limits[schedule.data_type.lower()] _schedule.Field_1 = "Through: 12/31" @@ -172,14 +175,12 @@ class Idf: _schedule.Field_49 = "Until: 24:00" _schedule.Field_50 = _val[23] - def _add_schedule(self, usage_zone, schedule_type): + def _add_schedule(self, usage, new_schedule): for schedule in self._idf.idfobjects[self._HOURLY_SCHEDULE]: - if schedule.Name == f'{schedule_type} schedules {usage_zone.usage}': + if schedule.Name == f'{new_schedule.type} schedules {usage}': return - for schedule in usage_zone.schedules: - if schedule.type == schedule_type: - if schedule.time_range == "day": - return self._add_daily_schedule(usage_zone, schedule) + if new_schedule.time_range == "day": + return self._add_daily_schedule(usage, new_schedule) return def _add_construction(self, thermal_boundary): @@ -202,12 +203,12 @@ class Idf: _kwargs[f'Layer_{i + 1}'] = layers[1].material.name self._idf.newidfobject(self._CONSTRUCTION, **_kwargs) - def _add_zone(self, usage_zone): + def _add_zone(self, usage_zone, thermal_zone_volume): for zone in self._idf.idfobjects['ZONE']: if zone.Name == usage_zone.id: return - # todo: what does we need to define a zone in energy plus? - self._idf.newidfobject(self._ZONE, Name=usage_zone.id, Volume=usage_zone.volume) + # todo: what do we need to define a zone in energy plus? + self._idf.newidfobject(self._ZONE, Name=usage_zone.id, Volume=thermal_zone_volume * usage_zone.percentage) self._add_heating_system(usage_zone) def _add_thermostat(self, usage_zone): @@ -217,8 +218,8 @@ class Idf: return thermostat return self._idf.newidfobject(self._THERMOSTAT, Name=thermostat_name, - Constant_Heating_Setpoint=usage_zone.heating_setpoint, - Constant_Cooling_Setpoint=usage_zone.cooling_setpoint) + Constant_Heating_Setpoint=usage_zone.thermal_control.mean_heating_set_point, + Constant_Cooling_Setpoint=usage_zone.thermal_control.mean_cooling_set_point) def _add_heating_system(self, usage_zone): for air_system in self._idf.idfobjects[self._IDEAL_LOAD_AIR_SYSTEM]: @@ -232,14 +233,19 @@ class Idf: Cooling_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {usage_zone.usage}', Template_Thermostat_Name=thermostat.Name) - def _add_occupancy(self, usage_zone): + def _add_occupancy(self, usage_zone, area): + number_of_people = area * usage_zone.occupancy.occupancy_density + fraction_radiant = usage_zone.occupancy.sensible_radiative_internal_gain / \ + (usage_zone.occupancy.sensible_radiative_internal_gain + + usage_zone.occupancy.sensible_convective_internal_gain + + usage_zone.occupancy.latent_internal_gain) self._idf.newidfobject(self._PEOPLE, Name=f'{usage_zone.id}_occupancy', Zone_or_ZoneList_Name=usage_zone.id, Number_of_People_Schedule_Name=f'Occupancy schedules {usage_zone.usage}', Number_of_People_Calculation_Method="People", - Number_of_People=500, # todo: get people from where? - Fraction_Radiant=0.3, # todo: howto get this from InternalGains + Number_of_People=number_of_people, + Fraction_Radiant=fraction_radiant, Activity_Level_Schedule_Name=f'Occupancy schedules {usage_zone.usage}' ) @@ -261,7 +267,7 @@ class Idf: Zone_or_ZoneList_Name=usage_zone.id, Schedule_Name=f'Infiltration schedules {usage_zone.usage}', Design_Flow_Rate_Calculation_Method='AirChanges/Hour', - Air_Changes_per_Hour=0.35, # todo: change it from usage catalog + Air_Changes_per_Hour=usage_zone.mechanical_air_change, Constant_Term_Coefficient=0.606, # todo: change it from usage catalog Temperature_Term_Coefficient=3.6359996E-02, # todo: change it from usage catalog Velocity_Term_Coefficient=0.1177165, # todo: change it from usage catalog @@ -275,20 +281,33 @@ class Idf: """ for building in self._city.buildings: - for usage_zone in building.usage_zones: - self._add_schedule(usage_zone, "Infiltration") - self._add_schedule(usage_zone, "Lights") - self._add_schedule(usage_zone, "Occupancy") - self._add_schedule(usage_zone, "Refrigeration") - self._add_schedule(usage_zone, "HVAC Avail") + for internal_zone in building.internal_zones: + for thermal_zone in internal_zone.thermal_zones: + for thermal_boundary in thermal_zone.thermal_boundaries: + self._add_construction(thermal_boundary) + for usage_zone in thermal_zone.usage_zones: + usage = usage_zone.usage + usage_zone_area = thermal_zone.floor_area * usage_zone.percentage - self._add_zone(usage_zone) - self._add_heating_system(usage_zone) - self._add_infiltration(usage_zone) - self._add_occupancy(usage_zone) - for thermal_zone in building.thermal_zones: - for thermal_boundary in thermal_zone.thermal_boundaries: - self._add_construction(thermal_boundary) + # todo: infiltration can be written with two values (system on and system off) in E+? Or just as schedule? + # self._add_schedule(usage, "Infiltration") + for schedule in usage_zone.lighting.schedules: + for day_type in schedule.day_types: + if day_type == cte.MONDAY: + self._add_schedule(usage, schedule) + for schedule in usage_zone.occupancy.occupancy_schedules: + for day_type in schedule.day_types: + if day_type == cte.MONDAY: + self._add_schedule(usage, usage_zone.occupancy.occupancy_schedules) + for schedule in usage_zone.thermal_control.hvac_availability_schedules: + for day_type in schedule.day_types: + if day_type == cte.MONDAY: + self._add_schedule(usage, schedule) + + self._add_zone(usage_zone, thermal_zone.volume) + self._add_heating_system(usage_zone) +# self._add_infiltration(usage_zone) + self._add_occupancy(usage_zone, usage_zone_area) if self._export_type == "Surfaces": self._add_surfaces(building) @@ -335,12 +354,14 @@ class Idf: def _add_surfaces(self, building): - for thermal_zone in building.thermal_zones: - for boundary in thermal_zone.thermal_boundaries: - idf_surface_type = self.idf_surfaces[boundary.surface.type] - for usage_zone in thermal_zone.usage_zones: - surface = self._idf.newidfobject(self._SURFACE, Name=f'{boundary.surface.name}', - Surface_Type=idf_surface_type, Zone_Name=usage_zone.id, - Construction_Name=boundary.construction_name) - coordinates = self._matrix_to_list(boundary.surface.solid_polygon.coordinates, self._city.lower_corner) - surface.setcoords(coordinates) + for internal_zone in building.internal_zones: + for thermal_zone in internal_zone.thermal_zones: + for boundary in thermal_zone.thermal_boundaries: + idf_surface_type = self.idf_surfaces[boundary.parent_surface.type] + for usage_zone in thermal_zone.usage_zones: + surface = self._idf.newidfobject(self._SURFACE, Name=f'{boundary.parent_surface.name}', + Surface_Type=idf_surface_type, Zone_Name=usage_zone.id, + Construction_Name=boundary.construction_name) + coordinates = self._matrix_to_list(boundary.parent_surface.solid_polygon.coordinates, + self._city.lower_corner) + surface.setcoords(coordinates) diff --git a/imports/construction/helpers/storeys_generation.py b/imports/construction/helpers/storeys_generation.py index da0b4dcc..8e890061 100644 --- a/imports/construction/helpers/storeys_generation.py +++ b/imports/construction/helpers/storeys_generation.py @@ -20,8 +20,9 @@ class StoreysGeneration: """ StoreysGeneration """ - def __init__(self, building, divide_in_storeys=False): + def __init__(self, building, internal_zone, divide_in_storeys=False): self._building = building + self._internal_zone = internal_zone self._thermal_zones = [] self._divide_in_storeys = divide_in_storeys self._floor_area = 0 @@ -39,7 +40,8 @@ class StoreysGeneration: self._building.storeys_above_ground) number_of_storeys = 1 if not self._divide_in_storeys or number_of_storeys == 1: - storey = Storey('storey_0', self._building.surfaces, [None, None], self._building.volume, self._floor_area) + storey = Storey('storey_0', self._building.surfaces, [None, None], self._internal_zone.volume, + self._internal_zone, self._floor_area) for thermal_boundary in storey.thermal_boundaries: if thermal_boundary.type != cte.INTERIOR_WALL or thermal_boundary.type != cte.INTERIOR_SLAB: # external thermal boundary -> only one thermal zone @@ -98,13 +100,13 @@ class StoreysGeneration: surfaces_child.append(ceiling) volume = ceiling.area_above_ground * height total_volume += volume - storeys.append(Storey(name, surfaces_child, neighbours, volume, self._floor_area)) + storeys.append(Storey(name, surfaces_child, neighbours, volume, self._internal_zone, 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, self._floor_area)) + storeys.append(Storey(name, surfaces_child_last_storey, neighbours, volume, self._internal_zone, self._floor_area)) for storey in storeys: for thermal_boundary in storey.thermal_boundaries: diff --git a/imports/construction/nrel_physics_interface.py b/imports/construction/nrel_physics_interface.py index 6f08e536..db716bd7 100644 --- a/imports/construction/nrel_physics_interface.py +++ b/imports/construction/nrel_physics_interface.py @@ -234,5 +234,5 @@ class NrelPhysicsInterface: def _create_storeys(building, archetype): building.average_storey_height = archetype.average_storey_height building.storeys_above_ground = archetype.storeys_above_ground - thermal_zones = StoreysGeneration(building).thermal_zones + thermal_zones = StoreysGeneration(building, building.internal_zones[0]).thermal_zones building.internal_zones[0].thermal_zones = thermal_zones diff --git a/imports/customized_imports/sanam_customized_usage_parameters.py b/imports/customized_imports/sanam_customized_usage_parameters.py index ea3cac62..cfc4358e 100644 --- a/imports/customized_imports/sanam_customized_usage_parameters.py +++ b/imports/customized_imports/sanam_customized_usage_parameters.py @@ -72,8 +72,8 @@ class SanamCustomizedUsageParameters: _occupancy = Occupancy() usage_zone.occupancy = _occupancy usage_zone.occupancy.occupancy_density = archetype.occupancy.occupancy_density - archetype_mechanical_air_change = float(archetype.mechanical_air_change) * \ - float(usage_zone.occupancy.occupancy_density) * cte.METERS_TO_FEET ** 2 \ + archetype_mechanical_air_change = float(archetype.mechanical_air_change) \ + * float(archetype.occupancy.occupancy_density) \ * cte.HOUR_TO_MINUTES / cte.METERS_TO_FEET ** 3 / volume_per_area usage_zone.mechanical_air_change = archetype_mechanical_air_change diff --git a/imports/schedules/doe_idf.py b/imports/schedules/doe_idf.py index 61390199..f5c941cf 100644 --- a/imports/schedules/doe_idf.py +++ b/imports/schedules/doe_idf.py @@ -12,6 +12,7 @@ from imports.schedules.helpers.schedules_helper import SchedulesHelper from city_model_structure.attributes.schedule import Schedule from city_model_structure.building_demand.occupancy import Occupancy from city_model_structure.building_demand.lighting import Lighting +from city_model_structure.building_demand.thermal_control import ThermalControl import helpers.constants as cte @@ -141,3 +142,7 @@ class DoeIdf: if usage_zone.lighting is None: usage_zone.lighting = Lighting() usage_zone.lighting.schedules = [schedule] + elif schedule.type == cte.HVAC_AVAILABILITY: + if usage_zone.thermal_control is None: + usage_zone.thermal_control = ThermalControl() + usage_zone.thermal_control.hvac_availability_schedules = [schedule] diff --git a/imports/usage/ca_usage_parameters.py b/imports/usage/ca_usage_parameters.py index b2320fa6..dc933d52 100644 --- a/imports/usage/ca_usage_parameters.py +++ b/imports/usage/ca_usage_parameters.py @@ -64,7 +64,7 @@ class CaUsageParameters(HftUsageInterface): usage_zone.hours_day = archetype.hours_day usage_zone.days_year = archetype.days_year _appliances = Appliances() - _appliances.appliances_density = archetype.appliances.appliances_density + _appliances.density = archetype.appliances.density usage_zone.appliances = _appliances _control = ThermalControl() _control.mean_heating_set_point = archetype.thermal_control.mean_heating_set_point diff --git a/imports/usage/comnet_usage_parameters.py b/imports/usage/comnet_usage_parameters.py index 5d159af7..4556d21f 100644 --- a/imports/usage/comnet_usage_parameters.py +++ b/imports/usage/comnet_usage_parameters.py @@ -78,7 +78,7 @@ class ComnetUsageParameters: _lighting.latent_fraction = ch().comnet_lighting_latent _lighting.convective_fraction = ch().comnet_lighting_convective _lighting.radiative_fraction = ch().comnet_lighting_radiant - _lighting.lighting_density = data['lighting'][comnet_usage][4] + _lighting.density = data['lighting'][comnet_usage][4] # plug loads _appliances = None @@ -87,21 +87,22 @@ class ComnetUsageParameters: _appliances.latent_fraction = ch().comnet_plugs_latent _appliances.convective_fraction = ch().comnet_plugs_convective _appliances.radiative_fraction = ch().comnet_plugs_radiant - _appliances.appliances_density = data['plug loads'][comnet_usage][0] + _appliances.density = data['plug loads'][comnet_usage][0] # occupancy _occupancy = Occupancy() - _occupancy.occupancy_density = data['occupancy'][comnet_usage][0] + value = data['occupancy'][comnet_usage][0] + if value != 0: + _occupancy.occupancy_density = value + else: + _occupancy.occupancy_density = 0 _occupancy.sensible_convective_internal_gain = data['occupancy'][comnet_usage][1] \ * ch().comnet_occupancy_sensible_convective _occupancy.sensible_radiative_internal_gain = data['occupancy'][comnet_usage][1] \ * ch().comnet_occupancy_sensible_radiant _occupancy.latent_internal_gain = data['occupancy'][comnet_usage][2] - if _occupancy.occupancy_density <= 0: - _usage_zone.mechanical_air_change = 0 - else: - _usage_zone.mechanical_air_change = data['ventilation rate'][comnet_usage][0] / _occupancy.occupancy_density + _usage_zone.mechanical_air_change = data['ventilation rate'][comnet_usage][0] schedules_usage = UsageHelper.schedules_key(data['schedules_key'][comnet_usage][0]) @@ -225,21 +226,27 @@ class ComnetUsageParameters: usage_zone.mechanical_air_change = archetype.mechanical_air_change * cte.METERS_TO_FEET ** 2 \ * cte.HOUR_TO_MINUTES / cte.METERS_TO_FEET ** 3 / volume_per_area _occupancy = Occupancy() - _occupancy.occupancy_density = archetype.occupancy.occupancy_density * cte.METERS_TO_FEET**2 - _occupancy.sensible_radiative_internal_gain = archetype.occupancy.sensible_radiative_internal_gain - _occupancy.latent_internal_gain = archetype.occupancy.latent_internal_gain - _occupancy.sensible_convective_internal_gain = archetype.occupancy.sensible_convective_internal_gain + _occupancy.occupancy_density = archetype.occupancy.occupancy_density / cte.METERS_TO_FEET**2 + _occupancy.sensible_radiative_internal_gain = archetype.occupancy.sensible_radiative_internal_gain \ + * archetype.occupancy.occupancy_density / cte.METERS_TO_FEET**2 \ + * cte.BTU_H_TO_WATTS + _occupancy.latent_internal_gain = archetype.occupancy.latent_internal_gain \ + * archetype.occupancy.occupancy_density / cte.METERS_TO_FEET**2 \ + * cte.BTU_H_TO_WATTS + _occupancy.sensible_convective_internal_gain = archetype.occupancy.sensible_convective_internal_gain \ + * archetype.occupancy.occupancy_density / cte.METERS_TO_FEET**2 \ + * cte.BTU_H_TO_WATTS _occupancy.occupancy_schedules = archetype.occupancy.occupancy_schedules usage_zone.occupancy = _occupancy _lighting = Lighting() - _lighting.lighting_density = archetype.lighting.lighting_density / cte.METERS_TO_FEET**2 + _lighting.density = archetype.lighting.density / cte.METERS_TO_FEET ** 2 _lighting.convective_fraction = archetype.lighting.convective_fraction _lighting.radiative_fraction = archetype.lighting.radiative_fraction _lighting.latent_fraction = archetype.lighting.latent_fraction _lighting.schedules = archetype.lighting.schedules usage_zone.lighting = _lighting _appliances = Appliances() - _appliances.appliances_density = archetype.appliances.appliances_density / cte.METERS_TO_FEET**2 + _appliances.density = archetype.appliances.density / cte.METERS_TO_FEET ** 2 _appliances.convective_fraction = archetype.appliances.convective_fraction _appliances.radiative_fraction = archetype.appliances.radiative_fraction _appliances.latent_fraction = archetype.appliances.latent_fraction diff --git a/imports/usage/hft_usage_interface.py b/imports/usage/hft_usage_interface.py index 959afbef..d3605ce1 100644 --- a/imports/usage/hft_usage_interface.py +++ b/imports/usage/hft_usage_interface.py @@ -125,7 +125,7 @@ class HftUsageInterface: if 'appliance' in zone_usage_type: _appliances = Appliances() - _appliances.appliances_density = zone_usage_type['appliance']['#text'] #todo: check units + _appliances.density = zone_usage_type['appliance']['#text'] #todo: check units usage_zone_archetype.appliances = _appliances @@ -220,7 +220,7 @@ class HftUsageInterface: if 'appliance' in usage_zone_variant: _appliances = Appliances() - _appliances.appliances_density = usage_zone_variant['appliance']['#text'] # todo: check units + _appliances.density = usage_zone_variant['appliance']['#text'] # todo: check units usage_zone_archetype.appliances = _appliances diff --git a/requirements.txt b/requirements.txt index cce571d9..ea2ac892 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,6 @@ pandas~=1.2.3 requests~=2.25.1 esoreader~=1.2.3 geomeppy~=0.11.8 -pathlib~=1.0.1 PyWavefront~=1.3.3 xlrd~=2.0.1 openpyxl~=3.0.7 @@ -16,5 +15,4 @@ ply~=3.11 rhino3dm~=7.7.0 scipy==1.7.1 PyYAML==6.0 -yaml~=0.2.5 pyecore~=0.12.2 \ No newline at end of file diff --git a/unittests/test_doe_idf.py b/unittests/test_doe_idf.py index c75e6372..112b9ed7 100644 --- a/unittests/test_doe_idf.py +++ b/unittests/test_doe_idf.py @@ -29,9 +29,11 @@ class TestBuildings(TestCase): output_path = Path('../unittests/tests_outputs/').resolve() city = GeometryFactory('citygml', city_file).city ConstructionFactory('nrel', city).enrich() - UsageFactory('ca', city).enrich() - SchedulesFactory('doe_idf', city).enrich() + UsageFactory('comnet', city).enrich() +# UsageFactory('ca', city).enrich() +# SchedulesFactory('doe_idf', city).enrich() ExportsFactory('idf', city, output_path).export() + self.assertEqual(10, len(city.buildings)) for building in city.buildings: for internal_zone in building.internal_zones: diff --git a/unittests/test_usage_factory.py b/unittests/test_usage_factory.py index 269457be..92c47ba5 100644 --- a/unittests/test_usage_factory.py +++ b/unittests/test_usage_factory.py @@ -9,7 +9,6 @@ from unittest import TestCase from imports.geometry_factory import GeometryFactory from imports.usage_factory import UsageFactory from imports.geometry.helpers.geometry_helper import GeometryHelper -from imports.construction_factory import ConstructionFactory class TestUsageFactory(TestCase): @@ -113,14 +112,14 @@ class TestUsageFactory(TestCase): self.assertIsNone(occupancy.occupants, 'occupancy density is not none') self.assertIsNotNone(usage_zone.lighting, 'lighting is none') lighting = usage_zone.lighting - self.assertIsNotNone(lighting.lighting_density, 'lighting density is none') + self.assertIsNotNone(lighting.density, 'lighting density is none') self.assertIsNotNone(lighting.latent_fraction, 'lighting latent fraction is none') self.assertIsNotNone(lighting.convective_fraction, 'lighting convective fraction is none') self.assertIsNotNone(lighting.radiative_fraction, 'lighting radiant fraction is none') self.assertIsNotNone(lighting.schedules, 'lighting schedule is none') self.assertIsNotNone(usage_zone.appliances, 'appliances is none') appliances = usage_zone.appliances - self.assertIsNotNone(appliances.appliances_density, 'appliances density is none') + self.assertIsNotNone(appliances.density, 'appliances density is none') self.assertIsNotNone(appliances.latent_fraction, 'appliances latent fraction is none') self.assertIsNotNone(appliances.convective_fraction, 'appliances convective fraction is none') self.assertIsNotNone(appliances.radiative_fraction, 'appliances radiant fraction is none') From 50490df96503840d044d2df01bd342744fa7b20c Mon Sep 17 00:00:00 2001 From: Pilar Date: Thu, 7 Apr 2022 16:50:36 -0400 Subject: [PATCH 2/2] Fixed bugs in thermal zone and checked that all tests affected pass --- .../building_demand/thermal_control.py | 4 +- .../building_demand/thermal_zone.py | 196 ++++++++++-------- imports/usage/hft_usage_interface.py | 54 +++-- unittests/test_construction_factory.py | 13 +- unittests/test_enrichement.py | 167 +++++++-------- unittests/test_usage_factory.py | 2 - 6 files changed, 247 insertions(+), 189 deletions(-) diff --git a/city_model_structure/building_demand/thermal_control.py b/city_model_structure/building_demand/thermal_control.py index 41f88bb6..77ef928e 100644 --- a/city_model_structure/building_demand/thermal_control.py +++ b/city_model_structure/building_demand/thermal_control.py @@ -54,7 +54,7 @@ class ThermalControl: Set heating set point defined for a thermal zone in Celsius :param value: float """ - self._mean_heating_set_point = value + self._mean_heating_set_point = float(value) @property def heating_set_back(self) -> Union[None, float]: @@ -93,7 +93,7 @@ class ThermalControl: Set cooling set point defined for a thermal zone in Celsius :param value: float """ - self._mean_cooling_set_point = value + self._mean_cooling_set_point = float(value) @property def hvac_availability_schedules(self) -> Union[None, List[Schedule]]: diff --git a/city_model_structure/building_demand/thermal_zone.py b/city_model_structure/building_demand/thermal_zone.py index 2b4b016e..079fa3b1 100644 --- a/city_model_structure/building_demand/thermal_zone.py +++ b/city_model_structure/building_demand/thermal_zone.py @@ -4,7 +4,7 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Contributors Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ -import uuid +import uuid, sys import copy from typing import List, Union, TypeVar from city_model_structure.building_demand.occupancy import Occupancy @@ -238,8 +238,7 @@ class ThermalZone: for day_type in schedule.day_types: if day_type == requested_day_type: return schedule - else: - return None + return None @property def not_detailed_source_mean_annual_internal_gains(self) -> Union[None, List[InternalGains]]: @@ -257,9 +256,9 @@ class ThermalZone: _radiative_part = 0 _latent_part = 0 for _usage_zone in self._parent_internal_zone.usage_zones: - if _usage_zone.internal_gains is None: + if _usage_zone.not_detailed_source_mean_annual_internal_gains is None: return None - for _internal_gain in _usage_zone.internal_gains: + for _internal_gain in _usage_zone.not_detailed_source_mean_annual_internal_gains: _average_internal_gain += _internal_gain.average_internal_gain _convective_part += _internal_gain.average_internal_gain * _internal_gain.convective_fraction _radiative_part += _internal_gain.average_internal_gain * _internal_gain.radiative_fraction @@ -270,24 +269,35 @@ class ThermalZone: _grouped_internal_gain.convective_fraction = _convective_part / _average_internal_gain _grouped_internal_gain.radiative_fraction = _radiative_part / _average_internal_gain _grouped_internal_gain.latent_fraction = _latent_part / _average_internal_gain - _internal_gains_reference = self._parent_internal_zone.usage_zones[0].internal_gains[0] - day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY, cte.SATURDAY, cte.SUNDAY] - schedule_of_days = [] - for i_day in range(0, 7): - schedule_of_day = \ - copy.deepcopy(self._get_schedule_of_day(day_types[i_day], _internal_gains_reference.schedules)) - schedule_of_day.day_types = [day_types[i_day]] - new_values = [] - for i_value in range(0, len(_internal_gains_reference.schedules[0].values)): - _new_value = 0 - for _usage_zone in self._parent_internal_zone.usage_zones: - for _internal_gain in _usage_zone.internal_gains: - _value = self._get_schedule_of_day(day_types[i_day], _internal_gain.schedules)[i_value] - _new_value += _usage_zone.percentage * _value / len(_usage_zone.internal_gains) - new_values.append(_new_value) - schedule_of_day.values = new_values - schedule_of_days.append(schedule_of_day) - _grouped_internal_gain.schedules = schedule_of_days + + _internal_gains_reference = \ + self._parent_internal_zone.usage_zones[0].not_detailed_source_mean_annual_internal_gains[0] + if _internal_gains_reference.schedules is not None: + day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY, cte.SATURDAY, cte.SUNDAY] + schedule_of_days = [] + for i_day in range(0, 7): + schedule_of_day = \ + copy.deepcopy(self._get_schedule_of_day(day_types[i_day], _internal_gains_reference.schedules)) + if schedule_of_day is None: + sys.stderr.write(f'Error. Not found requested day type when generating internal gains schedules ' + f'in thermal_zone.\n') + schedule_of_day.day_types = [day_types[i_day]] + new_values = [] + for i_value in range(0, len(_internal_gains_reference.schedules[0].values)): + _new_value = 0 + for _usage_zone in self._parent_internal_zone.usage_zones: + for _internal_gain in _usage_zone.not_detailed_source_mean_annual_internal_gains: + _value = self._get_schedule_of_day(day_types[i_day], _internal_gain.schedules) + if _value is None: + sys.stderr.write(f'Error. Not found requested day type when generating internal gains schedules ' + f'in thermal_zone.\n') + _value = _value.values[i_value] + _new_value += _usage_zone.percentage * _value \ + / len(_usage_zone.not_detailed_source_mean_annual_internal_gains) + new_values.append(_new_value) + schedule_of_day.values = new_values + schedule_of_days.append(schedule_of_day) + _grouped_internal_gain.schedules = schedule_of_days else: _grouped_internal_gain.convective_fraction = 0 _grouped_internal_gain.radiative_fraction = 0 @@ -361,6 +371,8 @@ class ThermalZone: if self._mechanical_air_change is None: self._mechanical_air_change = 0 for usage_zone in self._parent_internal_zone.usage_zones: + if usage_zone.mechanical_air_change is None: + return None self._mechanical_air_change += usage_zone.percentage * usage_zone.mechanical_air_change return self._mechanical_air_change @@ -391,27 +403,29 @@ class ThermalZone: if usage_zone.occupancy is None: return None _occupancy_density += usage_zone.percentage * usage_zone.occupancy.occupancy_density - _convective_part += usage_zone.percentage * usage_zone.occupancy.sensible_convective_internal_gain - _radiative_part += usage_zone.percentage * usage_zone.occupancy.sensible_radiative_internal_gain - _latent_part += usage_zone.percentage * usage_zone.occupancy.latent_internal_gain + if usage_zone.occupancy.sensible_convective_internal_gain is not None: + _convective_part += usage_zone.percentage * usage_zone.occupancy.sensible_convective_internal_gain + _radiative_part += usage_zone.percentage * usage_zone.occupancy.sensible_radiative_internal_gain + _latent_part += usage_zone.percentage * usage_zone.occupancy.latent_internal_gain self._occupancy.occupancy_density = _occupancy_density self._occupancy.sensible_convective_internal_gain = _convective_part self._occupancy.sensible_radiative_internal_gain = _radiative_part self._occupancy.latent_internal_gain = _latent_part _occupancy_reference = self._parent_internal_zone.usage_zones[0].occupancy - _schedules = [] - for i_schedule in range(0, len(_occupancy_reference.occupancy_schedules)): - schedule = copy.deepcopy(_occupancy_reference.occupancy_schedules[i_schedule]) - new_values = [] - for i_value in range(0, len(_occupancy_reference.occupancy_schedules[i_schedule].values)): - _new_value = 0 - for usage_zone in self._parent_internal_zone.usage_zones: - _new_value += usage_zone.percentage * usage_zone.occupancy.occupancy_schedules[i_schedule].values[i_value] - new_values.append(_new_value) - schedule.values = new_values - _schedules.append(schedule) - self._occupancy.occupancy_schedules = _schedules + if _occupancy_reference.occupancy_schedules is not None: + _schedules = [] + for i_schedule in range(0, len(_occupancy_reference.occupancy_schedules)): + schedule = copy.deepcopy(_occupancy_reference.occupancy_schedules[i_schedule]) + new_values = [] + for i_value in range(0, len(_occupancy_reference.occupancy_schedules[i_schedule].values)): + _new_value = 0 + for usage_zone in self._parent_internal_zone.usage_zones: + _new_value += usage_zone.percentage * usage_zone.occupancy.occupancy_schedules[i_schedule].values[i_value] + new_values.append(_new_value) + schedule.values = new_values + _schedules.append(schedule) + self._occupancy.occupancy_schedules = _schedules return self._occupancy @occupancy.setter @@ -440,12 +454,13 @@ class ThermalZone: if usage_zone.lighting is None: return None _lighting_density += usage_zone.percentage * usage_zone.lighting.density - _convective_part += usage_zone.percentage * usage_zone.lighting.density \ - * usage_zone.lighting.convective_fraction - _radiative_part += usage_zone.percentage * usage_zone.lighting.density \ - * usage_zone.lighting.radiative_fraction - _latent_part += usage_zone.percentage * usage_zone.lighting.density \ - * usage_zone.lighting.latent_fraction + if usage_zone.lighting.convective_fraction is not None: + _convective_part += usage_zone.percentage * usage_zone.lighting.density \ + * usage_zone.lighting.convective_fraction + _radiative_part += usage_zone.percentage * usage_zone.lighting.density \ + * usage_zone.lighting.radiative_fraction + _latent_part += usage_zone.percentage * usage_zone.lighting.density \ + * usage_zone.lighting.latent_fraction self._lighting.density = _lighting_density if _lighting_density > 0: self._lighting.convective_fraction = _convective_part / _lighting_density @@ -457,18 +472,19 @@ class ThermalZone: self._lighting.latent_fraction = 0 _lighting_reference = self._parent_internal_zone.usage_zones[0].lighting - _schedules = [] - for i_schedule in range(0, len(_lighting_reference.schedules)): - schedule = copy.deepcopy(_lighting_reference.schedules[i_schedule]) - new_values = [] - for i_value in range(0, len(_lighting_reference.schedules[i_schedule].values)): - _new_value = 0 - for usage_zone in self._parent_internal_zone.usage_zones: - _new_value += usage_zone.percentage * usage_zone.lighting.schedules[i_schedule].values[i_value] - new_values.append(_new_value) - schedule.values = new_values - _schedules.append(schedule) - self._lighting.schedules = _schedules + if _lighting_reference.schedules is not None: + _schedules = [] + for i_schedule in range(0, len(_lighting_reference.schedules)): + schedule = copy.deepcopy(_lighting_reference.schedules[i_schedule]) + new_values = [] + for i_value in range(0, len(_lighting_reference.schedules[i_schedule].values)): + _new_value = 0 + for usage_zone in self._parent_internal_zone.usage_zones: + _new_value += usage_zone.percentage * usage_zone.lighting.schedules[i_schedule].values[i_value] + new_values.append(_new_value) + schedule.values = new_values + _schedules.append(schedule) + self._lighting.schedules = _schedules return self._lighting @lighting.setter @@ -497,12 +513,13 @@ class ThermalZone: if usage_zone.appliances is None: return None _appliances_density += usage_zone.percentage * usage_zone.appliances.density - _convective_part += usage_zone.percentage * usage_zone.appliances.density \ - * usage_zone.appliances.convective_fraction - _radiative_part += usage_zone.percentage * usage_zone.appliances.density \ - * usage_zone.appliances.radiative_fraction - _latent_part += usage_zone.percentage * usage_zone.appliances.density \ - * usage_zone.appliances.latent_fraction + if usage_zone.appliances.convective_fraction is not None: + _convective_part += usage_zone.percentage * usage_zone.appliances.density \ + * usage_zone.appliances.convective_fraction + _radiative_part += usage_zone.percentage * usage_zone.appliances.density \ + * usage_zone.appliances.radiative_fraction + _latent_part += usage_zone.percentage * usage_zone.appliances.density \ + * usage_zone.appliances.latent_fraction self._appliances.density = _appliances_density if _appliances_density > 0: self._appliances.convective_fraction = _convective_part / _appliances_density @@ -514,18 +531,19 @@ class ThermalZone: self._appliances.latent_fraction = 0 _appliances_reference = self._parent_internal_zone.usage_zones[0].appliances - _schedules = [] - for i_schedule in range(0, len(_appliances_reference.schedules)): - schedule = copy.deepcopy(_appliances_reference.schedules[i_schedule]) - new_values = [] - for i_value in range(0, len(_appliances_reference.schedules[i_schedule].values)): - _new_value = 0 - for usage_zone in self._parent_internal_zone.usage_zones: - _new_value += usage_zone.percentage * usage_zone.appliances.schedules[i_schedule].values[i_value] - new_values.append(_new_value) - schedule.values = new_values - _schedules.append(schedule) - self._appliances.schedules = _schedules + if _appliances_reference.schedules is not None: + _schedules = [] + for i_schedule in range(0, len(_appliances_reference.schedules)): + schedule = copy.deepcopy(_appliances_reference.schedules[i_schedule]) + new_values = [] + for i_value in range(0, len(_appliances_reference.schedules[i_schedule].values)): + _new_value = 0 + for usage_zone in self._parent_internal_zone.usage_zones: + _new_value += usage_zone.percentage * usage_zone.appliances.schedules[i_schedule].values[i_value] + new_values.append(_new_value) + schedule.values = new_values + _schedules.append(schedule) + self._appliances.schedules = _schedules return self._appliances @appliances.setter @@ -562,9 +580,14 @@ class ThermalZone: + self.occupancy.sensible_radiative_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_radiative_internal_gain / _total_heat_gain - _internal_gain.convective_fraction = self.occupancy.sensible_convective_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) @@ -613,25 +636,30 @@ class ThermalZone: self._thermal_control.mean_cooling_set_point = _mean_cooling_set_point _thermal_control_reference = self._parent_internal_zone.usage_zones[0].thermal_control - _types_reference = [_thermal_control_reference.hvac_availability_schedules, - _thermal_control_reference.heating_set_point_schedules, - _thermal_control_reference.cooling_set_point_schedules] + _types_reference = [] + if _thermal_control_reference.hvac_availability_schedules is not None: + _types_reference.append([cte.HVAC_AVAILABILITY, _thermal_control_reference.hvac_availability_schedules]) + if _thermal_control_reference.heating_set_point_schedules is not None: + _types_reference.append([cte.HEATING_SET_POINT, _thermal_control_reference.heating_set_point_schedules]) + if _thermal_control_reference.cooling_set_point_schedules is not None: + _types_reference.append([cte.COOLING_SET_POINT, _thermal_control_reference.cooling_set_point_schedules]) + for i_type in range(0, len(_types_reference)): _schedules = [] - _schedule_type = _types_reference[i_type] + _schedule_type = _types_reference[i_type][1] for i_schedule in range(0, len(_schedule_type)): schedule = copy.deepcopy(_schedule_type[i_schedule]) new_values = [] for i_value in range(0, len(_schedule_type[i_schedule].values)): _new_value = 0 for usage_zone in self._parent_internal_zone.usage_zones: - if i_type == 0: + if _types_reference[i_type][0] == cte.HVAC_AVAILABILITY: _new_value += usage_zone.percentage * \ usage_zone.thermal_control.hvac_availability_schedules[i_schedule].values[i_value] - elif i_type == 1: + elif _types_reference[i_type][0] == cte.HEATING_SET_POINT: _new_value += usage_zone.percentage * \ usage_zone.thermal_control.heating_set_point_schedules[i_schedule].values[i_value] - elif i_type == 2: + elif _types_reference[i_type][0] == cte.COOLING_SET_POINT: _new_value += usage_zone.percentage * \ usage_zone.thermal_control.cooling_set_point_schedules[i_schedule].values[i_value] new_values.append(_new_value) diff --git a/imports/usage/hft_usage_interface.py b/imports/usage/hft_usage_interface.py index d3605ce1..0524785a 100644 --- a/imports/usage/hft_usage_interface.py +++ b/imports/usage/hft_usage_interface.py @@ -59,11 +59,16 @@ class HftUsageInterface: _schedule.time_range = cte.DAY _schedule.time_step = cte.HOUR _schedule.data_type = cte.ANY_NUMBER - _schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY] + _schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY, cte.SATURDAY, + cte.SUNDAY] _values = zone_usage_type['occupancy']['internGains']['load']['weekDayProfile']['values'] while ' ' in _values: _values = _values.replace(' ', ' ') - _schedule.values = _values.split() + _values = _values.split() + _values_float = [] + for _value in _values: + _values_float.append(float(_value)) + _schedule.values = _values_float _internal_gain.schedules = [_schedule] usage_zone_archetype.not_detailed_source_mean_annual_internal_gains = [_internal_gain] @@ -80,11 +85,16 @@ class HftUsageInterface: _schedule.time_range = cte.DAY _schedule.time_step = cte.HOUR _schedule.data_type = cte.TEMPERATURE - _schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY] + _schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY, cte.SATURDAY, + cte.SUNDAY] _values = zone_usage_type['endUses']['space_heating']['schedule']['weekDayProfile']['values'] while ' ' in _values: _values = _values.replace(' ', ' ') - _schedule.values = _values.split() + _values = _values.split() + _values_float = [] + for _value in _values: + _values_float.append(float(_value)) + _schedule.values = _values_float _thermal_control.heating_set_point_schedules = [_schedule] if 'space_cooling' in zone_usage_type['endUses']: @@ -96,11 +106,16 @@ class HftUsageInterface: _schedule.time_range = cte.DAY _schedule.time_step = cte.HOUR _schedule.data_type = cte.TEMPERATURE - _schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY] + _schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY, cte.SATURDAY, + cte.SUNDAY] _values = zone_usage_type['endUses']['space_cooling']['schedule']['weekDayProfile']['values'] while ' ' in _values: _values = _values.replace(' ', ' ') - _schedule.values = _values.split() + _values = _values.split() + _values_float = [] + for _value in _values: + _values_float.append(float(_value)) + _schedule.values = _values_float _thermal_control.cooling_set_point_schedules = [_schedule] usage_zone_archetype.thermal_control = _thermal_control @@ -164,11 +179,16 @@ class HftUsageInterface: _schedule.time_range = cte.DAY _schedule.time_step = cte.HOUR _schedule.data_type = cte.ANY_NUMBER - _schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY] + _schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY, cte.SATURDAY, + cte.SUNDAY] _values = usage_zone_variant['occupancy']['internGains']['load']['weekDayProfile']['values'] while ' ' in _values: _values = _values.replace(' ', ' ') - _schedule.values = _values.split() + _values = _values.split() + _values_float = [] + for _value in _values: + _values_float.append(float(_value)) + _schedule.values = _values_float _internal_gain.schedules = [_schedule] usage_zone_archetype.not_detailed_source_mean_annual_internal_gains = [_internal_gain] @@ -187,11 +207,16 @@ class HftUsageInterface: _schedule.time_range = cte.DAY _schedule.time_step = cte.HOUR _schedule.data_type = cte.TEMPERATURE - _schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY] + _schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY, cte.SATURDAY, + cte.SUNDAY] _values = usage_zone_variant['endUses']['space_heating']['schedule']['weekDayProfile']['values'] while ' ' in _values: _values = _values.replace(' ', ' ') - _schedule.values = _values.split() + _values = _values.split() + _values_float = [] + for _value in _values: + _values_float.append(float(_value)) + _schedule.values = _values_float _thermal_control.heating_set_point_schedules = [_schedule] if 'space_cooling' in usage_zone_variant['endUses'] and \ @@ -205,11 +230,16 @@ class HftUsageInterface: _schedule.time_range = cte.DAY _schedule.time_step = cte.HOUR _schedule.data_type = cte.TEMPERATURE - _schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY] + _schedule.day_types = [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY, cte.SATURDAY, + cte.SUNDAY] _values = usage_zone_variant['endUses']['space_cooling']['schedule']['weekDayProfile']['values'] while ' ' in _values: _values = _values.replace(' ', ' ') - _schedule.values = _values.split() + _values = _values.split() + _values_float = [] + for _value in _values: + _values_float.append(float(_value)) + _schedule.values = _values_float _thermal_control.cooling_set_point_schedules = [_schedule] usage_zone_archetype.thermal_control = _thermal_control diff --git a/unittests/test_construction_factory.py b/unittests/test_construction_factory.py index 9348e8d3..7ddd129b 100644 --- a/unittests/test_construction_factory.py +++ b/unittests/test_construction_factory.py @@ -84,9 +84,18 @@ class TestConstructionFactory(TestCase): 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.view_factors_matrix, 'thermal_zone view factors matrix is none') - self.assertIsNone(thermal_zone.usage_zones, 'thermal_zone usage_zones 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') + self.assertIsNone(thermal_zone.usage, 'thermal_zone usage is not none') + self.assertIsNone(thermal_zone.not_detailed_source_mean_annual_internal_gains, + 'thermal_zone not detailed source internal gains is not none') + self.assertIsNone(thermal_zone.hours_day, 'thermal_zone hours a day is not none') + self.assertIsNone(thermal_zone.days_year, 'thermal_zone days a year is not none') + self.assertIsNone(thermal_zone.mechanical_air_change, 'thermal_zone mechanical air change is not none') + self.assertIsNone(thermal_zone.occupancy, 'thermal_zone occupancy is not none') + self.assertIsNone(thermal_zone.lighting, 'thermal_zone lighting is not none') + self.assertIsNone(thermal_zone.appliances, 'thermal_zone appliances is not none') + self.assertIsNone(thermal_zone.thermal_control, 'thermal_zone thermal control is not none') + self.assertIsNone(thermal_zone.get_internal_gains(), 'thermal_zone internal gains not returns none') def _check_thermal_boundaries(self, thermal_zone): for thermal_boundary in thermal_zone.thermal_boundaries: diff --git a/unittests/test_enrichement.py b/unittests/test_enrichement.py index 95861429..804ba697 100644 --- a/unittests/test_enrichement.py +++ b/unittests/test_enrichement.py @@ -30,6 +30,16 @@ class TestGeometryFactory(TestCase): self.assertIsNotNone(self._city, 'city is none') return self._city + def _check_result(self, city): + 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.assertIsNotNone(usage_zone.id, 'usage id is none') + for thermal_zone in internal_zone.thermal_zones: + self._check_thermal_zone(thermal_zone) + def _check_buildings(self, city): for building in city.buildings: self.assertIsNotNone(building.internal_zones, 'no internal zones created') @@ -42,13 +52,19 @@ class TestGeometryFactory(TestCase): self.assertIsNotNone(building.storeys_above_ground, 'building storeys_above_ground is none') self.assertTrue(building.is_conditioned, 'building is_conditioned is not conditioned') - def _check_usage_zone(self, usage_zone): - self.assertIsNotNone(usage_zone.id, 'usage id is none') - - def _check_thermal_zones(self, thermal_zone): + def _check_thermal_zone(self, thermal_zone): self.assertIsNotNone(thermal_zone.id, 'thermal_zone id is none') - self.assertIsNone(thermal_zone.usage_zones, 'thermal_zone usage_zones is not none') - self.assertIsNone(thermal_zone.thermal_control, 'thermal_zone thermal_control is not none') + self.assertIsNotNone(thermal_zone.usage, 'thermal_zone usage is not none') + self.assertIsNotNone(thermal_zone.hours_day, 'thermal_zone hours a day is none') + self.assertIsNotNone(thermal_zone.days_year, 'thermal_zone days a year is none') + self.assertIsNotNone(thermal_zone.occupancy, 'thermal_zone occupancy is none') + self.assertIsNotNone(thermal_zone.thermal_control, 'thermal_zone thermal control is none') + self.assertIsNotNone(thermal_zone.get_internal_gains(), 'thermal_zone internal gains returns none') + + def _check_extra_thermal_zone(self, thermal_zone): + self.assertIsNotNone(thermal_zone.lighting, 'thermal_zone lighting is none') + self.assertIsNotNone(thermal_zone.appliances, 'thermal_zone appliances is none') + self.assertIsNotNone(thermal_zone.mechanical_air_change, 'thermal_zone mechanical air change is none') @staticmethod def _prepare_case_usage_first(city, input_key, construction_key, usage_key): @@ -72,92 +88,69 @@ class TestGeometryFactory(TestCase): ConstructionFactory(construction_key, city).enrich() UsageFactory(usage_key, city).enrich() + def _test_hft(self, file): + _construction_keys = ['nrel', 'nrcan'] + _usage_keys = ['ca', 'comnet', 'hft'] + for construction_key in _construction_keys: + for usage_key in _usage_keys: + # construction factory called first + city = self._get_citygml(file) + self.assertTrue(len(city.buildings) > 0) + self._prepare_case_construction_first(city, 'hft', construction_key, usage_key) + self._check_result(city) + if usage_key == 'comnet': + for building in city.buildings: + for internal_zone in building.internal_zones: + for thermal_zone in internal_zone.thermal_zones: + self._check_extra_thermal_zone(thermal_zone) + # usage factory called first + city = self._get_citygml(file) + self.assertTrue(len(city.buildings) > 0) + self._prepare_case_usage_first(city, 'hft', construction_key, usage_key) + self._check_result(city) + if usage_key == 'comnet': + for building in city.buildings: + for internal_zone in building.internal_zones: + for thermal_zone in internal_zone.thermal_zones: + self._check_extra_thermal_zone(thermal_zone) + + def _test_pluto(self, file): + _construction_keys = ['nrel'] + _usage_keys = ['comnet', 'hft'] + for construction_key in _construction_keys: + for usage_key in _usage_keys: + # construction factory called first + city = self._get_citygml(file) + self.assertTrue(len(city.buildings) > 0) + self._prepare_case_construction_first(city, 'pluto', construction_key, usage_key) + self._check_result(city) + if usage_key == 'comnet': + for building in city.buildings: + for internal_zone in building.internal_zones: + for thermal_zone in internal_zone.thermal_zones: + self._check_extra_thermal_zone(thermal_zone) + # usage factory called first + city = self._get_citygml(file) + self.assertTrue(len(city.buildings) > 0) + self._prepare_case_usage_first(city, 'pluto', construction_key, usage_key) + self._check_result(city) + if usage_key == 'comnet': + for building in city.buildings: + for internal_zone in building.internal_zones: + for thermal_zone in internal_zone.thermal_zones: + self._check_extra_thermal_zone(thermal_zone) + def test_enrichment(self): """ Test enrichment of the city with different order and all possible combinations :return: None """ file_1 = 'one_building_in_kelowna.gml' + print('file 1') + self._test_hft(file_1) file_2 = 'pluto_building.gml' + print('file 2') + self._test_pluto(file_2) file_3 = 'C40_Final.gml' - _construction_keys = ['nrel', 'nrcan'] - _usage_keys = ['ca', 'comnet'] # todo: add 'hft' - - for construction_key in _construction_keys: - for usage_key in _usage_keys: - city = self._get_citygml(file_1) - self.assertTrue(len(city.buildings) == 1) - self._prepare_case_construction_first(city, 'hft', construction_key, usage_key) - 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) - for thermal_zone in internal_zone.thermal_zones: - self._check_thermal_zones(thermal_zone) - - for construction_key in _construction_keys: - for usage_key in _usage_keys: - city = self._get_citygml(file_1) - self.assertTrue(len(city.buildings) == 1) - self._prepare_case_usage_first(city, 'hft', construction_key, usage_key) - 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) - for thermal_zone in internal_zone.thermal_zones: - self._check_thermal_zones(thermal_zone) - - for construction_key in _construction_keys: - if construction_key != 'nrcan': - for usage_key in _usage_keys: - if usage_key != 'ca': - city = self._get_citygml(file_2) - self.assertTrue(len(city.buildings) == 1) - self._prepare_case_construction_first(city, 'pluto', construction_key, usage_key) - 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) - for thermal_zone in internal_zone.thermal_zones: - self._check_thermal_zones(thermal_zone) - - for construction_key in _construction_keys: - if construction_key != 'nrcan': - for usage_key in _usage_keys: - if usage_key != 'ca': - city = self._get_citygml(file_2) - self.assertTrue(len(city.buildings) == 1) - self._prepare_case_usage_first(city, 'pluto', construction_key, usage_key) - 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) - for thermal_zone in internal_zone.thermal_zones: - self._check_thermal_zones(thermal_zone) - - city = self._get_citygml(file_3) - self.assertTrue(len(city.buildings) == 10) - - for construction_key in _construction_keys: - if construction_key != 'nrcan': - for usage_key in _usage_keys: - if usage_key != 'ca': - city = self._get_citygml(file_2) - self.assertTrue(len(city.buildings) == 1) - self._prepare_case_construction_first(city, 'pluto', construction_key, usage_key) - 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) - for thermal_zone in internal_zone.thermal_zones: - self._check_thermal_zones(thermal_zone) + print('file 3') + self._test_hft(file_3) diff --git a/unittests/test_usage_factory.py b/unittests/test_usage_factory.py index 92c47ba5..7d2dada1 100644 --- a/unittests/test_usage_factory.py +++ b/unittests/test_usage_factory.py @@ -71,7 +71,6 @@ class TestUsageFactory(TestCase): def _check_usage_zone(self, usage_zone): self.assertIsNotNone(usage_zone.usage, 'usage is none') self.assertIsNotNone(usage_zone.percentage, 'usage percentage is none') - self.assertIsNotNone(usage_zone.get_internal_gains, 'internal gains is none') self.assertIsNotNone(usage_zone.hours_day, 'hours per day is none') self.assertIsNotNone(usage_zone.days_year, 'days per year is none') self.assertIsNotNone(usage_zone.thermal_control, 'thermal control is none') @@ -177,4 +176,3 @@ class TestUsageFactory(TestCase): self.assertIsNone(occupancy.occupants, 'occupancy density is not none') self.assertIsNone(usage_zone.lighting, 'lighting is not none') self.assertIsNone(usage_zone.appliances, 'appliances is not none') -