cerc idf implementation refactoring and added systems

This commit is contained in:
Guille Gutierrez 2024-09-18 06:56:04 +02:00
parent 62c9a5aab7
commit 5e384c8185
2 changed files with 98 additions and 113 deletions

View File

@ -24,26 +24,34 @@ class CercIdf:
_materials_added_to_idf = {}
_windows_added_to_idf = {'glazing_index': 0}
_constructions_added_to_idf = {}
_thermostat_added_to_idf = {}
def __init__(self, city, output_path, idf_file_path, idd_file_path, epw_file_path,
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_schedules_path = str((output_path / 'schedules.idf').resolve())
self._output_file_schedules_path = str((output_path / 'file_schedules.idf').resolve())
self._output_solid_materials_path = str((output_path / 'solid_materials.idf').resolve())
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_infiltration_path = str((output_path / 'infiltration.idf').resolve())
self._output_ventilation_path = str((output_path / 'ventilation.idf').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()),
'file_schedules': str((output_path / 'file_schedules.idf').resolve()),
'solid_materials': str((output_path / 'solid_materials.idf').resolve()),
'nomass_materials': str((output_path / 'nomass_materials.idf').resolve()),
'window_materials': str((output_path / 'window_materials.idf').resolve()),
'constructions': str((output_path / 'constructions.idf').resolve()),
'zones': str((output_path / 'zones.idf').resolve()),
'occupancy': str((output_path / 'occupancy.idf').resolve()),
'lighting': str((output_path / 'lights.idf').resolve()),
'appliances': str((output_path / 'appliances.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
}
self._files = {}
for key, value in self._file_paths.items():
self._files[key] = open(value, 'w')
self._idd_file_path = str(idd_file_path)
self._idf_file_path = str(idf_file_path)
@ -61,20 +69,6 @@ class CercIdf:
with open(self._idf_file_path, 'r') as base_idf:
lines = base_idf.readlines()
self._output_schedules = open(self._output_schedules_path, 'w')
self._output_file_schedules = open(self._output_file_schedules_path, 'w')
self._output_solid_materials = open(self._output_solid_materials_path, 'w')
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._output_infiltration = open(self._output_infiltration_path, 'w')
self._output_ventilation = open(self._output_ventilation_path, 'w')
# Create base values
self._create_geometry_rules()
@ -82,30 +76,23 @@ class CercIdf:
self._idf_file.writelines(lines)
self._export()
def __del__(self):
self._output_schedules.close()
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()
self._output_infiltration.close()
self._output_ventilation.close()
def _merge_files(self):
for file in self._files.values():
file.close()
for path in self._file_paths.values():
with open(path, 'r') as file:
lines = file.readlines()
self._idf_file.writelines(lines)
def _create_geometry_rules(self):
file = self._output_zones
file = self._files['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', ';')
def _create_output_control_lighting(self):
file = self._output_appliances
file = self._files['appliances']
self._write_to_idf_format(file, idf_cte.OUTPUT_CONTROL)
self._write_to_idf_format(file, 'Comma', 'Column Separator', ';')
@ -132,7 +119,7 @@ class CercIdf:
def _add_surfaces(self, building, zone_name):
# Verify if create building surfaces "by hand" it's faster wwr it's missing
zone_name = f'{zone_name}'
file = self._output_surfaces
file = self._files['surfaces']
for thermal_zone in building.thermal_zones_from_internal_zones:
for index, boundary in enumerate(thermal_zone.thermal_boundaries):
surface_type = idf_cte.idf_surfaces[boundary.parent_surface.type]
@ -247,7 +234,7 @@ class CercIdf:
with open(file_name, 'w', encoding='utf8') as file:
for value in schedule.values:
file.write(f'{str(value)},\n')
file = self._output_file_schedules
file = self._files['file_schedules']
self._write_to_idf_format(file, idf_cte.FILE_SCHEDULE)
self._write_to_idf_format(file, schedule_name, 'Name')
self._write_to_idf_format(file, idf_cte.idf_type_limits[schedule.data_type], 'Schedule Type Limits Name')
@ -266,7 +253,7 @@ class CercIdf:
schedule_name = f'{schedule_type} schedules {usage}'
if schedule_name not in self._schedules_added_to_idf:
self._schedules_added_to_idf[schedule_name] = True
file = self._output_schedules
file = self._files['schedules']
self._write_to_idf_format(file, idf_cte.COMPACT_SCHEDULE)
self._write_to_idf_format(file, schedule_name, 'Name')
self._write_to_idf_format(file, idf_cte.idf_type_limits[schedules[0].data_type], 'Schedule Type Limits Name')
@ -286,7 +273,7 @@ class CercIdf:
self._write_to_idf_format(file, 'Until: 24:00,0.0', f'Field {counter + 2}', ';')
def _add_solid_material(self, layer):
file = self._output_solid_materials
file = self._files['solid_materials']
self._write_to_idf_format(file, idf_cte.SOLID_MATERIAL)
self._write_to_idf_format(file, layer.material_name, 'Name')
self._write_to_idf_format(file, idf_cte.ROUGHNESS, 'Roughness')
@ -299,7 +286,7 @@ class CercIdf:
self._write_to_idf_format(file, layer.visible_absorptance, 'Visible Absorptance', ';')
def _add_nomass_material(self, layer):
file = self._output_nomass_materials
file = self._files['nomass_materials']
self._write_to_idf_format(file, idf_cte.NOMASS_MATERIAL)
self._write_to_idf_format(file, layer.material_name, 'Name')
self._write_to_idf_format(file, idf_cte.ROUGHNESS, 'Roughness')
@ -336,7 +323,7 @@ class CercIdf:
glazing_index = self._windows_added_to_idf['glazing_index'] + 1
self._windows_added_to_idf[name] = True
self._windows_added_to_idf['glazing_index'] = glazing_index # increase the count
file = self._output_window_materials
file = self._files['window_materials']
self._write_to_idf_format(file, idf_cte.WINDOW_MATERIAL)
self._write_to_idf_format(file, f'glazing_{glazing_index}', 'Name')
self._write_to_idf_format(file, thermal_opening.overall_u_value, 'UFactor')
@ -356,7 +343,7 @@ class CercIdf:
if name not in self._constructions_added_to_idf:
self._constructions_added_to_idf[name] = True
file = self._output_constructions
file = self._files['constructions']
self._write_to_idf_format(file, idf_cte.CONSTRUCTION)
self._write_to_idf_format(file, name, 'Name')
eol = ','
@ -377,13 +364,13 @@ class CercIdf:
material_name = f'glazing_{i}'
if construction_name not in self._constructions_added_to_idf:
self._constructions_added_to_idf[construction_name] = True
file = self._output_constructions
file = self._files['constructions']
self._write_to_idf_format(file, idf_cte.CONSTRUCTION)
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
file = self._files['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')
@ -409,7 +396,7 @@ class CercIdf:
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
file = self._files['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')
@ -446,7 +433,7 @@ class CercIdf:
watts_per_zone_floor_area = thermal_zone.lighting.density * storeys_number
subcategory = f'ELECTRIC EQUIPMENT#{zone_name}#GeneralLights'
schedule_name = f'Lighting schedules {thermal_zone.usage_name}'
file = self._output_lighting
file = self._files['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')
@ -469,7 +456,7 @@ class CercIdf:
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
file = self._files['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')
@ -488,7 +475,7 @@ class CercIdf:
def _add_infiltration(self, thermal_zone, zone_name):
schedule_name = f'Infiltration schedules {thermal_zone.usage_name}'
infiltration = thermal_zone.infiltration_rate_system_off * cte.HOUR_TO_SECONDS
file = self._output_infiltration
file = self._files['infiltration']
self._write_to_idf_format(file, idf_cte.INFILTRATION)
self._write_to_idf_format(file, f'{zone_name}_infiltration', 'Name')
self._write_to_idf_format(file, zone_name, 'Zone or ZoneList or Space or SpaceList Name')
@ -507,7 +494,7 @@ class CercIdf:
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._output_ventilation
file = self._files['ventilation']
self._write_to_idf_format(file, idf_cte.VENTILATION)
self._write_to_idf_format(file, f'{zone_name}_ventilation', 'Name')
self._write_to_idf_format(file, zone_name, 'Zone or ZoneList or Space or SpaceList Name')
@ -536,60 +523,54 @@ class CercIdf:
self._write_to_idf_format(file, idf_cte.EMPTY, 'Maximum Outdoor Temperature Schedule Name')
self._write_to_idf_format(file, 40, 'Maximum Wind Speed', ';')
def _merge_files(self):
self._output_schedules.flush()
with open(self._output_schedules_path, 'r') as file:
lines = file.readlines()
self._idf_file.writelines(lines)
self._output_file_schedules.flush()
with open(self._output_file_schedules_path, 'r') as file:
lines = file.readlines()
self._idf_file.writelines(lines)
self._output_solid_materials.flush()
with open(self._output_solid_materials_path, 'r') as file:
lines = file.readlines()
self._idf_file.writelines(lines)
self._output_nomass_materials.flush()
with open(self._output_nomass_materials_path, 'r') as file:
lines = file.readlines()
self._idf_file.writelines(lines)
self._output_window_materials.flush()
with open(self._output_window_materials_path, 'r') as file:
lines = file.readlines()
self._idf_file.writelines(lines)
self._output_constructions.flush()
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)
# todo: this should be move down instead it's here to simplify validation
self._output_infiltration.flush()
with open(self._output_infiltration_path, 'r') as file:
lines = file.readlines()
self._idf_file.writelines(lines)
self._output_ventilation.flush()
with open(self._output_ventilation_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()
self._idf_file.writelines(lines)
def _add_thermostat(self, thermal_zone):
thermostat_name = f'Thermostat {thermal_zone.usage_name}'
heating_schedule = f'Heating thermostat schedules {thermal_zone.usage_name}'
cooling_schedule = f'Cooling thermostat schedules {thermal_zone.usage_name}'
if thermostat_name not in self._thermostat_added_to_idf:
self._thermostat_added_to_idf[thermostat_name] = True
file = self._files['thermostat']
self._write_to_idf_format(file, idf_cte.THERMOSTAT)
self._write_to_idf_format(file, thermostat_name, 'Name')
self._write_to_idf_format(file, heating_schedule, 'Heating Setpoint Schedule Name')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Constant Heating Setpoint')
self._write_to_idf_format(file, cooling_schedule, 'Cooling Setpoint Schedule Name', ';')
def _add_heating_system(self, thermal_zone, zone_name):
availability_schedule = f'HVAC AVAIL SCHEDULES {thermal_zone.usage_name}'
thermostat_name = f'Thermostat {thermal_zone.usage_name}'
file = self._files['ideal_load_system']
self._write_to_idf_format(file, idf_cte.IDEAL_LOAD_SYSTEM)
self._write_to_idf_format(file, zone_name, 'Zone Name')
self._write_to_idf_format(file, thermostat_name, 'Template Thermostat Name')
self._write_to_idf_format(file, availability_schedule, 'System Availability Schedule Name')
self._write_to_idf_format(file, 50, 'Maximum Heating Supply Air Temperature')
self._write_to_idf_format(file, 13, 'Minimum Cooling Supply Air Temperature')
self._write_to_idf_format(file, 0.0156, 'Maximum Heating Supply Air Humidity Ratio')
self._write_to_idf_format(file, 0.0077, 'Minimum Cooling Supply Air Humidity Ratio')
self._write_to_idf_format(file, 'NoLimit', 'Heating Limit')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Maximum Heating Air Flow Rate')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Maximum Sensible Heating Capacity')
self._write_to_idf_format(file, 'NoLimit', 'Cooling Limit')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Maximum Cooling Air Flow Rate')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Maximum Total Cooling Capacity')
self._write_to_idf_format(file, availability_schedule, 'Heating Availability Schedule Name')
self._write_to_idf_format(file, availability_schedule, 'Cooling Availability Schedule Name')
self._write_to_idf_format(file, 'ConstantSensibleHeatRatio', 'Dehumidification Control Type')
self._write_to_idf_format(file, 0.7, 'Cooling Sensible Heat Ratio')
self._write_to_idf_format(file, 60, 'Dehumidification Setpoint')
self._write_to_idf_format(file, 'None', 'Humidification Control Type')
self._write_to_idf_format(file, 30, 'Humidification Setpoint')
self._write_to_idf_format(file, 'None', 'Outdoor Air Method')
self._write_to_idf_format(file, 0.00944, 'Outdoor Air Flow Rate per Person')
self._write_to_idf_format(file, 0.0, 'Outdoor Air Flow Rate per Zone Floor Area')
self._write_to_idf_format(file, 0, 'Outdoor Air Flow Rate per Zone')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Design Specification Outdoor Air Object Name')
self._write_to_idf_format(file, 'None', 'Demand Controlled Ventilation Type')
self._write_to_idf_format(file, 'NoEconomizer', 'Outdoor Air Economizer Type')
self._write_to_idf_format(file, 'None', 'Heat Recovery Type')
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 _export(self):
for building in self._city.buildings:
@ -641,6 +622,8 @@ class CercIdf:
self._add_appliances(thermal_zone, building.name)
self._add_infiltration(thermal_zone, building.name)
self._add_ventilation(thermal_zone, building.name)
self._add_thermostat(thermal_zone)
self._add_heating_system(thermal_zone, building.name)
if is_target:
self._add_surfaces(building, building.name)

View File

@ -15,6 +15,8 @@ APPLIANCES = '\nOTHEREQUIPMENT,\n'
OUTPUT_CONTROL = '\nOutputControl:IlluminanceMap:Style,\n'
INFILTRATION = '\nZONEINFILTRATION:DESIGNFLOWRATE,\n'
VENTILATION = '\nZONEVENTILATION:DESIGNFLOWRATE,\n'
THERMOSTAT = '\nHVACTEMPLATE:THERMOSTAT,\n'
IDEAL_LOAD_SYSTEM = '\nHVACTEMPLATE:ZONE:IDEALLOADSAIRSYSTEM,\n'
AUTOCALCULATE = 'autocalculate'