Partial refactor
This commit is contained in:
parent
dc98b634e8
commit
78aa84c338
|
@ -1,5 +1,5 @@
|
|||
"""
|
||||
Idf exports one building to idf format
|
||||
Cerc Idf exports one city or some buildings to idf format
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Concordia CERC group
|
||||
Project Coder Guille Guillermo.GutierrezMorote@concordia.ca
|
||||
|
@ -9,15 +9,23 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
|
|||
import copy
|
||||
import os
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
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
|
||||
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
|
||||
from hub.exports.building_energy.idf_helper.idf_constructions import IdfConstructions
|
||||
from hub.exports.building_energy.idf_helper.idf_file_schedules import IdfFileSchedules
|
||||
from hub.exports.building_energy.idf_helper.idf_lighting import IdfLighting
|
||||
from hub.exports.building_energy.idf_helper.idf_materials import IdfMaterials
|
||||
from hub.exports.building_energy.idf_helper.idf_occupancy import IdfOccupancy
|
||||
from hub.exports.building_energy.idf_helper.idf_schedules import IdfSchedules
|
||||
from hub.exports.building_energy.idf_helper.idf_surfaces import IdfSurfaces
|
||||
from hub.exports.building_energy.idf_helper.idf_windows_constructions import IdfWindowsConstructions
|
||||
from hub.exports.building_energy.idf_helper.idf_windows_materials import IdfWindowsMaterials
|
||||
|
||||
|
||||
class CercIdf:
|
||||
class CercIdf(IdfBase):
|
||||
"""
|
||||
Exports city to IDF
|
||||
"""
|
||||
|
@ -28,60 +36,36 @@ class CercIdf:
|
|||
_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())
|
||||
def __init__(self, city, output_path, idf_file_path, idd_file_path, epw_file_path, target_buildings=None):
|
||||
super().__init__(city, output_path, idf_file_path, idd_file_path, epw_file_path, target_buildings)
|
||||
self._add_surfaces = IdfSurfaces.add
|
||||
self._add_schedules = IdfSchedules.add
|
||||
self._add_file_schedules = IdfFileSchedules.add
|
||||
self._add_idf_schedules = IdfSchedules.add
|
||||
self._add_constructions = IdfConstructions.add
|
||||
self._add_materials = IdfMaterials.add
|
||||
self._add_windows_materials = IdfWindowsMaterials.add
|
||||
self._add_windows_constructions = IdfWindowsConstructions.add
|
||||
self._add_occupancy = IdfOccupancy.add
|
||||
self._add_lighting = IdfLighting.add
|
||||
|
||||
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()),
|
||||
'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()),
|
||||
'dhw': str((output_path / 'dhw.idf').resolve()),
|
||||
}
|
||||
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)
|
||||
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 = []
|
||||
|
||||
if target_buildings is None:
|
||||
self._target_buildings = [building.name for building in self._city.buildings]
|
||||
else:
|
||||
for building_name in target_buildings:
|
||||
building = city.city_object(building_name)
|
||||
if building.neighbours is not None:
|
||||
self._adjacent_buildings += building.neighbours
|
||||
with open(self._idf_file_path, 'r') as base_idf:
|
||||
lines = base_idf.readlines()
|
||||
|
||||
# Create base values
|
||||
self._create_geometry_rules()
|
||||
|
||||
with open(self._output_file_path, 'w') as self._idf_file:
|
||||
self._idf_file.writelines(lines)
|
||||
self._export()
|
||||
|
||||
# Create base values
|
||||
self._create_geometry_rules()
|
||||
|
||||
def _create_geometry_rules(self):
|
||||
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 _merge_files(self):
|
||||
for file in self._files.values():
|
||||
file.close()
|
||||
|
@ -97,85 +81,6 @@ class CercIdf:
|
|||
lines = base_idf.readlines()
|
||||
self._idf_file.writelines(lines)
|
||||
|
||||
def _create_geometry_rules(self):
|
||||
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._files['appliances']
|
||||
self._write_to_idf_format(file, idf_cte.OUTPUT_CONTROL)
|
||||
self._write_to_idf_format(file, 'Comma', 'Column Separator', ';')
|
||||
|
||||
@staticmethod
|
||||
def _write_to_idf_format(file, field, comment='', eol=','):
|
||||
if comment != '':
|
||||
comment = f' !- {comment}'
|
||||
field = f' {field}{eol}'.ljust(26, ' ')
|
||||
file.write(f'{field}{comment}\n')
|
||||
else:
|
||||
file.write(f'{field}{comment}')
|
||||
|
||||
@staticmethod
|
||||
def _matrix_to_list(points, lower_corner):
|
||||
lower_x = lower_corner[0]
|
||||
lower_y = lower_corner[1]
|
||||
lower_z = lower_corner[2]
|
||||
points_list = []
|
||||
for point in points:
|
||||
point_tuple = (point[0] - lower_x, point[1] - lower_y, point[2] - lower_z)
|
||||
points_list.append(point_tuple)
|
||||
return points_list
|
||||
|
||||
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._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]
|
||||
outside_boundary_condition = idf_cte.OUTDOORS
|
||||
sun_exposure = idf_cte.SUN_EXPOSED
|
||||
wind_exposure = idf_cte.WIND_EXPOSED
|
||||
outside_boundary_condition_object = idf_cte.EMPTY
|
||||
name = f'Building_{building.name}_surface_{index}'
|
||||
construction_name = f'{boundary.construction_name} {boundary.parent_surface.type}'
|
||||
space_name = idf_cte.EMPTY
|
||||
if boundary.parent_surface.type == cte.GROUND:
|
||||
outside_boundary_condition = idf_cte.GROUND
|
||||
sun_exposure = idf_cte.NON_SUN_EXPOSED
|
||||
wind_exposure = idf_cte.NON_WIND_EXPOSED
|
||||
if boundary.parent_surface.percentage_shared is not None and boundary.parent_surface.percentage_shared > 0.5:
|
||||
outside_boundary_condition_object = f'Building_{building.name}_surface_{index}'
|
||||
outside_boundary_condition = idf_cte.SURFACE
|
||||
sun_exposure = idf_cte.NON_SUN_EXPOSED
|
||||
wind_exposure = idf_cte.NON_WIND_EXPOSED
|
||||
self._write_to_idf_format(file, idf_cte.BUILDING_SURFACE)
|
||||
self._write_to_idf_format(file, name, 'Name')
|
||||
self._write_to_idf_format(file, surface_type, 'Surface Type')
|
||||
self._write_to_idf_format(file, construction_name, 'Construction Name')
|
||||
self._write_to_idf_format(file, zone_name, 'Zone Name')
|
||||
self._write_to_idf_format(file, space_name, 'Space Name')
|
||||
self._write_to_idf_format(file, outside_boundary_condition, 'Outside Boundary Condition')
|
||||
self._write_to_idf_format(file, outside_boundary_condition_object, 'Outside Boundary Condition Object')
|
||||
self._write_to_idf_format(file, sun_exposure, 'Sun Exposure')
|
||||
self._write_to_idf_format(file, wind_exposure, 'Wind Exposure')
|
||||
self._write_to_idf_format(file, idf_cte.AUTOCALCULATE, 'View Factor to Ground')
|
||||
self._write_to_idf_format(file, idf_cte.AUTOCALCULATE, 'Number of Vertices')
|
||||
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):
|
||||
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)
|
||||
|
||||
@staticmethod
|
||||
def _create_infiltration_schedules(thermal_zone):
|
||||
_infiltration_schedules = []
|
||||
|
@ -237,225 +142,7 @@ class CercIdf:
|
|||
_schedule.values = [value for _ in range(0, amount)]
|
||||
return [_schedule]
|
||||
|
||||
def _add_file_schedules(self, usage, schedule_type, schedules):
|
||||
schedule_name = f'{schedule_type} schedules {usage}'
|
||||
for schedule in schedules:
|
||||
if schedule_name not in self._schedules_added_to_idf:
|
||||
self._schedules_added_to_idf[schedule_name] = True
|
||||
file_name = str(
|
||||
(Path(self._output_path) / f'{schedule_type} schedules {usage.replace("/", "_")}.csv').resolve())
|
||||
with open(file_name, 'w', encoding='utf8') as file:
|
||||
for value in schedule.values:
|
||||
file.write(f'{str(value)},\n')
|
||||
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')
|
||||
self._write_to_idf_format(file, Path(file_name).name, 'File Name')
|
||||
self._write_to_idf_format(file, 1, 'Column Number')
|
||||
self._write_to_idf_format(file, 0, 'Rows to Skip at Top')
|
||||
self._write_to_idf_format(file, 8760, 'Number of Hours of Data')
|
||||
self._write_to_idf_format(file, 'Comma', 'Column Separator')
|
||||
self._write_to_idf_format(file, 'No', 'Interpolate to Timestep')
|
||||
self._write_to_idf_format(file, '60', 'Minutes per Item')
|
||||
self._write_to_idf_format(file, 'Yes', 'Adjust Schedule for Daylight Savings', ';')
|
||||
|
||||
def _add_idf_schedules(self, usage, schedule_type, schedules):
|
||||
if len(schedules) < 1:
|
||||
return
|
||||
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._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')
|
||||
self._write_to_idf_format(file, 'Through: 12/31', 'Field 1')
|
||||
counter = 1
|
||||
for j, schedule in enumerate(schedules):
|
||||
_val = schedule.values
|
||||
_new_field = ''
|
||||
for day_type in schedule.day_types:
|
||||
_new_field += f' {idf_cte.idf_day_types[day_type]}'
|
||||
self._write_to_idf_format(file, f'For:{_new_field}', f'Field {j * 25 + 2}')
|
||||
counter += 1
|
||||
for i, _ in enumerate(_val):
|
||||
self._write_to_idf_format(file, f'Until: {i + 1:02d}:00,{_val[i]}', f'Field {j * 25 + 3 + i}')
|
||||
counter += 1
|
||||
self._write_to_idf_format(file, 'For AllOtherDays', f'Field {counter + 1}')
|
||||
self._write_to_idf_format(file, 'Until: 24:00,0.0', f'Field {counter + 2}', ';')
|
||||
|
||||
def _add_solid_material(self, layer):
|
||||
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')
|
||||
self._write_to_idf_format(file, layer.thickness, 'Thickness')
|
||||
self._write_to_idf_format(file, layer.conductivity, 'Conductivity')
|
||||
self._write_to_idf_format(file, layer.density, 'Density')
|
||||
self._write_to_idf_format(file, layer.specific_heat, 'Specific Heat')
|
||||
self._write_to_idf_format(file, layer.thermal_absorptance, 'Thermal Absorptance')
|
||||
self._write_to_idf_format(file, layer.solar_absorptance, 'Solar Absorptance')
|
||||
self._write_to_idf_format(file, layer.visible_absorptance, 'Visible Absorptance', ';')
|
||||
|
||||
def _add_nomass_material(self, layer):
|
||||
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')
|
||||
self._write_to_idf_format(file, layer.thermal_resistance, 'Thermal Resistance')
|
||||
self._write_to_idf_format(file, 0.9, 'Thermal Absorptance')
|
||||
self._write_to_idf_format(file, 0.7, 'Solar Absorptance')
|
||||
self._write_to_idf_format(file, 0.7, 'Visible Absorptance', ';')
|
||||
|
||||
def _add_materials(self, thermal_boundary):
|
||||
for layer in thermal_boundary.layers:
|
||||
if layer.material_name not in self._materials_added_to_idf:
|
||||
self._materials_added_to_idf[layer.material_name] = True
|
||||
if layer.no_mass:
|
||||
self._add_nomass_material(layer)
|
||||
else:
|
||||
self._add_solid_material(layer)
|
||||
|
||||
def _add_default_material(self):
|
||||
layer = Layer()
|
||||
layer.material_name = 'DefaultMaterial'
|
||||
layer.thickness = 0.1
|
||||
layer.conductivity = 0.1
|
||||
layer.density = 1000
|
||||
layer.specific_heat = 1000
|
||||
layer.thermal_absorptance = 0.9
|
||||
layer.solar_absorptance = 0.9
|
||||
layer.visible_absorptance = 0.7
|
||||
self._add_solid_material(layer)
|
||||
return layer
|
||||
|
||||
def _add_window_materials(self, thermal_opening):
|
||||
name = f'{thermal_opening.overall_u_value}_{thermal_opening.g_value}'
|
||||
if name not in self._windows_added_to_idf:
|
||||
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._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')
|
||||
self._write_to_idf_format(file, thermal_opening.g_value, 'Solar Heat Gain Coefficient', ';')
|
||||
|
||||
def _add_constructions(self, thermal_boundary):
|
||||
if thermal_boundary.layers is None:
|
||||
thermal_boundary.layers = [self._add_default_material()]
|
||||
name = f'{thermal_boundary.construction_name} {thermal_boundary.parent_surface.type}'
|
||||
|
||||
if name not in self._constructions_added_to_idf:
|
||||
self._constructions_added_to_idf[name] = True
|
||||
file = self._files['constructions']
|
||||
self._write_to_idf_format(file, idf_cte.CONSTRUCTION)
|
||||
self._write_to_idf_format(file, name, 'Name')
|
||||
eol = ','
|
||||
if len(thermal_boundary.layers) == 1:
|
||||
eol = ';'
|
||||
self._write_to_idf_format(file, thermal_boundary.layers[0].material_name, 'Outside Layer', eol)
|
||||
for i in range(1, len(thermal_boundary.layers) - 1):
|
||||
comment = f'Layer {i + 1}'
|
||||
material_name = thermal_boundary.layers[i].material_name
|
||||
if i == len(thermal_boundary.layers) - 2:
|
||||
eol = ';'
|
||||
self._write_to_idf_format(file, material_name, comment, eol)
|
||||
|
||||
def _add_windows_constructions(self):
|
||||
glazing_index = self._windows_added_to_idf['glazing_index'] + 1
|
||||
for i in range(1, glazing_index):
|
||||
construction_name = f'window_construction_{i}'
|
||||
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._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._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')
|
||||
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._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')
|
||||
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'
|
||||
schedule_name = f'Lighting schedules {thermal_zone.usage_name}'
|
||||
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')
|
||||
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, '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}'
|
||||
|
@ -617,7 +304,7 @@ class CercIdf:
|
|||
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:
|
||||
window_material['Solar_Heat_Gain_Coefficient'] == thermal_opening.g_value:
|
||||
return
|
||||
|
||||
order = str(len(self._idf.idfobjects[self._WINDOW_MATERIAL_SIMPLE]) + 1)
|
||||
|
|
75
hub/exports/building_energy/idf_helper/idf_base.py
Normal file
75
hub/exports/building_energy/idf_helper/idf_base.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import hub.exports.building_energy.idf_helper as idf_cte
|
||||
|
||||
|
||||
class IdfBase:
|
||||
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._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()),
|
||||
'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()),
|
||||
'dhw': str((output_path / 'dhw.idf').resolve()),
|
||||
}
|
||||
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)
|
||||
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 = []
|
||||
|
||||
if target_buildings is None:
|
||||
self._target_buildings = [building.name for building in self._city.buildings]
|
||||
else:
|
||||
for building_name in target_buildings:
|
||||
building = city.city_object(building_name)
|
||||
if building.neighbours is not None:
|
||||
self._adjacent_buildings += building.neighbours
|
||||
|
||||
def _create_output_control_lighting(self):
|
||||
file = self._files['appliances']
|
||||
self._write_to_idf_format(file, idf_cte.OUTPUT_CONTROL)
|
||||
self._write_to_idf_format(file, 'Comma', 'Column Separator', ';')
|
||||
|
||||
@staticmethod
|
||||
def _write_to_idf_format(file, field, comment='', eol=','):
|
||||
if comment != '':
|
||||
comment = f' !- {comment}'
|
||||
field = f' {field}{eol}'.ljust(26, ' ')
|
||||
file.write(f'{field}{comment}\n')
|
||||
else:
|
||||
file.write(f'{field}{comment}')
|
||||
|
||||
@staticmethod
|
||||
def _matrix_to_list(points, lower_corner):
|
||||
lower_x = lower_corner[0]
|
||||
lower_y = lower_corner[1]
|
||||
lower_z = lower_corner[2]
|
||||
points_list = []
|
||||
for point in points:
|
||||
point_tuple = (point[0] - lower_x, point[1] - lower_y, point[2] - lower_z)
|
||||
points_list.append(point_tuple)
|
||||
return points_list
|
52
hub/exports/building_energy/idf_helper/idf_constructions.py
Normal file
52
hub/exports/building_energy/idf_helper/idf_constructions.py
Normal file
|
@ -0,0 +1,52 @@
|
|||
from hub.city_model_structure.building_demand.layer import Layer
|
||||
from hub.exports.building_energy.cerc_idf import CercIdf
|
||||
import hub.exports.building_energy.idf_helper as idf_cte
|
||||
|
||||
|
||||
class IdfConstructions(CercIdf):
|
||||
def _add_solid_material(self, layer):
|
||||
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')
|
||||
self._write_to_idf_format(file, layer.thickness, 'Thickness')
|
||||
self._write_to_idf_format(file, layer.conductivity, 'Conductivity')
|
||||
self._write_to_idf_format(file, layer.density, 'Density')
|
||||
self._write_to_idf_format(file, layer.specific_heat, 'Specific Heat')
|
||||
self._write_to_idf_format(file, layer.thermal_absorptance, 'Thermal Absorptance')
|
||||
self._write_to_idf_format(file, layer.solar_absorptance, 'Solar Absorptance')
|
||||
self._write_to_idf_format(file, layer.visible_absorptance, 'Visible Absorptance', ';')
|
||||
|
||||
def _add_default_material(self):
|
||||
layer = Layer()
|
||||
layer.material_name = 'DefaultMaterial'
|
||||
layer.thickness = 0.1
|
||||
layer.conductivity = 0.1
|
||||
layer.density = 1000
|
||||
layer.specific_heat = 1000
|
||||
layer.thermal_absorptance = 0.9
|
||||
layer.solar_absorptance = 0.9
|
||||
layer.visible_absorptance = 0.7
|
||||
self._add_solid_material(layer)
|
||||
return layer
|
||||
|
||||
def add(self, thermal_boundary):
|
||||
if thermal_boundary.layers is None:
|
||||
thermal_boundary.layers = [self._add_default_material()]
|
||||
name = f'{thermal_boundary.construction_name} {thermal_boundary.parent_surface.type}'
|
||||
|
||||
if name not in self._constructions_added_to_idf:
|
||||
self._constructions_added_to_idf[name] = True
|
||||
file = self._files['constructions']
|
||||
self._write_to_idf_format(file, idf_cte.CONSTRUCTION)
|
||||
self._write_to_idf_format(file, name, 'Name')
|
||||
eol = ','
|
||||
if len(thermal_boundary.layers) == 1:
|
||||
eol = ';'
|
||||
self._write_to_idf_format(file, thermal_boundary.layers[0].material_name, 'Outside Layer', eol)
|
||||
for i in range(1, len(thermal_boundary.layers) - 1):
|
||||
comment = f'Layer {i + 1}'
|
||||
material_name = thermal_boundary.layers[i].material_name
|
||||
if i == len(thermal_boundary.layers) - 2:
|
||||
eol = ';'
|
||||
self._write_to_idf_format(file, material_name, comment, eol)
|
29
hub/exports/building_energy/idf_helper/idf_file_schedules.py
Normal file
29
hub/exports/building_energy/idf_helper/idf_file_schedules.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
from pathlib import Path
|
||||
import hub.exports.building_energy.idf_helper as idf_cte
|
||||
|
||||
from hub.exports.building_energy.cerc_idf import CercIdf
|
||||
|
||||
|
||||
class IdfFileSchedules(CercIdf):
|
||||
def add(self, usage, schedule_type, schedules):
|
||||
schedule_name = f'{schedule_type} schedules {usage}'
|
||||
for schedule in schedules:
|
||||
if schedule_name not in self._schedules_added_to_idf:
|
||||
self._schedules_added_to_idf[schedule_name] = True
|
||||
file_name = str(
|
||||
(Path(self._output_path) / f'{schedule_type} schedules {usage.replace("/", "_")}.csv').resolve())
|
||||
with open(file_name, 'w', encoding='utf8') as file:
|
||||
for value in schedule.values:
|
||||
file.write(f'{str(value)},\n')
|
||||
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')
|
||||
self._write_to_idf_format(file, Path(file_name).name, 'File Name')
|
||||
self._write_to_idf_format(file, 1, 'Column Number')
|
||||
self._write_to_idf_format(file, 0, 'Rows to Skip at Top')
|
||||
self._write_to_idf_format(file, 8760, 'Number of Hours of Data')
|
||||
self._write_to_idf_format(file, 'Comma', 'Column Separator')
|
||||
self._write_to_idf_format(file, 'No', 'Interpolate to Timestep')
|
||||
self._write_to_idf_format(file, '60', 'Minutes per Item')
|
||||
self._write_to_idf_format(file, 'Yes', 'Adjust Schedule for Daylight Savings', ';')
|
27
hub/exports/building_energy/idf_helper/idf_lighting.py
Normal file
27
hub/exports/building_energy/idf_helper/idf_lighting.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
from hub.exports.building_energy.cerc_idf import CercIdf
|
||||
import hub.exports.building_energy.idf_helper as idf_cte
|
||||
|
||||
|
||||
class IdfLighting(CercIdf):
|
||||
def add(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'
|
||||
schedule_name = f'Lighting schedules {thermal_zone.usage_name}'
|
||||
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')
|
||||
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, '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', ';')
|
36
hub/exports/building_energy/idf_helper/idf_materials.py
Normal file
36
hub/exports/building_energy/idf_helper/idf_materials.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
from hub.exports.building_energy.cerc_idf import CercIdf
|
||||
import hub.exports.building_energy.idf_helper as idf_cte
|
||||
|
||||
|
||||
class IdfMaterials(CercIdf):
|
||||
def _add_solid_material(self, layer):
|
||||
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')
|
||||
self._write_to_idf_format(file, layer.thickness, 'Thickness')
|
||||
self._write_to_idf_format(file, layer.conductivity, 'Conductivity')
|
||||
self._write_to_idf_format(file, layer.density, 'Density')
|
||||
self._write_to_idf_format(file, layer.specific_heat, 'Specific Heat')
|
||||
self._write_to_idf_format(file, layer.thermal_absorptance, 'Thermal Absorptance')
|
||||
self._write_to_idf_format(file, layer.solar_absorptance, 'Solar Absorptance')
|
||||
self._write_to_idf_format(file, layer.visible_absorptance, 'Visible Absorptance', ';')
|
||||
|
||||
def _add_nomass_material(self, layer):
|
||||
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')
|
||||
self._write_to_idf_format(file, layer.thermal_resistance, 'Thermal Resistance')
|
||||
self._write_to_idf_format(file, 0.9, 'Thermal Absorptance')
|
||||
self._write_to_idf_format(file, 0.7, 'Solar Absorptance')
|
||||
self._write_to_idf_format(file, 0.7, 'Visible Absorptance', ';')
|
||||
|
||||
def add(self, thermal_boundary):
|
||||
for layer in thermal_boundary.layers:
|
||||
if layer.material_name not in self._materials_added_to_idf:
|
||||
self._materials_added_to_idf[layer.material_name] = True
|
||||
if layer.no_mass:
|
||||
self._add_nomass_material(layer)
|
||||
else:
|
||||
self._add_solid_material(layer)
|
46
hub/exports/building_energy/idf_helper/idf_occupancy.py
Normal file
46
hub/exports/building_energy/idf_helper/idf_occupancy.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
from hub.exports.building_energy.cerc_idf import CercIdf
|
||||
import hub.exports.building_energy.idf_helper as idf_cte
|
||||
|
||||
|
||||
class IdfOccupancy(CercIdf):
|
||||
def add(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._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')
|
||||
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', ';')
|
29
hub/exports/building_energy/idf_helper/idf_schedules.py
Normal file
29
hub/exports/building_energy/idf_helper/idf_schedules.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
from hub.exports.building_energy.cerc_idf import CercIdf
|
||||
import hub.exports.building_energy.idf_helper as idf_cte
|
||||
|
||||
|
||||
class IdfSchedules(CercIdf):
|
||||
def add(self, usage, schedule_type, schedules):
|
||||
if len(schedules) < 1:
|
||||
return
|
||||
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._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')
|
||||
self._write_to_idf_format(file, 'Through: 12/31', 'Field 1')
|
||||
counter = 1
|
||||
for j, schedule in enumerate(schedules):
|
||||
_val = schedule.values
|
||||
_new_field = ''
|
||||
for day_type in schedule.day_types:
|
||||
_new_field += f' {idf_cte.idf_day_types[day_type]}'
|
||||
self._write_to_idf_format(file, f'For:{_new_field}', f'Field {j * 25 + 2}')
|
||||
counter += 1
|
||||
for i, _ in enumerate(_val):
|
||||
self._write_to_idf_format(file, f'Until: {i + 1:02d}:00,{_val[i]}', f'Field {j * 25 + 3 + i}')
|
||||
counter += 1
|
||||
self._write_to_idf_format(file, 'For AllOtherDays', f'Field {counter + 1}')
|
||||
self._write_to_idf_format(file, 'Until: 24:00,0.0', f'Field {counter + 2}', ';')
|
52
hub/exports/building_energy/idf_helper/idf_surfaces.py
Normal file
52
hub/exports/building_energy/idf_helper/idf_surfaces.py
Normal file
|
@ -0,0 +1,52 @@
|
|||
import hub.exports.building_energy.idf_helper as idf_cte
|
||||
import hub.helpers.constants as cte
|
||||
from hub.exports.building_energy.cerc_idf import CercIdf
|
||||
|
||||
|
||||
class IdfSurfaces(CercIdf):
|
||||
def add(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._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]
|
||||
outside_boundary_condition = idf_cte.OUTDOORS
|
||||
sun_exposure = idf_cte.SUN_EXPOSED
|
||||
wind_exposure = idf_cte.WIND_EXPOSED
|
||||
outside_boundary_condition_object = idf_cte.EMPTY
|
||||
name = f'Building_{building.name}_surface_{index}'
|
||||
construction_name = f'{boundary.construction_name} {boundary.parent_surface.type}'
|
||||
space_name = idf_cte.EMPTY
|
||||
if boundary.parent_surface.type == cte.GROUND:
|
||||
outside_boundary_condition = idf_cte.GROUND
|
||||
sun_exposure = idf_cte.NON_SUN_EXPOSED
|
||||
wind_exposure = idf_cte.NON_WIND_EXPOSED
|
||||
if boundary.parent_surface.percentage_shared is not None and boundary.parent_surface.percentage_shared > 0.5:
|
||||
outside_boundary_condition_object = f'Building_{building.name}_surface_{index}'
|
||||
outside_boundary_condition = idf_cte.SURFACE
|
||||
sun_exposure = idf_cte.NON_SUN_EXPOSED
|
||||
wind_exposure = idf_cte.NON_WIND_EXPOSED
|
||||
self._write_to_idf_format(file, idf_cte.BUILDING_SURFACE)
|
||||
self._write_to_idf_format(file, name, 'Name')
|
||||
self._write_to_idf_format(file, surface_type, 'Surface Type')
|
||||
self._write_to_idf_format(file, construction_name, 'Construction Name')
|
||||
self._write_to_idf_format(file, zone_name, 'Zone Name')
|
||||
self._write_to_idf_format(file, space_name, 'Space Name')
|
||||
self._write_to_idf_format(file, outside_boundary_condition, 'Outside Boundary Condition')
|
||||
self._write_to_idf_format(file, outside_boundary_condition_object, 'Outside Boundary Condition Object')
|
||||
self._write_to_idf_format(file, sun_exposure, 'Sun Exposure')
|
||||
self._write_to_idf_format(file, wind_exposure, 'Wind Exposure')
|
||||
self._write_to_idf_format(file, idf_cte.AUTOCALCULATE, 'View Factor to Ground')
|
||||
self._write_to_idf_format(file, idf_cte.AUTOCALCULATE, 'Number of Vertices')
|
||||
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):
|
||||
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)
|
|
@ -0,0 +1,16 @@
|
|||
from hub.exports.building_energy.cerc_idf import CercIdf
|
||||
import hub.exports.building_energy.idf_helper as idf_cte
|
||||
|
||||
|
||||
class IdfWindowsConstructions(CercIdf):
|
||||
def add(self):
|
||||
glazing_index = self._windows_added_to_idf['glazing_index'] + 1
|
||||
for i in range(1, glazing_index):
|
||||
construction_name = f'window_construction_{i}'
|
||||
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._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', ';')
|
|
@ -0,0 +1,16 @@
|
|||
from hub.exports.building_energy.cerc_idf import CercIdf
|
||||
import hub.exports.building_energy.idf_helper as idf_cte
|
||||
|
||||
|
||||
class IdfWindowsMaterials(CercIdf):
|
||||
def add(self, thermal_opening):
|
||||
name = f'{thermal_opening.overall_u_value}_{thermal_opening.g_value}'
|
||||
if name not in self._windows_added_to_idf:
|
||||
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._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')
|
||||
self._write_to_idf_format(file, thermal_opening.g_value, 'Solar Heat Gain Coefficient', ';')
|
21
hub/exports/building_energy/idf_helper/idf_zones.py
Normal file
21
hub/exports/building_energy/idf_helper/idf_zones.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
from hub.exports.building_energy.cerc_idf import CercIdf
|
||||
import hub.exports.building_energy.idf_helper as idf_cte
|
||||
|
||||
|
||||
class IdfZones(CercIdf):
|
||||
def add(self, thermal_zone, zone_name):
|
||||
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')
|
||||
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', ';')
|
|
@ -11,7 +11,7 @@ import requests
|
|||
|
||||
from hub.exports.building_energy.energy_ade import EnergyAde
|
||||
from hub.exports.building_energy.idf import Idf
|
||||
from hub.exports.building_energy.cerc_idf import CercIdf
|
||||
from hub.exports.building_energy.idf_helper import CercIdf
|
||||
from hub.exports.building_energy.insel.insel_monthly_energy_balance import InselMonthlyEnergyBalance
|
||||
from hub.helpers.utils import validate_import_export_type
|
||||
from hub.imports.weather.helpers.weather import Weather as wh
|
||||
|
|
|
@ -144,7 +144,7 @@ class TestExports(TestCase):
|
|||
UsageFactory('nrcan', city).enrich()
|
||||
WeatherFactory('epw', city).enrich()
|
||||
try:
|
||||
EnergyBuildingsExportsFactory('cerc_idf', city, self._output_path)._cerc_idf
|
||||
EnergyBuildingsExportsFactory('idf_helper', city, self._output_path)._cerc_idf
|
||||
|
||||
except Exception:
|
||||
self.fail("Idf ExportsFactory raised ExceptionType unexpectedly!")
|
||||
|
|
Loading…
Reference in New Issue
Block a user