diff --git a/hub/catalog_factories/usage/nrcan_catalog.py b/hub/catalog_factories/usage/nrcan_catalog.py index 8caf9d10..246e05f3 100644 --- a/hub/catalog_factories/usage/nrcan_catalog.py +++ b/hub/catalog_factories/usage/nrcan_catalog.py @@ -131,10 +131,9 @@ class NrcanCatalog(Catalog): mechanical_air_change = space_type['ventilation_air_changes'] # cfm/ft2 to m3/m2.s ventilation_rate = space_type['ventilation_per_area'] / (cte.METERS_TO_FEET * cte.MINUTES_TO_SECONDS) - if ventilation_rate == 0: - # cfm/person to m3/m2.s - ventilation_rate = space_type['ventilation_per_person'] / (cte.METERS_TO_FEET * cte.MINUTES_TO_SECONDS)\ - / occupancy_density + # cfm/person to m3/m2.s + ventilation_rate += space_type['ventilation_per_person'] / (pow(cte.METERS_TO_FEET, 3) * cte.MINUTES_TO_SECONDS)\ + * occupancy_density lighting_radiative_fraction = space_type['lighting_fraction_radiant'] lighting_convective_fraction = 0 diff --git a/hub/exports/building_energy/idf.py b/hub/exports/building_energy/idf.py index df817e78..3aa95d95 100644 --- a/hub/exports/building_energy/idf.py +++ b/hub/exports/building_energy/idf.py @@ -4,6 +4,7 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2022 Concordia CERC group Project Coder Guille Guillermo.GutierrezMorote@concordia.ca Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca + Oriol Gavaldà Torrellas oriol.gavalda@concordia.ca """ import copy from pathlib import Path @@ -100,7 +101,6 @@ class Idf: self._adjacent_buildings = adjacent_buildings if self._adjacent_buildings is None: self._adjacent_buildings = [] - self._export() @staticmethod @@ -202,9 +202,13 @@ class Idf: _infiltration_values = [] for hvac_value in hvac_availability_schedule.values: if hvac_value == 0: - _infiltration_values.append(thermal_zone.infiltration_rate_system_off) + _infiltration_values.append(1.0) else: - _infiltration_values.append(thermal_zone.infiltration_rate_system_on) + if thermal_zone.infiltration_rate_system_off == 0: + _infiltration_values.append(0.0) + else: + _infiltration_values.append( + thermal_zone.infiltration_rate_system_on / thermal_zone.infiltration_rate_system_off) _schedule.values = _infiltration_values _infiltration_schedules.append(_schedule) for schedule in self._idf.idfobjects[self._HOURLY_SCHEDULE]: @@ -315,7 +319,6 @@ class Idf: for zone in self._idf.idfobjects['ZONE']: if zone.Name == name: return - # todo: what do we need to define a zone in energy plus? self._idf.newidfobject(self._ZONE, Name=name, Volume=thermal_zone.volume) self._add_heating_system(thermal_zone, name) @@ -362,13 +365,9 @@ class Idf: def _add_lighting(self, thermal_zone: ThermalZone, zone_name: str): fraction_radiant = thermal_zone.lighting.radiative_fraction - # todo: fraction visible should come from catalog - fraction_visible = 0.3 method = 'Watts/Area' - factor_size = thermal_zone.total_floor_area / thermal_zone.footprint_area - watts_per_zone_floor_area = thermal_zone.lighting.density * factor_size - # todo: fraction replaceable should come from catalog - fraction_replaceable = 1 + storeys_number = int(thermal_zone.total_floor_area / thermal_zone.footprint_area) + watts_per_zone_floor_area = thermal_zone.lighting.density * storeys_number subcategory = f'ELECTRIC EQUIPMENT#{zone_name}#GeneralLights' self._idf.newidfobject(self._LIGHTS, @@ -378,21 +377,17 @@ class Idf: Design_Level_Calculation_Method=method, Watts_per_Zone_Floor_Area=watts_per_zone_floor_area, Fraction_Radiant=fraction_radiant, - Fraction_Visible=fraction_visible, - Fraction_Replaceable=fraction_replaceable, EndUse_Subcategory=subcategory ) def _add_appliances(self, thermal_zone, zone_name): fuel_type = 'Electricity' fraction_radiant = thermal_zone.appliances.radiative_fraction - fraction_latent = 0 + fraction_latent = thermal_zone.appliances.latent_fraction method = 'Watts/Area' - factor_size = thermal_zone.total_floor_area / thermal_zone.footprint_area - watts_per_zone_floor_area = thermal_zone.appliances.density * factor_size + storeys_number = int(thermal_zone.total_floor_area / thermal_zone.footprint_area) + watts_per_zone_floor_area = thermal_zone.appliances.density * storeys_number subcategory = f'ELECTRIC EQUIPMENT#{zone_name}#InteriorEquipment' - # _object = self._idf.newidfobject(self._APPLIANCES) - # print(vars(_object)) self._idf.newidfobject(self._APPLIANCES, Fuel_Type=fuel_type, Name=f'{zone_name}_appliance', @@ -406,36 +401,30 @@ class Idf: ) def _add_infiltration(self, thermal_zone, zone_name): - # for zone in self._idf.idfobjects["ZONE"]: - # if zone.Name == f'{zone_name}_infiltration': - # return + + for zone in self._idf.idfobjects["ZONE"]: + if zone.Name == f'{zone_name}_infiltration': + return schedule = f'Infiltration schedules {thermal_zone.usage_name}' - # if schedule not in self._idf.idfobjects[self._HOURLY_SCHEDULE]: - # return - # todo: eliminate the factor - factorreduct = 0.5 + if schedule not in self._idf.idfobjects[self._HOURLY_SCHEDULE]: + return self._idf.newidfobject(self._INFILTRATION, Name=f'{zone_name}_infiltration', Zone_or_ZoneList_Name=zone_name, Schedule_Name=schedule, Design_Flow_Rate_Calculation_Method='AirChanges/Hour', - Air_Changes_per_Hour=thermal_zone.infiltration_rate_system_off * factorreduct + Air_Changes_per_Hour=thermal_zone.infiltration_rate_system_off ) - def _add_dhw(self, thermal_zone, zone_name): - fuel_type = 'Electricity' - method = 'Watts/Area' - factor_size = thermal_zone.total_floor_area / thermal_zone.footprint_area - # todo: revision of values of peak flow (too low). Added a factor, but to check original units + def _add_dhw(self, building, thermal_zone, zone_name): peak_flow_rate = thermal_zone.domestic_hot_water.peak_flow * thermal_zone.total_floor_area - # = self._idf.newidfobject(self._dhw) - # print(vars(_object)) self._idf.newidfobject(self._DHW, Name=f'DHW {zone_name}', Peak_Flow_Rate=peak_flow_rate, Flow_Rate_Fraction_Schedule_Name=f'DHW_prof schedules {thermal_zone.usage_name}', Target_Temperature_Schedule_Name=f'DHW_temp schedules {thermal_zone.usage_name}', Hot_Water_Supply_Temperature_Schedule_Name=f'DHW_temp schedules {thermal_zone.usage_name}', + Cold_Water_Supply_Temperature_Schedule_Name=f'cold_temp schedules {building.name}', EndUse_Subcategory=f'DHW {zone_name}', Zone_Name=zone_name ) @@ -489,7 +478,7 @@ class Idf: self._add_schedules(usage, 'Lighting', thermal_zone.lighting.schedules) self._add_schedules(usage, 'Appliance', thermal_zone.appliances.schedules) self._add_schedules(usage, 'DHW_prof', thermal_zone.domestic_hot_water.schedules) - # self._add_service_temp_schedules(thermal_zone) + # todo self._add_service_temp_schedules(thermal_zone) value = int(thermal_zone.domestic_hot_water.service_temperature) self._add_constant_hourly_year_schedules(thermal_zone, value, 'DHW_temp') self._add_people_activity_level_schedules(thermal_zone) @@ -519,8 +508,6 @@ class Idf: Variable_Name="Zone Ideal Loads Supply Air Total Heating Energy", Reporting_Frequency="Monthly", ) - # _object = self._idf.newidfobject("OUTPUT:VARIABLE") - # print(vars(_object)) self._idf.newidfobject( "OUTPUT:VARIABLE", @@ -534,18 +521,6 @@ class Idf: Reporting_Frequency="Monthly", ) - # self._idf.newidfobject( - # "OUTPUTCONTROL:TABLE:STYLE", - # Variable_Name="CommaAndHTML, JtoKWH", - # ) - - # self._idf.match() - # try: - # self._idf.intersect_match() - # except IndexError: - # seems to be a bug from geomeppy when surfaces cannot be intersected - # pass - # post-process to erase windows associated to adiabatic walls windows_list = [] for window in self._idf.idfobjects[self._WINDOW]: @@ -598,15 +573,12 @@ class Idf: Fraction_of_Shading_Surface_That_Is_Glazed=0) def _add_pure_geometry(self, building, zone_name): - for surface in building.surfaces: outside_boundary_condition = 'Outdoors' sun_exposure = 'SunExposed' wind_exposure = 'WindExposed' outside_boundary_condition_object = None - # TODO: set assumption in constants, to select minimun shared area - #print(f'wall {surface.name} {surface.percentage_shared}') - if surface.percentage_shared is not None and surface.percentage_shared > 0.1: + if surface.percentage_shared is not None and surface.percentage_shared > 0.5: outside_boundary_condition = 'Surface' outside_boundary_condition_object = surface.name sun_exposure = 'NoSun' @@ -645,8 +617,7 @@ class Idf: sun_exposure = 'SunExposed' wind_exposure = 'WindExposed' outside_boundary_condition_object = '' - # TODO: set assumption in constants, to select minimun shared area - if boundary.parent_surface.percentage_shared is not None and boundary.parent_surface.percentage_shared >= 0.1: + if boundary.parent_surface.percentage_shared is not None and boundary.parent_surface.percentage_shared >= 0.5: outside_boundary_condition = 'Surface' outside_boundary_condition_object = boundary.parent_surface.name sun_exposure = 'NoSun' @@ -659,7 +630,6 @@ class Idf: construction_name = f'{boundary.construction_name}_{boundary.parent_surface.vegetation.name}' else: construction_name = boundary.construction_name - #print(f'shared wall {boundary.parent_surface.name} {outside_boundary_condition_object} {idf_surface_type}') surface = self._idf.newidfobject(self._SURFACE, Name=f'{boundary.parent_surface.name}', Surface_Type=idf_surface_type, Zone_Name=zone_name, diff --git a/hub/imports/usage/nrcan_usage_parameters.py b/hub/imports/usage/nrcan_usage_parameters.py index 77afb9d0..19b56ac5 100644 --- a/hub/imports/usage/nrcan_usage_parameters.py +++ b/hub/imports/usage/nrcan_usage_parameters.py @@ -45,12 +45,12 @@ class NrcanUsageParameters: sys.stderr.write(f'Building {building.name} has unknown usage archetype for usage: {usage_name}\n') continue - usage_name = Dictionaries().hub_usage_to_comnet_usage[building.function] + comnet_usage_name = Dictionaries().hub_usage_to_comnet_usage[building.function] try: - comnet_archetype_usage = self._search_archetypes(comnet_catalog, usage_name) + comnet_archetype_usage = self._search_archetypes(comnet_catalog, comnet_usage_name) except KeyError: - logger.error(f'Building {building.name} has unknown usage archetype for usage: {usage_name}\n') - sys.stderr.write(f'Building {building.name} has unknown usage archetype for usage: {usage_name}\n') + logger.error(f'Building {building.name} has unknown usage archetype for usage: {comnet_usage_name}\n') + sys.stderr.write(f'Building {building.name} has unknown usage archetype for usage: {comnet_usage_name}\n') continue for internal_zone in building.internal_zones: @@ -81,8 +81,10 @@ class NrcanUsageParameters: @staticmethod def _assign_values(usage, archetype, volume_per_area, cold_water_temperature): if archetype.mechanical_air_change > 0: + # ACH usage.mechanical_air_change = archetype.mechanical_air_change elif archetype.ventilation_rate > 0: + # m3/m2.s to ACH usage.mechanical_air_change = archetype.ventilation_rate / volume_per_area * cte.HOUR_TO_SECONDS else: usage.mechanical_air_change = 0