From 27514d4d773c61dc405cde4052b41f23555d34c9 Mon Sep 17 00:00:00 2001 From: guille Date: Mon, 23 Sep 2024 17:52:52 +0200 Subject: [PATCH] cerc idf implementation refactoring and added systems --- hub/exports/building_energy/cerc_idf.py | 89 ++++++++++++++++--- .../building_energy/idf_files/outputs.idf | 64 +++++++++++++ .../building_energy/idf_helper/__init__.py | 3 +- 3 files changed, 142 insertions(+), 14 deletions(-) create mode 100644 hub/exports/building_energy/idf_files/outputs.idf diff --git a/hub/exports/building_energy/cerc_idf.py b/hub/exports/building_energy/cerc_idf.py index 001be16d..0df2f52e 100644 --- a/hub/exports/building_energy/cerc_idf.py +++ b/hub/exports/building_energy/cerc_idf.py @@ -7,10 +7,12 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord Oriol Gavalda Torrellas oriol.gavalda@concordia.ca """ import copy +import os from datetime import datetime from pathlib import Path -import hub.helpers.constants as cte + import hub.exports.building_energy.idf_helper as idf_cte +import hub.helpers.constants as cte from hub.city_model_structure.attributes.schedule import Schedule from hub.city_model_structure.building_demand.layer import Layer @@ -30,7 +32,7 @@ class CercIdf: target_buildings=None): self._city = city self._output_path = str(output_path.resolve()) - self._output_file_path = str((output_path / f'{city.name}.idf').resolve()) + self._output_file_path = str((output_path / f'{city.name}.idf').resolve()) self._file_paths = { 'schedules': str((output_path / 'schedules.idf').resolve()), @@ -43,11 +45,13 @@ class CercIdf: 'occupancy': str((output_path / 'occupancy.idf').resolve()), 'lighting': str((output_path / 'lights.idf').resolve()), 'appliances': str((output_path / 'appliances.idf').resolve()), + 'surfaces': str((output_path / 'surfaces.idf').resolve()), + 'shading': str((output_path / 'shading.idf').resolve()), 'infiltration': str((output_path / 'infiltration.idf').resolve()), 'ventilation': str((output_path / 'ventilation.idf').resolve()), 'thermostat': str((output_path / 'thermostat.idf').resolve()), 'ideal_load_system': str((output_path / 'ideal_load_system.idf').resolve()), - 'surfaces': str((output_path / 'surfaces.idf').resolve()) # todo: move surface to the right position after appliances + 'dhw': str((output_path / 'dhw.idf').resolve()), } self._files = {} for key, value in self._file_paths.items(): @@ -55,7 +59,9 @@ class CercIdf: self._idd_file_path = str(idd_file_path) self._idf_file_path = str(idf_file_path) + self._outputs_file_path = str(Path(idf_file_path).parent / 'outputs.idf') self._epw_file_path = str(epw_file_path) + self._target_buildings = target_buildings self._adjacent_buildings = [] @@ -83,6 +89,13 @@ class CercIdf: with open(path, 'r') as file: lines = file.readlines() self._idf_file.writelines(lines) + for path in self._file_paths.values(): + os.unlink(path) + + def _add_outputs(self): + with open(self._outputs_file_path, 'r') as base_idf: + lines = base_idf.readlines() + self._idf_file.writelines(lines) def _create_geometry_rules(self): file = self._files['zones'] @@ -330,13 +343,6 @@ class CercIdf: self._write_to_idf_format(file, thermal_opening.g_value, 'Solar Heat Gain Coefficient', ';') def _add_constructions(self, thermal_boundary): - """ - CONSTRUCTION, - 1000_1900_6 Roof, !- Name - Asphalt 1, !- Outside Layer - virtual_no_mass_13, !- Layer 2 - MW Glass Wool (rolls); !- Layer 3 - """ if thermal_boundary.layers is None: thermal_boundary.layers = [self._add_default_material()] name = f'{thermal_boundary.construction_name} {thermal_boundary.parent_surface.type}' @@ -493,7 +499,6 @@ class CercIdf: def _add_ventilation(self, thermal_zone, zone_name): schedule_name = f'Ventilation schedules {thermal_zone.usage_name}' air_change = thermal_zone.mechanical_air_change * cte.HOUR_TO_SECONDS - infiltration = thermal_zone.infiltration_rate_system_off * cte.HOUR_TO_SECONDS file = self._files['ventilation'] self._write_to_idf_format(file, idf_cte.VENTILATION) self._write_to_idf_format(file, f'{zone_name}_ventilation', 'Name') @@ -572,17 +577,70 @@ class CercIdf: self._write_to_idf_format(file, 0.70, 'Sensible Heat Recovery Effectiveness') self._write_to_idf_format(file, 0.65, 'Latent Heat Recovery Effectiveness', ';') + def _add_dhw(self, thermal_zone, zone_name): + name = f'DHW {zone_name}' + peak_flow_rate = thermal_zone.domestic_hot_water.peak_flow * thermal_zone.total_floor_area + flow_rate_schedule = f'DHW_prof schedules {thermal_zone.usage_name}' + dhw_schedule = f'DHW_temp schedules {thermal_zone.usage_name}' + cold_temp_schedule = f'cold_temp schedules {thermal_zone.usage_name}' + file = self._files['dhw'] + self._write_to_idf_format(file, idf_cte.DHW) + self._write_to_idf_format(file, name, 'Name') + self._write_to_idf_format(file, name, 'EndUse Subcategory') + self._write_to_idf_format(file, peak_flow_rate, 'Peak Flow Rate') + self._write_to_idf_format(file, flow_rate_schedule, 'Flow Rate Fraction Schedule Name') + self._write_to_idf_format(file, dhw_schedule, 'Target Temperature Schedule Name') + self._write_to_idf_format(file, dhw_schedule, 'Hot Water Supply Temperature Schedule Name') + self._write_to_idf_format(file, cold_temp_schedule, 'Cold Water Supply Temperature Schedule Name') + self._write_to_idf_format(file, zone_name, 'Zone Name', ';') + + def _add_shading(self, building): + name = building.name + file = self._files['shading'] + for s, surface in enumerate(building.surfaces): + + self._write_to_idf_format(file, idf_cte.SHADING) + self._write_to_idf_format(file, f'{name}_{s}', 'Name') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Transmittance Schedule Name') + self._write_to_idf_format(file, idf_cte.AUTOCALCULATE, 'Number of Vertices') + eol = ',' + coordinates = self._matrix_to_list(surface.solid_polygon.coordinates, self._city.lower_corner) + coordinates_length = len(coordinates) + for i, coordinate in enumerate(coordinates): + vertex = i + 1 + if vertex == coordinates_length: + eol = ';' + self._write_to_idf_format(file, coordinate[0], f'Vertex {vertex} Xcoordinate') + self._write_to_idf_format(file, coordinate[1], f'Vertex {vertex} Ycoordinate') + self._write_to_idf_format(file, coordinate[2], f'Vertex {vertex} Zcoordinate', eol) + + def _add_window_construction_and_material(self, thermal_opening): + for window_material in self._idf.idfobjects[self._WINDOW_MATERIAL_SIMPLE]: + if window_material['UFactor'] == thermal_opening.overall_u_value and \ + window_material['Solar_Heat_Gain_Coefficient'] == thermal_opening.g_value: + return + + order = str(len(self._idf.idfobjects[self._WINDOW_MATERIAL_SIMPLE]) + 1) + material_name = 'glazing_' + order + _kwargs = {'Name': material_name, 'UFactor': thermal_opening.overall_u_value, + 'Solar_Heat_Gain_Coefficient': thermal_opening.g_value} + self._idf.newidfobject(self._WINDOW_MATERIAL_SIMPLE, **_kwargs) + + window_construction_name = 'window_construction_' + order + _kwargs = {'Name': window_construction_name, 'Outside_Layer': material_name} + self._idf.newidfobject(self._CONSTRUCTION, **_kwargs) + def _export(self): + start = datetime.now() for building in self._city.buildings: is_target = building.name in self._target_buildings or building.name in self._adjacent_buildings for internal_zone in building.internal_zones: if internal_zone.thermal_zones_from_internal_zones is None: - self._target_buildings.remove(building.name) is_target = False continue for thermal_zone in internal_zone.thermal_zones_from_internal_zones: if is_target: - start = datetime.now() + service_temperature = thermal_zone.domestic_hot_water.service_temperature usage = thermal_zone.usage_name occ = thermal_zone.occupancy @@ -624,9 +682,14 @@ class CercIdf: self._add_ventilation(thermal_zone, building.name) self._add_thermostat(thermal_zone) self._add_heating_system(thermal_zone, building.name) + self._add_dhw(thermal_zone, building.name) if is_target: self._add_surfaces(building, building.name) + else: + self._add_shading(building) self._create_output_control_lighting() # Add lighting control to the lighting # Merge files self._merge_files() + self._add_outputs() + print(f'Export completed in: {datetime.now() - start}') diff --git a/hub/exports/building_energy/idf_files/outputs.idf b/hub/exports/building_energy/idf_files/outputs.idf new file mode 100644 index 00000000..d0442210 --- /dev/null +++ b/hub/exports/building_energy/idf_files/outputs.idf @@ -0,0 +1,64 @@ + +Output:Table:SummaryReports, + AnnualBuildingUtilityPerformanceSummary, !- Report 1 Name + DemandEndUseComponentsSummary, !- Report 2 Name + SensibleHeatGainSummary, !- Report 3 Name + InputVerificationandResultsSummary, !- Report 4 Name + AdaptiveComfortSummary, !- Report 5 Name + Standard62.1Summary, !- Report 6 Name + ClimaticDataSummary, !- Report 7 Name + EquipmentSummary, !- Report 8 Name + EnvelopeSummary, !- Report 9 Name + LightingSummary, !- Report 10 Name + HVACSizingSummary, !- Report 11 Name + SystemSummary, !- Report 12 Name + ComponentSizingSummary, !- Report 13 Name + OutdoorAirSummary, !- Report 14 Name + ObjectCountSummary, !- Report 15 Name + EndUseEnergyConsumptionOtherFuelsMonthly, !- Report 16 Name + PeakEnergyEndUseOtherFuelsMonthly; !- Report 17 Name + +OutputControl:Table:Style, + CommaAndHTML, !- Column Separator + JtoKWH; !- Unit Conversion + +OUTPUT:VARIABLE, + *, !- Key Value + Zone Ideal Loads Supply Air Total Heating Energy, !- Variable Name + Hourly; !- Reporting Frequency + +OUTPUT:VARIABLE, + *, !- Key Value + Zone Ideal Loads Supply Air Total Cooling Energy, !- Variable Name + Hourly; !- Reporting Frequency + +OUTPUT:VARIABLE, + *, !- Key Value + Water Use Equipment Heating Rate, !- Variable Name + Hourly; !- Reporting Frequency + +OUTPUT:VARIABLE, + *, !- Key Value + Zone Lights Electricity Rate, !- Variable Name + Hourly; !- Reporting Frequency + +OUTPUT:VARIABLE, + *, !- Key Value + Other Equipment Electricity Rate, !- Variable Name + Hourly; !- Reporting Frequency + +Output:Meter, + DISTRICTHEATING:Facility, !- Key Name + hourly; !- Reporting Frequency + +Output:Meter, + DISTRICTCOOLING:Facility, !- Key Name + hourly; !- Reporting Frequency + +Output:Meter, + InteriorEquipment:Electricity, !- Key Name + hourly; !- Reporting Frequency + +Output:Meter, + InteriorLights:Electricity, !- Key Name + hourly; !- Reporting Frequency \ No newline at end of file diff --git a/hub/exports/building_energy/idf_helper/__init__.py b/hub/exports/building_energy/idf_helper/__init__.py index ff4b64ae..4c7f4887 100644 --- a/hub/exports/building_energy/idf_helper/__init__.py +++ b/hub/exports/building_energy/idf_helper/__init__.py @@ -17,7 +17,8 @@ INFILTRATION = '\nZONEINFILTRATION:DESIGNFLOWRATE,\n' VENTILATION = '\nZONEVENTILATION:DESIGNFLOWRATE,\n' THERMOSTAT = '\nHVACTEMPLATE:THERMOSTAT,\n' IDEAL_LOAD_SYSTEM = '\nHVACTEMPLATE:ZONE:IDEALLOADSAIRSYSTEM,\n' - +DHW = '\nWATERUSE:EQUIPMENT,\n' +SHADING = '\nSHADING:BUILDING:DETAILED,\n' AUTOCALCULATE = 'autocalculate' ROUGHNESS = 'MediumRough'