diff --git a/city_model_structure/building.py b/city_model_structure/building.py index 0c3d0082..f2ac273b 100644 --- a/city_model_structure/building.py +++ b/city_model_structure/building.py @@ -31,7 +31,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 3e377df1..9e0464b3 100644 --- a/city_model_structure/building_demand/appliances.py +++ b/city_model_structure/building_demand/appliances.py @@ -13,28 +13,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]: @@ -91,6 +91,7 @@ class Appliances: def schedules(self) -> Union[None, List[Schedule]]: """ Get schedules + dataType = fraction :return: None or [Schedule] """ return self._schedules @@ -99,6 +100,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 f3ad93a5..dfec83c4 100644 --- a/city_model_structure/building_demand/internal_gains.py +++ b/city_model_structure/building_demand/internal_gains.py @@ -111,6 +111,7 @@ class InternalGains: def schedules(self) -> Union[None, List[Schedule]]: """ Get internal gain schedule + dataType = fraction :return: [Schedule] """ return self._schedules @@ -119,6 +120,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 16bd2b58..a4def3d0 100644 --- a/city_model_structure/building_demand/internal_zone.py +++ b/city_model_structure/building_demand/internal_zone.py @@ -121,4 +121,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 26ea29eb..d2ba27e6 100644 --- a/city_model_structure/building_demand/lighting.py +++ b/city_model_structure/building_demand/lighting.py @@ -13,28 +13,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]: @@ -91,6 +91,7 @@ class Lighting: def schedules(self) -> Union[None, List[Schedule]]: """ Get schedules + dataType = fraction :return: None or [Schedule] """ return self._schedules @@ -99,6 +100,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 a7c9db5c..27376a03 100644 --- a/city_model_structure/building_demand/occupancy.py +++ b/city_model_structure/building_demand/occupancy.py @@ -24,7 +24,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 @@ -32,7 +32,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: @@ -93,6 +93,7 @@ class Occupancy: def occupancy_schedules(self) -> Union[None, List[Schedule]]: """ Get occupancy schedules + dataType = fraction :return: None or [Schedule] """ return self._occupancy_schedules @@ -101,6 +102,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 16278eaa..42264eda 100644 --- a/city_model_structure/building_demand/storey.py +++ b/city_model_structure/building_demand/storey.py @@ -16,7 +16,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 @@ -24,6 +24,7 @@ class Storey: self._thermal_zone = None self._neighbours = neighbours self._volume = volume + self._internal_zone = internal_zone self._floor_area = floor_area @property @@ -87,7 +88,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 e1acda5c..466f5cf1 100644 --- a/city_model_structure/building_demand/thermal_control.py +++ b/city_model_structure/building_demand/thermal_control.py @@ -55,7 +55,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]: @@ -94,12 +94,13 @@ 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]]: """ Get the availability of the conditioning system defined for a thermal zone + dataType = on/off :return: None or [Schedule] """ return self._hvac_availability_schedules @@ -108,6 +109,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 @@ -116,6 +118,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 @@ -124,6 +127,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 @@ -132,6 +136,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 @@ -140,6 +145,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 d3e19c46..267c27a9 100644 --- a/city_model_structure/building_demand/thermal_zone.py +++ b/city_model_structure/building_demand/thermal_zone.py @@ -5,22 +5,29 @@ Copyright © 2022 Concordia CERC group Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Code 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.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 @@ -30,11 +37,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): """ @@ -169,42 +184,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]: """ @@ -212,16 +191,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): @@ -238,3 +208,477 @@ 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 + 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.not_detailed_source_mean_annual_internal_gains is None: + return None + 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 + _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].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 + _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: + 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 + + @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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 = 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 = [] + 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][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 _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 _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 _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) + 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 b25fc1c5..279589c1 100644 --- a/city_model_structure/building_demand/usage_zone.py +++ b/city_model_structure/building_demand/usage_zone.py @@ -7,7 +7,6 @@ Code 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 @@ -31,7 +30,6 @@ class UsageZone: self._occupancy = None self._lighting = None self._appliances = None - self._internal_gains = None self._thermal_control = None @property @@ -210,51 +208,6 @@ class UsageZone: """ self._appliances = value - @property - def 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 dc910ff5..501136a4 100644 --- a/exports/exports_factory.py +++ b/exports/exports_factory.py @@ -20,6 +20,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 @@ -93,3 +95,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 4a0c0175..c51b648e 100644 --- a/exports/formats/idf.py +++ b/exports/formats/idf.py @@ -8,6 +8,7 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord """ from geomeppy import IDF +import helpers.constants as cte class Idf: @@ -38,19 +39,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' } @@ -120,8 +121,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" @@ -175,14 +176,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): @@ -205,12 +204,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): @@ -220,8 +219,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]: @@ -235,14 +234,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}' ) @@ -264,7 +268,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 @@ -278,20 +282,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) @@ -338,12 +355,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 d33a6c51..51d6e416 100644 --- a/imports/construction/helpers/storeys_generation.py +++ b/imports/construction/helpers/storeys_generation.py @@ -21,8 +21,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 @@ -40,7 +41,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 @@ -99,13 +101,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 268d55d4..4a0f9c8c 100644 --- a/imports/construction/nrel_physics_interface.py +++ b/imports/construction/nrel_physics_interface.py @@ -238,5 +238,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 f746014f..ffe401b6 100644 --- a/imports/customized_imports/sanam_customized_usage_parameters.py +++ b/imports/customized_imports/sanam_customized_usage_parameters.py @@ -73,8 +73,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 b37f86d1..eeb72168 100644 --- a/imports/schedules/doe_idf.py +++ b/imports/schedules/doe_idf.py @@ -13,6 +13,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 @@ -142,3 +143,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 22397d8b..0866284e 100644 --- a/imports/usage/ca_usage_parameters.py +++ b/imports/usage/ca_usage_parameters.py @@ -65,7 +65,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 7d001aa8..e2663479 100644 --- a/imports/usage/comnet_usage_parameters.py +++ b/imports/usage/comnet_usage_parameters.py @@ -79,7 +79,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 @@ -88,21 +88,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]) @@ -226,21 +227,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 a8ca2ad6..ffd4d1d7 100644 --- a/imports/usage/hft_usage_interface.py +++ b/imports/usage/hft_usage_interface.py @@ -60,11 +60,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] @@ -81,11 +86,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']: @@ -97,11 +107,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 @@ -126,7 +141,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 @@ -165,11 +180,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] @@ -188,11 +208,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 \ @@ -206,11 +231,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 @@ -221,7 +251,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_construction_factory.py b/unittests/test_construction_factory.py index 17790ebb..8eeb16ab 100644 --- a/unittests/test_construction_factory.py +++ b/unittests/test_construction_factory.py @@ -85,9 +85,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_doe_idf.py b/unittests/test_doe_idf.py index de587093..5eeaeb41 100644 --- a/unittests/test_doe_idf.py +++ b/unittests/test_doe_idf.py @@ -30,9 +30,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_enrichement.py b/unittests/test_enrichement.py index b8e10551..bf0af703 100644 --- a/unittests/test_enrichement.py +++ b/unittests/test_enrichement.py @@ -31,6 +31,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') @@ -43,13 +53,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): @@ -73,92 +89,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 641be4ea..93408819 100644 --- a/unittests/test_usage_factory.py +++ b/unittests/test_usage_factory.py @@ -10,7 +10,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): @@ -73,7 +72,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.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') @@ -114,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') @@ -179,4 +177,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') -