diff --git a/hub/exports/building_energy/cerc_idf.py b/hub/exports/building_energy/cerc_idf.py index 7d674f09..54ec7559 100644 --- a/hub/exports/building_energy/cerc_idf.py +++ b/hub/exports/building_energy/cerc_idf.py @@ -36,6 +36,10 @@ class CercIdf: self._output_nomass_materials_path = str((output_path / 'nomass_materials.idf').resolve()) self._output_window_materials_path = str((output_path / 'window_materials.idf').resolve()) self._output_constructions_path = str((output_path / 'constructions.idf').resolve()) + self._output_zones_path = str((output_path / 'zones.idf').resolve()) + self._output_occupancy_path = str((output_path / 'occupancy.idf').resolve()) + self._output_lighting_path = str((output_path / 'lights.idf').resolve()) + self._output_appliances_path = str((output_path / 'appliances.idf').resolve()) self._output_surfaces_path = str((output_path / 'surfaces.idf').resolve()) self._output_file_path = str((output_path / f'{city.name}.idf').resolve()) @@ -62,8 +66,12 @@ class CercIdf: self._output_nomass_materials = open(self._output_nomass_materials_path, 'w') self._output_window_materials = open(self._output_window_materials_path, 'w') self._output_constructions = open(self._output_constructions_path, 'w') + self._output_zones = open(self._output_zones_path, 'w') + self._output_occupancy = open(self._output_occupancy_path, 'w') + self._output_lighting = open(self._output_lighting_path, 'w') + self._output_appliances = open(self._output_appliances_path, 'w') self._output_surfaces = open(self._output_surfaces_path, 'w') - + self._create_geometry_rules() with open(self._output_file_path, 'w') as self._idf_file: self._idf_file.writelines(lines) self._export() @@ -73,6 +81,26 @@ class CercIdf: self._output_file_schedules.close() self._output_solid_materials.close() self._output_nomass_materials.close() + self._output_window_materials.close() + self._output_constructions.close() + self._output_zones.close() + self._output_occupancy.close() + self._output_lighting.close() + self._output_appliances.close() + self._output_surfaces.close() + + def _create_geometry_rules(self): + """ + GlobalGeometryRules, + UpperLeftCorner, !- Starting Vertex Position + CounterClockWise, !- Vertex Entry Direction + World; !- Coordinate System + """ + file = self._output_zones + self._write_to_idf_format(file, idf_cte.GLOBAL_GEOMETRY_RULES) + self._write_to_idf_format(file, 'UpperLeftCorner', 'Starting Vertex Position') + self._write_to_idf_format(file, 'CounterClockWise', 'Vertex Entry Direction') + self._write_to_idf_format(file, 'World', 'Coordinate System', ';') @staticmethod def _write_to_idf_format(file, field, comment='', eol=','): @@ -132,12 +160,14 @@ class CercIdf: coordinates = self._matrix_to_list(boundary.parent_surface.solid_polygon.coordinates, self._city.lower_corner) eol = ',' + coordinates_length = len(coordinates) for i, coordinate in enumerate(coordinates): - if i == len(coordinates) -1: + vertex = i + 1 + if vertex == coordinates_length: eol = ';' - self._write_to_idf_format(file, coordinate[0], f'Vertex {i} Xcoordinate') - self._write_to_idf_format(file, coordinate[1], f'Vertex {i} Ycoordinate') - self._write_to_idf_format(file, coordinate[2], f'Vertex {i} Zcoordinate', 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) @staticmethod def _create_infiltration_schedules(thermal_zone): @@ -344,6 +374,108 @@ class CercIdf: self._write_to_idf_format(file, construction_name, 'Name') self._write_to_idf_format(file, material_name, 'Outside Layer', ';') + def _add_zone(self, thermal_zone, zone_name): + file = self._output_zones + self._write_to_idf_format(file, idf_cte.ZONE) + self._write_to_idf_format(file, zone_name, 'Name') + self._write_to_idf_format(file, 0, 'Direction of Relative North') + self._write_to_idf_format(file, 0, 'X Origin') + self._write_to_idf_format(file, 0, 'Y Origin') + self._write_to_idf_format(file, 0, 'Z Origin') + self._write_to_idf_format(file, 1, 'Type') + self._write_to_idf_format(file, 1, 'Multiplier') + self._write_to_idf_format(file, idf_cte.AUTOCALCULATE, 'Ceiling Height') + self._write_to_idf_format(file, thermal_zone.volume, 'Volume') + self._write_to_idf_format(file, idf_cte.AUTOCALCULATE, 'Floor Area') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Zone Inside Convection Algorithm') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Zone Outside Convection Algorithm') + self._write_to_idf_format(file, 'Yes', 'Part of Total Floor Area', ';') + + def _add_occupancy(self, thermal_zone, zone_name): + number_of_people = thermal_zone.occupancy.occupancy_density * thermal_zone.total_floor_area + fraction_radiant = 0 + total_sensible = ( + thermal_zone.occupancy.sensible_radiative_internal_gain + thermal_zone.occupancy.sensible_convective_internal_gain + ) + if total_sensible != 0: + fraction_radiant = thermal_zone.occupancy.sensible_radiative_internal_gain / total_sensible + occupancy_schedule = f'Occupancy schedules {thermal_zone.usage_name}' + activity_level_schedule = f'Activity Level schedules {thermal_zone.usage_name}' + file = self._output_occupancy + self._write_to_idf_format(file, idf_cte.PEOPLE) + self._write_to_idf_format(file, f'{zone_name}_occupancy', 'Name') + self._write_to_idf_format(file, zone_name, 'Zone or ZoneList or Space or SpaceList Name') + self._write_to_idf_format(file, occupancy_schedule, 'Number of People Schedule Name') + self._write_to_idf_format(file, 'People', 'Number of People Calculation Method') + self._write_to_idf_format(file, number_of_people, 'Number of People') + self._write_to_idf_format(file, idf_cte.EMPTY, 'People per Floor Area') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Floor Area per Person') + self._write_to_idf_format(file, fraction_radiant, 'Fraction Radiant') + self._write_to_idf_format(file, idf_cte.AUTOCALCULATE, 'Sensible Heat Fraction') + self._write_to_idf_format(file, activity_level_schedule, 'Activity Level Schedule Name') + self._write_to_idf_format(file, '3.82e-08', 'Carbon Dioxide Generation Rate') + self._write_to_idf_format(file, 'No', 'Enable ASHRAE 55 Comfort Warnings') + self._write_to_idf_format(file, 'EnclosureAveraged', 'Mean Radiant Temperature Calculation Type') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Surface NameAngle Factor List Name') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Work Efficiency Schedule Name') + self._write_to_idf_format(file, 'ClothingInsulationSchedule', 'Clothing Insulation Calculation Method') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Clothing Insulation Calculation Method Schedule Name') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Clothing Insulation Schedule Name') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Air Velocity Schedule Name') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Thermal Comfort Model 1 Type') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Thermal Comfort Model 2 Type') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Thermal Comfort Model 3 Type') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Thermal Comfort Model 4 Type') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Thermal Comfort Model 5 Type') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Thermal Comfort Model 6 Type') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Thermal Comfort Model 7 Type') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Ankle Level Air Velocity Schedule Name') + self._write_to_idf_format(file, '15.56', 'Cold Stress Temperature Threshold') + self._write_to_idf_format(file, '30', 'Heat Stress Temperature Threshold', ';') + + def _add_lighting(self, thermal_zone, zone_name): + 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' + file = self._output_lighting + self._write_to_idf_format(file, idf_cte.LIGHTS) + self._write_to_idf_format(file, f'{zone_name}_lights', 'Name') + self._write_to_idf_format(file, zone_name, 'Zone or ZoneList or Space or SpaceList Name') + self._write_to_idf_format(file, f'Lighting schedules {thermal_zone.usage_name}', 'Schedule Name') + self._write_to_idf_format(file, 'Watts/Area', 'Design Level Calculation Method') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Lighting Level') + self._write_to_idf_format(file, watts_per_zone_floor_area, 'Watts per Zone Floor Area') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Watts per Person') + self._write_to_idf_format(file, 0, 'Return Air Fraction') + self._write_to_idf_format(file, thermal_zone.lighting.radiative_fraction, 'Fraction Radiant') + self._write_to_idf_format(file, 0, 'Fraction Visible') + self._write_to_idf_format(file, 1, 'Fraction Replaceable') + self._write_to_idf_format(file, subcategory, 'EndUse Subcategory') + self._write_to_idf_format(file, 'No', 'Return Air Fraction Calculated from Plenum Temperature') + self._write_to_idf_format(file, 0, 'Return Air Fraction Function of Plenum Temperature Coefficient 1') + self._write_to_idf_format(file, 0, 'Return Air Fraction Function of Plenum Temperature Coefficient 2',';') + + def _add_appliances(self, thermal_zone, zone_name): + schedule_name = f'Appliance schedules {thermal_zone.usage_name}' + 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' + file = self._output_appliances + self._write_to_idf_format(file, idf_cte.APPLIANCES) + self._write_to_idf_format(file, f'{zone_name}_appliance', 'Name') + self._write_to_idf_format(file, 'Electricity', 'Fuel Type') + self._write_to_idf_format(file, zone_name, 'Zone or ZoneList or Space or SpaceList Name') + self._write_to_idf_format(file, schedule_name, 'Schedule Name') + self._write_to_idf_format(file, 'Watts/Area', 'Design Level Calculation Method') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Design Level') + self._write_to_idf_format(file, watts_per_zone_floor_area, 'Power per Zone Floor Area') + self._write_to_idf_format(file, idf_cte.EMPTY, 'Power per Person') + self._write_to_idf_format(file, thermal_zone.appliances.latent_fraction, 'Fraction Latent') + self._write_to_idf_format(file, thermal_zone.appliances.radiative_fraction, 'Fraction Radiant') + self._write_to_idf_format(file, 0, 'Fraction Lost') + self._write_to_idf_format(file, 0, 'Carbon Dioxide Generation Rate') + self._write_to_idf_format(file, subcategory, 'EndUse Subcategory', ';') + def _merge_files(self): self._output_schedules.flush() with open(self._output_schedules_path, 'r') as file: @@ -369,6 +501,22 @@ class CercIdf: with open(self._output_constructions_path, 'r') as file: lines = file.readlines() self._idf_file.writelines(lines) + self._output_zones.flush() + with open(self._output_zones_path, 'r') as file: + lines = file.readlines() + self._idf_file.writelines(lines) + self._output_occupancy.flush() + with open(self._output_occupancy_path, 'r') as file: + lines = file.readlines() + self._idf_file.writelines(lines) + self._output_lighting.flush() + with open(self._output_lighting_path, 'r') as file: + lines = file.readlines() + self._idf_file.writelines(lines) + self._output_appliances.flush() + with open(self._output_appliances_path, 'r') as file: + lines = file.readlines() + self._idf_file.writelines(lines) self._output_surfaces.flush() with open(self._output_surfaces_path, 'r') as file: lines = file.readlines() @@ -414,9 +562,12 @@ class CercIdf: for thermal_opening in thermal_boundary.thermal_openings: self._add_window_materials(thermal_opening) self._add_windows_constructions() + self._add_zone(thermal_zone, building.name) + self._add_occupancy(thermal_zone, building.name) + self._add_lighting(thermal_zone, building.name) + self._add_appliances(thermal_zone, building.name) if is_target: self._add_surfaces(building, building.name) - # Merge files self._merge_files() diff --git a/hub/exports/building_energy/idf_helper/__init__.py b/hub/exports/building_energy/idf_helper/__init__.py index 2ca3f526..31cf5399 100644 --- a/hub/exports/building_energy/idf_helper/__init__.py +++ b/hub/exports/building_energy/idf_helper/__init__.py @@ -7,6 +7,11 @@ NOMASS_MATERIAL = '\nMATERIAL:NOMASS,\n' SOLID_MATERIAL = '\nMATERIAL,\n' WINDOW_MATERIAL = '\nWINDOWMATERIAL:SIMPLEGLAZINGSYSTEM,\n' CONSTRUCTION = '\nCONSTRUCTION,\n' +ZONE = '\nZONE,\n' +GLOBAL_GEOMETRY_RULES = '\nGlobalGeometryRules,\n' +PEOPLE = '\nPEOPLE,\n' +LIGHTS = '\nLIGHTS,\n' +APPLIANCES = '\nOTHEREQUIPMENT,\n' AUTOCALCULATE = 'autocalculate' ROUGHNESS = 'MediumRough'