cerc_idf basic implementation

This commit is contained in:
Guille Gutierrez 2024-09-12 06:57:15 +02:00
parent c4636c2c3c
commit 5401064905
6 changed files with 563 additions and 7 deletions

View File

@ -0,0 +1,422 @@
"""
Idf exports one building to idf format
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 Gavalda Torrellas oriol.gavalda@concordia.ca
"""
import copy
from datetime import datetime
from pathlib import Path
import hub.helpers.constants as cte
import hub.exports.building_energy.idf_helper as idf_cte
from hub.city_model_structure.attributes.schedule import Schedule
from hub.city_model_structure.building_demand.layer import Layer
class CercIdf:
"""
Exports city to IDF
"""
_schedules_added_to_idf = {}
_materials_added_to_idf = {}
_windows_added_to_idf = {'glazing_index': 0}
_constructions_added_to_idf = {}
def __init__(self, city, output_path, idf_file_path, idd_file_path, epw_file_path, export_type="Surfaces",
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_surfaces_path = str((output_path / 'surfaces.idf').resolve())
self._output_file_path = str((output_path / f'{city.name}.idf').resolve())
self._export_type = export_type
self._idd_file_path = str(idd_file_path)
self._idf_file_path = str(idf_file_path)
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()
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_surfaces = open(self._output_surfaces_path, 'w')
with open(self._output_file_path, 'w') as self._idf_file:
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()
@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._output_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 = ','
for i, coordinate in enumerate(coordinates):
if i == len(coordinates) -1:
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)
@staticmethod
def _create_infiltration_schedules(thermal_zone):
_infiltration_schedules = []
if thermal_zone.thermal_control is None:
return []
for hvac_availability_schedule in thermal_zone.thermal_control.hvac_availability_schedules:
_schedule = Schedule()
_schedule.type = cte.INFILTRATION
_schedule.data_type = cte.FRACTION
_schedule.time_step = cte.HOUR
_schedule.time_range = cte.DAY
_schedule.day_types = copy.deepcopy(hvac_availability_schedule.day_types)
_infiltration_values = []
for hvac_value in hvac_availability_schedule.values:
if hvac_value == 0:
_infiltration_values.append(1.0)
else:
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)
return _infiltration_schedules
@staticmethod
def _create_ventilation_schedules(thermal_zone):
_ventilation_schedules = []
if thermal_zone.thermal_control is None:
return []
for hvac_availability_schedule in thermal_zone.thermal_control.hvac_availability_schedules:
_schedule = Schedule()
_schedule.type = cte.VENTILATION
_schedule.data_type = cte.FRACTION
_schedule.time_step = cte.HOUR
_schedule.time_range = cte.DAY
_schedule.day_types = copy.deepcopy(hvac_availability_schedule.day_types)
_ventilation_schedules = thermal_zone.thermal_control.hvac_availability_schedules
return _ventilation_schedules
@staticmethod
def _create_constant_value_schedules(value, amount):
_schedule = Schedule()
_schedule.type = ''
_schedule.data_type = cte.ANY_NUMBER
_schedule.time_step = cte.HOUR
_schedule.time_range = cte.DAY
_schedule.day_types = ['monday',
'tuesday',
'wednesday',
'thursday',
'friday',
'saturday',
'sunday',
'holiday',
'winter_design_day',
'summer_design_day']
_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._output_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._output_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._output_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._output_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._output_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):
"""
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}'
if name not in self._constructions_added_to_idf:
self._constructions_added_to_idf[name] = True
file = self._output_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._output_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 _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_surfaces.flush()
with open(self._output_surfaces_path, 'r') as file:
lines = file.readlines()
self._idf_file.writelines(lines)
def _export(self):
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
if occ.occupancy_density == 0:
total_heat = 0
else:
total_heat = (
occ.sensible_convective_internal_gain +
occ.sensible_radiative_internal_gain +
occ.latent_internal_gain
) / occ.occupancy_density
self._add_idf_schedules(usage, 'Infiltration', self._create_infiltration_schedules(thermal_zone))
self._add_idf_schedules(usage, 'Ventilation', self._create_ventilation_schedules(thermal_zone))
self._add_idf_schedules(usage, 'Occupancy', thermal_zone.occupancy.occupancy_schedules)
self._add_idf_schedules(usage, 'HVAC AVAIL', thermal_zone.thermal_control.hvac_availability_schedules)
self._add_idf_schedules(usage, 'Heating thermostat', thermal_zone.thermal_control.heating_set_point_schedules)
self._add_idf_schedules(usage, 'Cooling thermostat', thermal_zone.thermal_control.cooling_set_point_schedules)
self._add_idf_schedules(usage, 'Lighting', thermal_zone.lighting.schedules)
self._add_idf_schedules(usage, 'Appliance', thermal_zone.appliances.schedules)
self._add_idf_schedules(usage, 'DHW_prof', thermal_zone.domestic_hot_water.schedules)
self._add_idf_schedules(usage, 'DHW_temp', self._create_constant_value_schedules(service_temperature, 24))
self._add_idf_schedules(usage, 'Activity Level', self._create_constant_value_schedules(total_heat, 24))
self._add_file_schedules(usage, 'cold_temp', self._create_constant_value_schedules(building.cold_water_temperature[cte.HOUR], 24))
for thermal_boundary in thermal_zone.thermal_boundaries:
self._add_materials(thermal_boundary)
self._add_constructions(thermal_boundary)
for thermal_opening in thermal_boundary.thermal_openings:
self._add_window_materials(thermal_opening)
self._add_windows_constructions()
if is_target:
self._add_surfaces(building, building.name)
# Merge files
self._merge_files()

View File

@ -107,6 +107,7 @@ class Idf:
else:
for building_name in target_buildings:
building = city.city_object(building_name)
print('Name: ', building_name)
if building.neighbours is not None:
self._adjacent_buildings += building.neighbours
self._export()
@ -520,7 +521,7 @@ class Idf:
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.remoidf_surface_typeve(building.name)
self._target_buildings.remove(building.name)
is_target = False
continue
for thermal_zone in internal_zone.thermal_zones_from_internal_zones:
@ -571,9 +572,8 @@ class Idf:
if self._export_type == "Surfaces":
if is_target:
if building.thermal_zones_from_internal_zones is not None:
start = datetime.datetime.now()
self._add_surfaces(building, building.name)
print(f'add surfaces {datetime.datetime.now() - start}')
pass
# self._add_surfaces(building, building.name)
else:
self._add_pure_geometry(building, building.name)
else:
@ -624,6 +624,10 @@ class Idf:
self._idf.removeidfobject(window)
self._idf.saveas(str(self._output_file))
for building in self._city.buildings:
if self._export_type == "Surfaces":
if is_target and building.thermal_zones_from_internal_zones is not None:
self._add_surfaces(building, building.name)
return self._idf
def run(self):
@ -703,8 +707,13 @@ class Idf:
self._idf.set_wwr(wwr)
def _add_surfaces(self, building, zone_name):
start = datetime.datetime.now()
print(f'thermal_zones {len(building.thermal_zones_from_internal_zones)}')
for thermal_zone in building.thermal_zones_from_internal_zones:
print(f'thermal zone: {datetime.datetime.now() - start}')
for index, boundary in enumerate(thermal_zone.thermal_boundaries):
print(f'{index} boundary: {datetime.datetime.now() - start}')
idf_surface_type = self.idf_surfaces[boundary.parent_surface.type]
outside_boundary_condition = 'Outdoors'
sun_exposure = 'SunExposed'
@ -731,12 +740,14 @@ class Idf:
else:
construction_name = f'{boundary.construction_name} {boundary.parent_surface.type}'
_kwargs['Construction_Name'] = construction_name
start = datetime.datetime.now()
surface = self._idf.newidfobject(self._SURFACE, **_kwargs)
print(f'create surface: {datetime.datetime.now() - start}')
coordinates = self._matrix_to_list(boundary.parent_surface.solid_polygon.coordinates,
self._city.lower_corner)
surface.setcoords(coordinates)
print(f'set coords surface: {datetime.datetime.now() - start}')
if self._lod >= 3:
for internal_zone in building.internal_zones:

View File

@ -0,0 +1,62 @@
!- Linux Line endings
Version,
24.1; !- Version Identifier
SimulationControl,
No, !- Do Zone Sizing Calculation
No, !- Do System Sizing Calculation
No, !- Do Plant Sizing Calculation
No, !- Run Simulation for Sizing Periods
Yes, !- Run Simulation for Weather File Run Periods
No, !- Do HVAC Sizing Simulation for Sizing Periods
1; !- Maximum Number of HVAC Sizing Simulation Passes
Building,
Buildings in b'Montreal', !- Name
0, !- North Axis
Suburbs, !- Terrain
0.04, !- Loads Convergence Tolerance Value
0.4, !- Temperature Convergence Tolerance Value
FullExterior, !- Solar Distribution
25, !- Maximum Number of Warmup Days
6; !- Minimum Number of Warmup Days
Timestep,
4; !- Number of Timesteps per Hour
RunPeriod,
Run Period 1, !- Name
1, !- Begin Month
1, !- Begin Day of Month
, !- Begin Year
12, !- End Month
31, !- End Day of Month
, !- End Year
Tuesday, !- Day of Week for Start Day
Yes, !- Use Weather File Holidays and Special Days
Yes, !- Use Weather File Daylight Saving Period
No, !- Apply Weekend Holiday Rule
Yes, !- Use Weather File Rain Indicators
Yes; !- Use Weather File Snow Indicators
SCHEDULETYPELIMITS,
Any Number, !- Name
, !- Lower Limit Value
, !- Upper Limit Value
, !- Numeric Type
Dimensionless; !- Unit Type
SCHEDULETYPELIMITS,
Fraction, !- Name
0, !- Lower Limit Value
1, !- Upper Limit Value
Continuous, !- Numeric Type
Dimensionless; !- Unit Type
SCHEDULETYPELIMITS,
On/Off, !- Name
0, !- Lower Limit Value
1, !- Upper Limit Value
Discrete, !- Numeric Type
Dimensionless; !- Unit Type

View File

@ -0,0 +1,47 @@
import hub.helpers.constants as cte
BUILDING_SURFACE = '\nBUILDINGSURFACE:DETAILED,\n'
COMPACT_SCHEDULE = '\nSCHEDULE:COMPACT,\n'
FILE_SCHEDULE = '\nSCHEDULE:FILE,\n'
NOMASS_MATERIAL = '\nMATERIAL:NOMASS,\n'
SOLID_MATERIAL = '\nMATERIAL,\n'
WINDOW_MATERIAL = '\nWINDOWMATERIAL:SIMPLEGLAZINGSYSTEM,\n'
CONSTRUCTION = '\nCONSTRUCTION,\n'
AUTOCALCULATE = 'autocalculate'
ROUGHNESS = 'MediumRough'
OUTDOORS = 'Outdoors'
GROUND = 'Ground'
SURFACE = 'Surface'
SUN_EXPOSED = 'SunExposed'
WIND_EXPOSED = 'WindExposed'
NON_SUN_EXPOSED = 'NoSun'
NON_WIND_EXPOSED = 'NoWind'
EMPTY = ''
idf_surfaces = {
cte.WALL: 'wall',
cte.GROUND: 'floor',
cte.ROOF: 'roof'
}
idf_type_limits = {
cte.ON_OFF: 'on/off',
cte.FRACTION: 'Fraction',
cte.ANY_NUMBER: 'Any Number',
cte.CONTINUOUS: 'Continuous',
cte.DISCRETE: 'Discrete'
}
idf_day_types = {
cte.MONDAY: 'Monday',
cte.TUESDAY: 'Tuesday',
cte.WEDNESDAY: 'Wednesday',
cte.THURSDAY: 'Thursday',
cte.FRIDAY: 'Friday',
cte.SATURDAY: 'Saturday',
cte.SUNDAY: 'Sunday',
cte.HOLIDAY: 'Holidays',
cte.WINTER_DESIGN_DAY: 'WinterDesignDay',
cte.SUMMER_DESIGN_DAY: 'SummerDesignDay'
}

View File

@ -11,6 +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.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
@ -60,6 +61,18 @@ class EnergyBuildingsExportsFactory:
return Idf(self._city, self._path, (idf_data_path / 'Minimal.idf'), (idf_data_path / 'Energy+.idd'), weather_path,
target_buildings=self._target_buildings)
@property
def _cerc_idf(self):
idf_data_path = (Path(__file__).parent / './building_energy/idf_files/').resolve()
url = wh().epw_file(self._city.region_code)
weather_path = (Path(__file__).parent.parent / f'data/weather/epw/{url.rsplit("/", 1)[1]}').resolve()
if not weather_path.exists():
with open(weather_path, 'wb') as epw_file:
epw_file.write(requests.get(url, allow_redirects=True).content)
return CercIdf(self._city, self._path, (idf_data_path / 'base.idf'), (idf_data_path / 'Energy+.idd'), weather_path,
target_buildings=self._target_buildings)
@property
def _insel_monthly_energy_balance(self):
"""

View File

@ -144,6 +144,7 @@ class TestExports(TestCase):
UsageFactory('nrcan', city).enrich()
WeatherFactory('epw', city).enrich()
try:
EnergyBuildingsExportsFactory('idf', city, self._output_path, target_buildings=[1]).export()
EnergyBuildingsExportsFactory('cerc_idf', city, self._output_path)._cerc_idf
except Exception:
self.fail("Idf ExportsFactory raised ExceptionType unexpectedly!")