"""Methods to generate EXP templates for Trane TRACE."""
from ladybug.datatype.area import Area
from ladybug.datatype.conductivity import Conductivity
from ladybug.datatype.density import Density
from ladybug.datatype.distance import Distance
from ladybug.datatype.power import Power
from ladybug.datatype.energyflux import EnergyFlux
from ladybug.datatype.volumeflowrate import VolumeFlowRate
from ladybug.datatype.volumeflowrateintensity import VolumeFlowRateIntensity
from ladybug.datatype.temperature import Temperature
from ladybug.datatype.specificheatcapacity import SpecificHeatCapacity
from ladybug.datatype.rvalue import RValue
from ladybug.datatype.uvalue import UValue
from honeybee.typing import readable_short_name, clean_ep_string
from honeybee_energy.material.opaque import EnergyMaterial, EnergyMaterialNoMass
from honeybee_energy.construction.opaque import OpaqueConstruction
from honeybee_energy.material.glazing import EnergyWindowMaterialSimpleGlazSys
from honeybee_energy.construction.window import WindowConstruction
from honeybee_energy.constructionset import ConstructionSet
from honeybee_energy.schedule.ruleset import ScheduleRuleset
from honeybee_energy.load.people import People
from honeybee_energy.load.lighting import Lighting
from honeybee_energy.load.equipment import ElectricEquipment, GasEquipment
from honeybee_energy.load.ventilation import Ventilation
from honeybee_energy.load.infiltration import Infiltration
from honeybee_energy.load.exhaust import ExhaustAir
from honeybee_energy.load.setpoint import Setpoint
from honeybee_energy.programtype import ProgramType
from honeybee_energy.lib.scheduletypelimits import temperature, activity_level
area_dt = Area()
conductivity_dt = Conductivity()
density_dt = Density()
distance_dt = Distance()
power_dt = Power()
flux_dt = EnergyFlux()
flow_dt = VolumeFlowRate()
flow_int_dt = VolumeFlowRateIntensity()
temp_dt = Temperature()
specific_heat_capacity_dt = SpecificHeatCapacity()
rvalue_dt = RValue()
uvalue_dt = UValue()
"""____________TRANSLATORS TO EXP____________"""
[docs]
def people_to_exp(people, si_units=False):
"""Get a TRACE 700 "T.LOAD_PEOPLE" entry string from a Honeybee People object.
Args:
people: A Honeybee People object.
si_units: Boolean to note whether the units of the values in the resulting
matrix are in SI (True) instead of IP (False). (Default: False).
Returns:
A text string for a TRACE 700 T.LOAD_PEOPLE library entry.
"""
description = readable_short_name(people.display_name, max_length=40)
radiant_pct = people.radiant_fraction * 100.0
area_unit = 'm2' if si_units else 'ft2'
power_unit = 'W' if si_units else 'Btu/h'
amount_unit_code = 2 if si_units else 1 # 1 = sq ft/person, 2 = sq m/person
load_unit_code = 8 if si_units else 7 # 7 = Btu/h, 8 = W
people_amount = area_dt.to_unit([people.area_per_person], area_unit, 'm2')[0]
sensible_ppl = power_dt.to_unit([people.activity_max_sensible], power_unit, 'W')[0]
latent_ppl = power_dt.to_unit([people.activity_max_latent], power_unit, 'W')[0]
# People;
# Description;
# 1;
# People amount;
# People amount unit;
# Sensible load;
# Sensible load unit;
# Latent load;
# Latent load unit;
# Longwave radiant %;
exp_line = (
f'People;{description};1;{people_amount:.1f};{amount_unit_code};'
f'{sensible_ppl:.0f};{load_unit_code};{latent_ppl:.0f};{load_unit_code};{radiant_pct:.0f};'
)
return exp_line
[docs]
def lighting_to_exp(lighting, fixture_type='SUSFLUOR'):
"""Get a TRACE 700 "T.LOAD_LIGHTS" entry string from a Honeybee Lighting object.
Args:
lighting: A Honeybee Lighting object.
fixture_type: Optional text string for the lighting fixture type.
(Default: 'SUSFLUOR').
Returns:
A text string for a TRACE 700 T.LOAD_LIGHTS library entry.
"""
description = lighting.display_name
plenum_pct = lighting.return_air_fraction * 100.0
radiant_pct = lighting.radiant_fraction * 100.0
visible_pct = lighting.visible_fraction * 100.0
# Lighting;
# Description;
# 1;
# Fixture type;
# Plenum %;
# Ballast factor;
# Longwave radiant %;
# Shortwave radiant %;
exp_line = (
f'Lighting;{description};1;{fixture_type};{plenum_pct:.0f};1;'
f'{radiant_pct:.0f};{visible_pct:.0f};'
)
return exp_line
[docs]
def equipment_to_exp(equipment, si_units=False, watts_per_area=None):
"""Get a TRACE 700 "T.LOAD_MISEQUIP" entry string from a Honeybee Equipment object.
Args:
equipment: A Honeybee ElectricEquipment or GasEquipment object.
si_units: Boolean to note whether the units of the values in the resulting
matrix are in SI (True) instead of IP (False). (Default: False).
watts_per_area: Optional float for total W/m2 override to represent a combined
electric + gas equipment load density. If None, equipment.watts_per_area
will be used. (Default: None).
Returns:
A text string for a TRACE 700 T.LOAD_MISEQUIP library entry.
"""
description = readable_short_name(equipment.display_name, max_length=40)
sensible_pct = (1.0 - equipment.latent_fraction) * 100.0
radiant_pct = equipment.radiant_fraction * 100.0
lost_pct = equipment.lost_fraction * 100.0
room_pct = 100.0 - lost_pct
plenum_pct = 0.0
meter_code = 2 if 'Gas' in equipment.__class__.__name__ else 1
air_path = 2 if lost_pct > 0 else 1
flux_unit = 'W/m2' if si_units else 'W/ft2'
misc_unit_code = 9 if si_units else 8 # 8 = W/sq ft, 9 = W/sq m
lpd_val = watts_per_area if watts_per_area is not None else equipment.watts_per_area
energy_value = flux_dt.to_unit([lpd_val], flux_unit, 'W/m2')[0]
# Miscellaneous;
# Description;
# 1;
# Energy consumption;
# Energy consumption unit;
# Sensible %;
# Room %;
# Plenum %;
# Radiant %;
# Energy meter;
# Air path;
exp_line = (
f'Miscellaneous;{description};1;{energy_value:.2f};{misc_unit_code};{sensible_pct:.0f};'
f'{room_pct:.0f};{plenum_pct:.0f};{radiant_pct:.0f};{meter_code};{air_path};'
)
return exp_line
[docs]
def program_to_trace700_internal_load_template(program, si_units=False):
"""Get a TRACE 700 "T.InternalLoadTemplate" entry string from a Honeybee ProgramType.
Args:
program: A Honeybee ProgramType object.
si_units: Boolean to note whether the units of the values in the resulting
matrix are in SI (True) instead of IP (False). (Default: False).
Returns:
A text string for a TRACE 700 T.InternalLoadTemplate entry.
"""
template_name = program.display_name
area_unit = 'm2' if si_units else 'ft2'
power_unit = 'W' if si_units else 'Btu/h'
flux_unit = 'W/m2' if si_units else 'W/ft2'
ppl_amount_unit = 2 if si_units else 1
ppl_load_unit = 8 if si_units else 7
ltg_unit = 4 if si_units else 3
misc_unit = 9 if si_units else 8
# Fields 2-9 (People)
ppl = program.people
if ppl is not None:
ppl_type = readable_short_name(ppl.display_name, max_length=40)
ppl_amount = area_dt.to_unit([ppl.area_per_person], area_unit, 'm2')[0]
sensible_watts = power_dt.to_unit([ppl.activity_max_sensible], power_unit, 'W')[0]
latent_watts = power_dt.to_unit([ppl.activity_max_latent], power_unit, 'W')[0]
else:
ppl_type = 'None'
ppl_amount = 0
# default sensible and latent loads: 250 Btu/h (73.26775 W)
# unit is hardcoded to IP since the sensible and latent values are converted
# to Btu/h when importing the template
sensible_watts = 250
latent_watts = 250
ppl_load_unit = 7
# Fields 10-13 (Lighting)
ltg = program.lighting
if ltg is not None:
ltg_type = ltg.display_name
ltg_lpd = flux_dt.to_unit([ltg.watts_per_area], flux_unit, 'W/m2')[0] if ltg else 0.0
else:
ltg_type = 'Fluorescent, hung below ceiling, 100% load to space'
ltg_lpd = 0.0
# Fields 14-18 (Miscellaneous Equipment)
elec_eq = program.electric_equipment
gas_eq = program.gas_equipment
if elec_eq is not None or gas_eq is not None:
elec_lpd = elec_eq.watts_per_area if elec_eq is not None else 0.0
gas_lpd = gas_eq.watts_per_area if gas_eq is not None else 0.0
combined_lpd = elec_lpd + gas_lpd
if combined_lpd > 0:
misc_energy = flux_dt.to_unit([combined_lpd], flux_unit, 'W/m2')[0]
eq_obj = elec_eq if elec_lpd >= gas_lpd else gas_eq
energy_meter = 1 if elec_lpd >= gas_lpd else 2
misc_type = readable_short_name(eq_obj.display_name, max_length=40)
else:
misc_type = 'None'
misc_energy = 0
energy_meter = 0
else:
misc_type = 'None'
misc_energy = 0
energy_meter = 0
field_19, field_20 = 1, 2 # workstation, workstation unit
exp_line = (
f"{template_name};{ppl_type};Cooling Only (Design);{ppl_amount:.1f};{ppl_amount_unit};"
f"{sensible_watts:.0f};{ppl_load_unit};{latent_watts:.0f};{ppl_load_unit};"
f"{ltg_type};Cooling Only (Design);{ltg_lpd:.2f};{ltg_unit};"
f"{misc_type};Cooling Only (Design);{misc_energy:.2f};{misc_unit};"
f"{energy_meter};{field_19};{field_20};"
)
return exp_line
[docs]
def program_to_trace700_airflow_template(
program, si_units=False, ventilation_method='Sum of Outdoor Air'
):
"""Get a TRACE 700 "T.AirflowTemplate" entry string from a Honeybee ProgramType.
Args:
program: A Honeybee ProgramType object.
si_units: Boolean to note whether the units of the values in the resulting
matrix are in SI (True) instead of IP (False). (Default: False).
ventilation_method: Optional text for the ventilation method to be used in the
airflow template. Choose from the following.
* Sum of Outdoor Air
* ASHRAE 62.1
Returns:
A text string for a TRACE 700 T.AirflowTemplate entry.
"""
blank_val = '9999.99'
template_name = program.display_name
# Fields 4-7 (Ventilation Layout)
vent = program.ventilation
vent_flow_unit = 'L/s' if si_units else 'cfm'
vent_unit = 6 if si_units else 3 # 6 = L/s/sq m, 3 = cfm/sq ft
ppl_unit = 7 if si_units else 2 # 7 = L/s/person m, 2 = cfm/person
vent_val, ppl, ez_cool, ez_heat = 0, 0, 100, 100
if vent is not None and ventilation_method == 'ASHRAE 62.1':
vent_val = flow_dt.to_unit([vent.flow_per_area], vent_flow_unit, 'm3/s')[0]
vent_val = vent_val if si_units else vent_val * 10.7639104 # convert to cfm/ft2
ppl = flow_dt.to_unit([vent.flow_per_person], vent_flow_unit, 'm3/s')[0]
ez_cool = int(vent.effectiveness_cooling * 100)
ez_cool = 100 if ez_cool > 100 else ez_cool
ez_heat = int(vent.effectiveness_heating * 100)
ez_heat = 100 if ez_heat > 100 else ez_heat
# Fields 25-26 (Exhaust Air Layout)
exhaust = program.exhaust
ea_unit = 10 if si_units else 4 # 10 = L/s, 4 = cfm
ea_val = 0
if exhaust is not None:
if exhaust.flow_per_area != 0:
ea_unit = 14 if si_units else 8 # 14 = L/s/sq m, 8 = cfm/sq ft
ea_val = exhaust.flow_per_area_si if si_units else exhaust.flow_per_area_ip
ea_val = round(ea_val, 3)
elif exhaust.flow_per_fixture != 0:
ea_val = exhaust.flow_per_fixture_si if si_units else exhaust.flow_per_fixture_ip
ea_val = round(ea_val * exhaust.fixture_count)
# Fields 8-13 (Infiltration Layout)
inf = program.infiltration
inf_unit = 5 if si_units else 3 # 5 = L/s/sq m, 3 = cfm/sq ft
inf_val = 0
if inf is not None:
inf_val = inf.flow_per_exterior_area_si if si_units else inf.flow_per_exterior_area_ip
if ventilation_method == 'ASHRAE 62.1':
exp_line = (
f"{template_name};Default Std62;Available (100%);{ppl:.3f};{ppl_unit};{vent_val:.3f};{vent_unit};"
f"None;Available (100%);{inf_val:.3f};{inf_unit};{inf_val:.3f};{inf_unit};"
f"{blank_val};0;Available (100%);{blank_val};8;{blank_val};9;{blank_val};10;{blank_val};10;"
f"{ea_val};{ea_unit};Available (100%);"
f"1;0;{ez_cool};0;{ez_heat};1;{blank_val};{blank_val};0;0;{blank_val};0;"
)
else:
exp_line = (
f"{template_name};None;Available (100%);{vent_val:.3f};{vent_unit};{vent_val:.3f};{vent_unit};"
f"None;Available (100%);{inf_val:.3f};{inf_unit};{inf_val:.3f};{inf_unit};"
f"{blank_val};0;Available (100%);{blank_val};8;{blank_val};9;{blank_val};10;{blank_val};10;"
f"{ea_val};{ea_unit};Available (100%);0;0;"
f"{blank_val};0;{blank_val};1;{blank_val};{blank_val};0;0;{blank_val};0;"
)
return exp_line
[docs]
def program_to_trace700_thermostat_template(program, si_units=False):
"""Get a TRACE 700 "T.ThermostatTemplate" entry string from a Honeybee ProgramType.
Args:
program: A Honeybee ProgramType object.
si_units: Boolean to note whether the units of the values in the resulting
matrix are in SI (True) instead of IP (False). (Default: False).
Returns:
A text string for a TRACE 700 T.ThermostatTemplate entry.
"""
set_pt = program.setpoint
template_name = program.display_name
temp_unit_label = 'C' if si_units else 'F'
temp_unit_code = 0 if si_units else 1 # 0 = Celsius, 1 = Fahrenheit
if set_pt is not None:
clg_db = temp_dt.to_unit([set_pt.cooling_setpoint], temp_unit_label, 'C')[0]
htg_db = temp_dt.to_unit([set_pt.heating_setpoint], temp_unit_label, 'C')[0]
clg_drift = temp_dt.to_unit([set_pt.cooling_setback], temp_unit_label, 'C')[0]
htg_drift = temp_dt.to_unit([set_pt.heating_setback], temp_unit_label, 'C')[0]
rh_val = set_pt.dehumidifying_setpoint if set_pt.dehumidifying_setpoint is not None else 50.0
else:
clg_db = temp_dt.to_unit([75.0], temp_unit_label, 'F')[0]
htg_db = temp_dt.to_unit([68.0], temp_unit_label, 'F')[0]
clg_drift = temp_dt.to_unit([90.0], temp_unit_label, 'F')[0]
htg_drift = temp_dt.to_unit([55.0], temp_unit_label, 'F')[0]
rh_val = 50.0
control_fields = '1;0;2;1;'
exp_line = (
f"{template_name};{clg_db:.1f};{temp_unit_code};{htg_db:.1f};{temp_unit_code};"
f"{rh_val:.0f};{clg_drift:.1f};None;{htg_drift:.1f};None;"
f"{control_fields}"
)
return exp_line
[docs]
def program_to_trace700_room_template(program):
"""Get a TRACE 700 "T.RoomTemplate" entry string from a Honeybee ProgramType.
Args:
program: A Honeybee ProgramType object.
Returns:
A text string for a TRACE 700 T.RoomTemplate entry.
"""
template_name = program.display_name
construction_template = 'Default'
exp_line = f'{template_name};{template_name};{template_name};{template_name};{construction_template};'
return exp_line
[docs]
def internal_loads_to_exp(program, si_units=False):
"""Get an EXP string containing internal load library entries and templates for a ProgramType.
Args:
program: A Honeybee ProgramType object.
si_units: Boolean to note whether the units of the values in the resulting
matrix are in SI (True) instead of IP (False). (Default: False).
Returns:
Text string of an EXP file for TRACE 700 containing internal load definitions.
"""
newline = '\n'
standalone_blocks = ['EDITORSv6.3.1']
if program.people:
standalone_blocks.extend([
'T.LOAD_PEOPLE',
people_to_exp(program.people, si_units)
])
if program.lighting:
standalone_blocks.extend([
'T.LOAD_LIGHTS',
lighting_to_exp(program.lighting)
])
elec_eq = program.electric_equipment
gas_eq = program.gas_equipment
if elec_eq is not None or gas_eq is not None:
elec_lpd = elec_eq.watts_per_area if elec_eq is not None else 0.0
gas_lpd = gas_eq.watts_per_area if gas_eq is not None else 0.0
combined_lpd = elec_lpd + gas_lpd
if elec_lpd + gas_lpd > 0:
eq_obj = elec_eq if elec_lpd >= gas_lpd else gas_eq
standalone_blocks.extend([
'T.LOAD_MISEQUIP',
equipment_to_exp(eq_obj, si_units, watts_per_area=combined_lpd)
])
internal_load_template = program_to_trace700_internal_load_template(program, si_units)
standalone_blocks.extend([
'T.InternalLoadTemplate',
internal_load_template
])
file_data = newline.join(standalone_blocks) + newline
return file_data
[docs]
def airflow_to_exp(program, si_units=False, ventilation_method='Sum of Outdoor Air'):
"""Get an EXP string containing only the airflow template for a ProgramType.
Args:
program: A Honeybee ProgramType object.
si_units: Boolean to note whether the units of the values in the resulting
matrix are in SI (True) instead of IP (False). (Default: False).
ventilation_method: Optional text for the ventilation method to be used in the
airflow template. Choose from the following.
* Sum of Outdoor Air
* ASHRAE 62.1
Returns:
Text string of an EXP file for TRACE 700 containing the airflow template.
"""
newline = '\n'
airflow_template = program_to_trace700_airflow_template(
program, si_units, ventilation_method
)
standalone_blocks = [
'EDITORSv6.3.1',
'T.AirflowTemplate',
airflow_template
]
file_data = newline.join([b for b in standalone_blocks if b != '']) + newline
return file_data
[docs]
def thermostat_to_exp(program, si_units=False):
"""Get an EXP string containing only the thermostat template for a ProgramType.
Args:
program: A Honeybee ProgramType object.
si_units: Boolean to note whether the units of the values in the resulting
matrix are in SI (True) instead of IP (False). (Default: False).
Returns:
Text string of an EXP file for TRACE 700 containing the thermostat template.
"""
newline = '\n'
thermostat_template = program_to_trace700_thermostat_template(program, si_units)
standalone_blocks = [
'EDITORSv6.3.1',
'T.ThermostatTemplate',
thermostat_template
]
file_data = newline.join([b for b in standalone_blocks if b != '']) + newline
return file_data
[docs]
def program_to_exp(program, si_units=False, ventilation_method='Sum of Outdoor Air'):
"""Get a complete EXP string for a Honeybee ProgramType.
Args:
program: A Honeybee ProgramType object for which the TRACE 700 EXP string
will be generated.
si_units: Boolean to note whether the units of the values in the resulting
matrix are in SI (True) instead of IP (False). (Default: False).
ventilation_method: Optional text for the ventilation method to be used in the
airflow template. Choose from the following.
* Sum of Outdoor Air
* ASHRAE 62.1
Returns:
Text string of a complete EXP file for TRACE 700.
"""
newline = '\n'
standalone_blocks = ['EDITORSv6.3.1']
if program.people:
standalone_blocks.extend([
'T.LOAD_PEOPLE',
people_to_exp(program.people, si_units)
])
if program.lighting:
standalone_blocks.extend([
'T.LOAD_LIGHTS',
lighting_to_exp(program.lighting)
])
elec_eq = program.electric_equipment
gas_eq = program.gas_equipment
if elec_eq is not None or gas_eq is not None:
elec_lpd = elec_eq.watts_per_area if elec_eq is not None else 0.0
gas_lpd = gas_eq.watts_per_area if gas_eq is not None else 0.0
combined_lpd = elec_lpd + gas_lpd
if elec_lpd + gas_lpd > 0:
eq_obj = elec_eq if elec_lpd >= gas_lpd else gas_eq
standalone_blocks.extend([
'T.LOAD_MISEQUIP',
equipment_to_exp(eq_obj, si_units, watts_per_area=combined_lpd)
])
internal_load_template = program_to_trace700_internal_load_template(program, si_units)
airflow_template = program_to_trace700_airflow_template(program, si_units, ventilation_method)
thermostat_template = program_to_trace700_thermostat_template(program, si_units)
room_template = program_to_trace700_room_template(program)
standalone_blocks.extend([
'T.InternalLoadTemplate', internal_load_template,
'T.AirflowTemplate', airflow_template,
'T.ThermostatTemplate', thermostat_template,
'T.RoomTemplate', room_template
])
file_data = newline.join(standalone_blocks) + newline
return file_data
[docs]
def programs_to_exp(programs, si_units=False, ventilation_method='Sum of Outdoor Air'):
"""Get a single combined EXP string for a list of Honeybee ProgramTypes.
Args:
programs: A list of Honeybee ProgramType objects.
si_units: Boolean to note whether the units of the values in the resulting
matrix are in SI (True) instead of IP (False). (Default: False).
ventilation_method: Optional text for the ventilation method to be used in the
airflow template. Choose from the following.
* Sum of Outdoor Air
* ASHRAE 62.1
Returns:
Text string of a combined EXP file for TRACE 700.
"""
newline = '\n'
people_templates = []
lighting_templates = []
equip_templates = []
internal_load_templates = []
airflow_templates = []
thermostat_templates = []
room_templates = []
seen_people = set()
seen_lighting = set()
seen_equip = set()
for program in programs:
if program.people and program.people.identifier not in seen_people:
people_templates.append(people_to_exp(program.people, si_units))
seen_people.add(program.people.identifier)
if program.lighting and program.lighting.identifier not in seen_lighting:
lighting_templates.append(lighting_to_exp(program.lighting))
seen_lighting.add(program.lighting.identifier)
elec_eq = program.electric_equipment
gas_eq = program.gas_equipment
if elec_eq is not None or gas_eq is not None:
elec_lpd = elec_eq.watts_per_area if elec_eq is not None else 0.0
gas_lpd = gas_eq.watts_per_area if gas_eq is not None else 0.0
combined_lpd = elec_lpd + gas_lpd
if combined_lpd > 0:
eq_obj = elec_eq if elec_lpd >= gas_lpd else gas_eq
if eq_obj.identifier not in seen_equip:
equip_templates.append(
equipment_to_exp(
eq_obj, si_units=si_units, watts_per_area=combined_lpd
)
)
seen_equip.add(eq_obj.identifier)
internal_load_templates.append(
program_to_trace700_internal_load_template(program, si_units)
)
airflow_templates.append(
program_to_trace700_airflow_template(program, si_units, ventilation_method)
)
thermostat_templates.append(program_to_trace700_thermostat_template(program, si_units))
room_templates.append(program_to_trace700_room_template(program))
standalone_blocks = ['EDITORSv6.3.1']
if people_templates:
standalone_blocks.append('T.LOAD_PEOPLE')
standalone_blocks.extend(people_templates)
if lighting_templates:
standalone_blocks.append('T.LOAD_LIGHTS')
standalone_blocks.extend(lighting_templates)
if equip_templates:
standalone_blocks.append('T.LOAD_MISEQUIP')
standalone_blocks.extend(equip_templates)
if internal_load_templates:
standalone_blocks.append('T.InternalLoadTemplate')
standalone_blocks.extend(internal_load_templates)
if airflow_templates:
standalone_blocks.append('T.AirflowTemplate')
standalone_blocks.extend(airflow_templates)
if thermostat_templates:
standalone_blocks.append('T.ThermostatTemplate')
standalone_blocks.extend(thermostat_templates)
if room_templates:
standalone_blocks.append('T.RoomTemplate')
standalone_blocks.extend(room_templates)
file_data = newline.join(standalone_blocks) + newline
return file_data
[docs]
def material_to_exp(material, si_units=False, id=999):
"""Converts a Honeybee EnergyMaterial or EnergyMaterialNoMass to a T.MATR_MATERIAL entry."""
description = readable_short_name(material.display_name, max_length=40)
material_id = f'M{id}' if not str(id).startswith('M') else str(id)
thickness_unit = 'm' if si_units else 'in'
thickness_code = 2 if si_units else 1
conductivity_unit = 'W/m-K' if si_units else 'Btu/h-ft-F'
conductivity_code = 0 if si_units else 1
density_unit = 'kg/m3' if si_units else 'lb/ft3'
density_code = 1 if si_units else 0
specific_heat_unit = 'kJ/kg-K' if si_units else 'Btu/lb-F'
specific_heat_code = 0 if si_units else 1
resistance_unit = 'm2-K/W' if si_units else 'h-ft2-F/Btu'
resistance_unit_code = 1
category = 'Undefined Material'
if isinstance(material, EnergyMaterialNoMass):
resistance = rvalue_dt.to_unit([material.r_value], resistance_unit, 'm2-K/W')[0]
thickness = 0
conductivity = 0
density = 0
specific_heat = 0
else:
thickness = distance_dt.to_unit([material.thickness], thickness_unit, 'm')[0]
conductivity = conductivity_dt.to_unit([material.conductivity], conductivity_unit, 'W/m-K')[0]
density = density_dt.to_unit([material.density], density_unit, 'kg/m3')[0]
thickness = distance_dt.to_unit([material.thickness], thickness_unit, 'm')[0]
specific_heat = specific_heat_capacity_dt.to_unit(
[material.specific_heat], specific_heat_unit, 'J/kg-K'
)[0]
resistance = 0.0 # this is 0 because the other fields are used
exp_line = (
f'0;{material_id};{category};0;{description};{thickness};{thickness_code};'
f'{conductivity};{conductivity_code};{density};{density_code};'
f'{specific_heat};{specific_heat_code};{resistance};{resistance_unit_code};'
)
return exp_line
[docs]
def opaque_construction_to_exp(
construction, const_type='Wall', si_units=False, starting_material_id=500, material_id_map=None
):
"""Converts a Honeybee OpaqueConstruction to a TRACE 700 construction entry."""
description = readable_short_name(construction.display_name, max_length=40)
film_map = {
'Wall': ('A0', 'E0'),
'Roof': ('A0', 'E0'),
'Floor': ('E0', 'E0'),
'Partition': ('A0', 'E0'),
'Exposed Floor': ('A0', 'E0')
}
out_film, in_film = film_map.get(const_type, ('A0', 'E0'))
layers_ids = ['None'] * 10
layers_ids[0] = out_film
material_lines = []
id_counter = starting_material_id
for idx, mat in enumerate(construction.materials):
if idx >= 8:
break
mat_key = mat.identifier
if material_id_map is not None and mat_key in material_id_map:
mat_tag = material_id_map[mat_key]
else:
mat_tag = f'M{id_counter}'
mat_line = material_to_exp(mat, si_units=si_units, id=id_counter)
material_lines.append(mat_line)
if material_id_map is not None:
material_id_map[mat_key] = mat_tag
id_counter += 1
layers_ids[idx + 1] = mat_tag
layers_ids[len(construction.materials) + 1] = in_film
layers_str = ';'.join(layers_ids)
b_coeffs = ';'.join([''] * 7)
d_coeffs = ';'.join([''] * 7)
u_factor_unit = 'W/m2-K' if si_units else 'Btu/h-ft2-F'
u_factor = uvalue_dt.to_unit([construction.u_factor], u_factor_unit, 'W/m2-K')[0]
u_code = 0 if si_units else 1
hc_si = construction.area_heat_capacity
heat_capacity = hc_si if si_units else (hc_si * 0.000048919)
hc_code = 0 if si_units else 1
wt_si = sum(
m.density * m.thickness
for m in construction.materials
if hasattr(m, 'density') and hasattr(m, 'thickness')
)
if si_units:
area_density = wt_si
ad_code = 0
else:
area_density = wt_si * 0.20481614
ad_code = 1
out_mat = construction.outside_material
in_mat = construction.inside_material
sol_abs_out = getattr(out_mat, 'solar_absorptance', 0.9)
therm_abs_out = getattr(out_mat, 'thermal_absorptance', 0.9)
sol_abs_in = getattr(in_mat, 'solar_absorptance', 0.65)
therm_abs_in = getattr(in_mat, 'thermal_absorptance', 0.9)
vis_refl_in = getattr(in_mat, 'visible_reflectance', 0.20)
roughness_map = {
'VeryRough': 0, 'Rough': 1, 'MediumRough': 2, 'MediumSmooth': 3,
'Smooth': 4, 'VerySmooth': 5
}
roughness = roughness_map.get(getattr(out_mat, 'roughness', 'Rough'), 1)
comment = ''
zero_padding_block = ';'.join(['0'] * 10)
exp_line = (
f'{const_type};{description};1;0;{layers_str};'
f'{b_coeffs};{d_coeffs};'
f'0;0;1;'
f'{area_density};{ad_code};{heat_capacity};{hc_code};{u_factor};{u_code};'
f'0;{comment};{sol_abs_out:.8f};'
f'{zero_padding_block};'
f'{therm_abs_out:.8f};{sol_abs_in:.8f};{therm_abs_in:.8f};{vis_refl_in:.8f};{roughness};1;'
)
return exp_line, material_lines
[docs]
def window_construction_to_exp(construction, si_units=False, is_door=False):
"""Converts a Honeybee WindowConstruction object to a T.GLAS_GLASS entry."""
description = construction.display_name
if is_door and not description.lower().endswith('door'):
description = f'{description} Door'
description = readable_short_name(description, max_length=40)
# get the summertime U-Value
u_value_unit = 'W/m2-K' if si_units else 'Btu/h-ft2-F'
if isinstance(construction, OpaqueConstruction): # same between winter and summer
u_factor = construction.u_factor
else: # get the U-Value specifically for summertime
u_factor = construction.u_factor_summer
u_value = uvalue_dt.to_unit([u_factor], u_value_unit, 'W/m2-K')[0]
u_value_code = 0 if si_units else 1
# get shading coefficient
# defined as the ratio of solar heat compared to a standard 3mm clear float glass
sc = getattr(construction, 'shading_coefficient', 0.0)
# get the solar and optical properties
# TRACE asks for hemispherical transmissivity, which is challenging to compute
# we will use transmittance at normal incidence in the hope that
# transmissivity is higher than transmittance and this will cancel out the
# fact that hemispherical values are lower than those at normal incidence
vt = getattr(construction, 'visible_transmittance', 0.5)
panes = getattr(construction, 'glazing_count', 1)
inside_vis_refl = construction.inside_solar_reflectance
inside_sol_refl = construction.inside_solar_reflectance
solar_transmittance = getattr(construction, 'solar_transmittance', 0.0)
# get the emissivity of the inside and outside
outside_emissivity = construction.outside_emissivity
inside_emissivity = construction.inside_emissivity
# Field 179: 1 for standard glass, 2 for glass doors
glass_type_code = 2 if is_door else 1
# TRACE 700 custom glass data (213 fields)
# This is copied from an exported default glass material in TRACE 700
glass_tail = (
"0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;"
"0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;"
"0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;"
"0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;"
f"0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;{glass_type_code};0;0;"
"0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;57.1500015258789063;"
"25.3999996185302734;25.3999996185302734;4.59999990463256836;1.10099995136260986;"
".89999997615814209;.89999997615814209;.89999997615814209;0;0;0;0;0;0;0;0;0;0;0;0;"
)
exp_line = (
f'{description};Glass;1;{sc:.6f};{u_value:.6f};{u_value_code};{panes};0;'
f'{vt:.4f};{inside_vis_refl:.4f};{solar_transmittance:.4f};{inside_sol_refl:.4f};'
f'{outside_emissivity:.4f};{inside_emissivity:.4f};{glass_tail}'
)
return exp_line
[docs]
def construction_set_to_trace700_template(construction_set, si_units=False):
"""Translates a Honeybee ConstructionSet into a TRACE 700 Construction Template line."""
template_name = readable_short_name(construction_set.display_name, max_length=40)
u_code = 0 if si_units else 1
def _get_opaque_info(cnst):
if cnst is None:
return 'None', 0.0
u_value_unit = 'W/m2-K' if si_units else 'Btu/h-ft2-F'
u_value = uvalue_dt.to_unit([cnst.u_value], u_value_unit, 'W/m2-K')[0]
return readable_short_name(cnst.display_name, max_length=40), u_value
def _get_window_info(cnst, is_door=False):
if cnst is None:
return 'None', 0.0, 0.0
u_value_unit = 'W/m2-K' if si_units else 'Btu/h-ft2-F'
u_value = uvalue_dt.to_unit([cnst.u_value], u_value_unit, 'W/m2-K')[0]
desc = cnst.display_name
if is_door and not desc.lower().endswith('door'):
desc = f'{desc} Door'
return readable_short_name(desc, max_length=40), u_value, cnst.shgc
def _get_door_info(cnst):
if cnst is None:
return 'None', 0.0, 0.0
is_glass = hasattr(cnst, 'shgc')
if is_glass:
return _get_window_info(cnst, is_door=True)
else:
desc, u_val = _get_opaque_info(cnst)
return readable_short_name(desc, max_length=40), u_val, 0.0
roof_cnst = construction_set.roof_ceiling_set.exterior_construction
roof_name, roof_u = _get_opaque_info(roof_cnst)
floor_cnst = (
construction_set.floor_set.ground_construction or
construction_set.floor_set.exterior_construction or
construction_set.floor_set.interior_construction
)
floor_name, floor_u = _get_opaque_info(floor_cnst)
skylight_cnst = construction_set.aperture_set.skylight_construction
skylight_name, skylight_u, skylight_shgc = _get_window_info(skylight_cnst)
wall_cnst = construction_set.wall_set.exterior_construction
wall_name, wall_u = _get_opaque_info(wall_cnst)
window_cnst = construction_set.aperture_set.window_construction
window_name, window_u, window_shgc = _get_window_info(window_cnst)
partition_cnst = construction_set.wall_set.interior_construction
partition_name, partition_u = _get_opaque_info(partition_cnst)
# resolve door construction priority
if getattr(construction_set.door_set, '_exterior_construction', None) is not None:
door_cnst = construction_set.door_set.exterior_construction
elif getattr(construction_set.door_set, '_exterior_glass_construction', None) is not None:
door_cnst = construction_set.door_set.exterior_glass_construction
else:
door_cnst = (
construction_set.door_set.exterior_construction or
construction_set.door_set.exterior_glass_construction
)
door_name, door_u, door_shgc = _get_door_info(door_cnst)
tail_fields = '0;0;'
exp_line = (
f'{template_name};'
f'{floor_name};{floor_u:.6f};{u_code};'
f'{roof_name};{roof_u:.6f};{u_code};'
f'{skylight_name};{skylight_u:.6f};{u_code};{skylight_shgc:.6f};'
f'{wall_name};{wall_u:.6f};{u_code};'
f'{window_name};{window_u:.6f};{u_code};{window_shgc:.6f};'
f'{partition_name};{partition_u:.6f};{u_code};'
f'10;0;2;0;10;0;'
f'{door_name};{door_u:.6f};{u_code};{door_shgc:.6f};'
f'{tail_fields}'
)
return exp_line
[docs]
def construction_sets_to_exp(construction_sets, si_units=False):
"""Get a single combined EXP string for a list of Honeybee ConstructionSets."""
newline = '\n'
exfloor_templates = []
floor_templates = []
partition_templates = []
roof_templates = []
wall_templates = []
glass_templates = []
material_templates = []
construction_templates = []
seen_constructions = set()
seen_materials = set()
material_id_map = {}
material_counter = 500
def _process_opaque(cnst, category):
nonlocal material_counter
if cnst is not None and cnst.identifier not in seen_constructions:
line, mat_lines = opaque_construction_to_exp(
cnst, const_type=category, si_units=si_units,
starting_material_id=material_counter, material_id_map=material_id_map
)
material_counter += len(mat_lines)
seen_constructions.add(cnst.identifier)
for ml in mat_lines:
mat_id = ml.split(';')[1]
if mat_id not in seen_materials:
material_templates.append(ml)
seen_materials.add(mat_id)
return line
return None
def _process_glass(cnst, is_door=False):
if cnst is not None:
key = (cnst.identifier, is_door)
if key not in seen_constructions:
line = window_construction_to_exp(cnst, si_units=si_units, is_door=is_door)
seen_constructions.add(key)
return line
return None
for c_set in construction_sets:
w_ext = _process_opaque(c_set.wall_set.exterior_construction, 'Wall')
if w_ext:
wall_templates.append(w_ext)
w_grd = _process_opaque(c_set.wall_set.ground_construction, 'Wall')
if w_grd:
wall_templates.append(w_grd)
w_int = _process_opaque(c_set.wall_set.interior_construction, 'Partition')
if w_int:
partition_templates.append(w_int)
r_ext = _process_opaque(c_set.roof_ceiling_set.exterior_construction, 'Roof')
if r_ext:
roof_templates.append(r_ext)
r_grd = _process_opaque(c_set.roof_ceiling_set.ground_construction, 'Roof')
if r_grd:
roof_templates.append(r_grd)
r_int = _process_opaque(c_set.roof_ceiling_set.interior_construction, 'Floor')
if r_int:
floor_templates.append(r_int)
f_ext = _process_opaque(c_set.floor_set.exterior_construction, 'Exposed Floor')
if f_ext:
exfloor_templates.append(f_ext)
f_grd = _process_opaque(c_set.floor_set.ground_construction, 'Floor')
if f_grd:
floor_templates.append(f_grd)
f_int = _process_opaque(c_set.floor_set.interior_construction, 'Floor')
if f_int:
floor_templates.append(f_int)
for ap_cnst in (
c_set.aperture_set.window_construction,
c_set.aperture_set.interior_construction,
c_set.aperture_set.skylight_construction,
c_set.aperture_set.operable_construction,
):
g_line = _process_glass(ap_cnst, is_door=False)
if g_line:
glass_templates.append(g_line)
d_ovh = _process_glass(c_set.door_set.overhead_construction, is_door=True)
if d_ovh:
glass_templates.append(d_ovh)
d_int = _process_glass(c_set.door_set.interior_construction, is_door=True)
if d_int:
glass_templates.append(d_int)
d_int_gls = _process_glass(c_set.door_set.interior_glass_construction, is_door=True)
if d_int_gls:
glass_templates.append(d_int_gls)
d_ext_gls = _process_glass(c_set.door_set.exterior_glass_construction, is_door=True)
if d_ext_gls:
glass_templates.append(d_ext_gls)
d_ext = _process_glass(c_set.door_set.exterior_construction, is_door=True)
if d_ext:
glass_templates.append(d_ext)
construction_templates.append(construction_set_to_trace700_template(c_set, si_units=si_units))
standalone_blocks = ['EDITORSv6.3.1']
if exfloor_templates:
standalone_blocks.append('T.CNST_EXFLOOR')
standalone_blocks.extend(exfloor_templates)
if floor_templates:
standalone_blocks.append('T.CNST_FLOOR')
standalone_blocks.extend(floor_templates)
if partition_templates:
standalone_blocks.append('T.CNST_PART')
standalone_blocks.extend(partition_templates)
if roof_templates:
standalone_blocks.append('T.CNST_ROOF')
standalone_blocks.extend(roof_templates)
if wall_templates:
standalone_blocks.append('T.CNST_WALL')
standalone_blocks.extend(wall_templates)
if glass_templates:
standalone_blocks.append('T.GLAS_GLASS')
standalone_blocks.extend(glass_templates)
if material_templates:
standalone_blocks.append('T.MATR_MATERIAL')
standalone_blocks.extend(material_templates)
if construction_templates:
standalone_blocks.append('T.ConstructionTemplate')
standalone_blocks.extend(construction_templates)
file_data = newline.join(standalone_blocks) + newline
return file_data
"""____________TRANSLATORS FROM EXP____________"""
[docs]
def internal_loads_from_exp(exp_str):
"""Get Honeybee People, Lighting and Equipment from a TRACE 700 InternalLoadTemplate.
Args:
exp_str: A TRACE 700 InternalLoadTemplate entry string.
Returns:
A tuple with three elements.
- people: A Honeybee People object if people are non-zero and defined
per floor area. None otherwise.
- lighting: A Honeybee Lighting object if lights are non-zero and defined
per floor area. None otherwise.
- equipment: A Honeybee ElectricEquipment or GasEquipment object if
miscellaneous loads are non-zero and defined per floor area. None otherwise.
"""
# deconstruct the string and get the identifiers
exp_vals = exp_str.split(';')
load_id = clean_ep_string(exp_vals[0])
people_id = clean_ep_string(exp_vals[1]) \
if exp_vals[1] != 'None' else '{} People'.format(load_id)
lights_id = clean_ep_string(exp_vals[9]) \
if exp_vals[9] != 'None' else '{} Lighting'.format(load_id)
equip_id = clean_ep_string(exp_vals[13]) \
if exp_vals[13] != 'None' else '{} Equipment'.format(load_id)
# process the people
people = None
ppl_val, ppl_unit = float(exp_vals[3]), int(exp_vals[4])
if ppl_val != 0 and ppl_unit in (1, 2):
if ppl_unit == 1:
ppl_val = area_dt.to_unit([ppl_val], 'm2', 'ft2')[0]
people = People(people_id, 1 / ppl_val)
sens_val, sens_unit = float(exp_vals[5]), int(exp_vals[6])
lat_val, lat_unit = float(exp_vals[7]), int(exp_vals[8])
if sens_unit in (7, 8) and sens_unit == lat_unit:
total = sens_val + lat_val
latent_fraction = lat_val / total
act_unit = 'Btu/h' if sens_unit == 7 else 'W'
sch_id = 'People Activity - {} [{}]'.format(total, act_unit)
if sens_unit == 7:
total = power_dt.to_unit([total], 'W', 'Btu/h')[0]
people.activity_schedule = \
ScheduleRuleset.from_constant_value(sch_id, total, activity_level)
people.latent_fraction = latent_fraction
# process the lighting
lighting = None
lgt_val, lgt_unit = float(exp_vals[11]), int(exp_vals[12])
if lgt_val != 0 and lgt_unit in (1, 3, 4):
if lgt_unit == 1:
lgt_val = flux_dt.to_unit([lgt_val], 'W/m2', 'Btu/h-ft2')[0]
elif lgt_unit == 3:
lgt_val = flux_dt.to_unit([lgt_val], 'W/m2', 'W/ft2')[0]
lighting = Lighting(lights_id, lgt_val)
# process the miscellaneous loafs
equipment = None
equip_val, equip_unit = float(exp_vals[15]), int(exp_vals[16])
if equip_val != 0 and equip_unit in (1, 8, 9):
equip_type = int(exp_vals[17])
if equip_unit == 1:
equip_val = flux_dt.to_unit([equip_val], 'W/m2', 'Btu/h-ft2')[0]
elif equip_unit == 8:
equip_val = flux_dt.to_unit([equip_val], 'W/m2', 'W/ft2')[0]
if equip_type == 2:
equipment = GasEquipment(equip_id, equip_val)
else:
equipment = ElectricEquipment(equip_id, equip_val)
return people, lighting, equipment
[docs]
def airflow_from_exp(exp_str):
""""Get Infiltration, Ventilation and ExhaustAir from TRACE 700 AirflowTemplate.
Args:
exp_str: A TRACE 700 AirflowTemplate entry string.
Returns:
A tuple with three elements.
- infiltration: A Honeybee Infiltration object if the infiltration is
non-zero and defined per exterior area. None otherwise.
- ventilation: A Honeybee Ventilation object if ventilation is non-zero
and is either "Sum of Outdoor Air" or "ASHRAE 62.1." None otherwise.
- exhaust: A Honeybee ExhaustAir object if exhaust air is non-zero and
defined per floor area or as an absolute flow rate. None otherwise.
"""
blank_val = '9999.99'
# deconstruct the string and get the identifier
exp_vals = exp_str.split(';')
airflow_id = clean_ep_string(exp_vals[0])
vent_id = clean_ep_string(exp_vals[1]) \
if exp_vals[1] != 'None' else '{} Ventilation'.format(airflow_id)
inf_id = clean_ep_string(exp_vals[7]) \
if exp_vals[7] != 'None' else '{} Infiltration'.format(airflow_id)
exhaust_id = '{} ExhaustAir'.format(airflow_id)
# process the ventilation
ventilation = None
vent_method = exp_vals[27]
if vent_method == 0: # Sum of Outdoor Air
v_cool_unit, v_heat_unit = int(exp_vals[4]), int(exp_vals[6])
vc_val, vh_val = float(exp_vals[3]), float(exp_vals[5])
if vc_val != 0 and vh_val != 0 and v_cool_unit != 4:
ventilation = Ventilation(vent_id)
if v_cool_unit == 0: # ACH
ventilation.air_changes_per_hour = vc_val
elif v_cool_unit == 1: # cfm
vc_val = flow_dt.to_unit([vc_val], 'm3/s', 'cfm')[0]
ventilation.flow_per_zone = vc_val
elif v_cool_unit == 2: # cfm/person
vc_val = flow_dt.to_unit([vc_val], 'm3/s', 'cfm')[0]
ventilation.flow_per_person = vc_val
elif v_cool_unit == 3: # cfm/ft2
vc_val = flow_int_dt.to_unit([vc_val], 'm3/s-m2', 'cfm/ft2')[0]
ventilation.flow_per_area = vc_val
elif v_cool_unit == 5: # L/s
vc_val = flow_dt.to_unit([vc_val], 'm3/s', 'L/s')[0]
ventilation.flow_per_zone = vc_val
elif v_cool_unit == 6: # L/s-m2
vc_val = flow_int_dt.to_unit([vc_val], 'm3/s-m2', 'L/s-m2')[0]
ventilation.flow_per_area = vc_val
elif v_cool_unit == 7: # L/s-person
vc_val = flow_dt.to_unit([vc_val], 'm3/s', 'L/s')[0]
ventilation.flow_per_person = vc_val
if v_cool_unit == v_heat_unit:
ventilation.effectiveness_heating = round(vc_val / vh_val, 3)
elif vent_method == 1: # ASHRAE 62.1
ppl_unit, area_unit = int(exp_vals[4]), int(exp_vals[6])
ppl_val, area_val = float(exp_vals[3]), float(exp_vals[5])
if (ppl_val != 0 or area_val != 0) and ppl_unit in (2, 7) and area_unit in (3, 6):
ventilation = Ventilation(vent_id)
if ppl_unit == 2: # cfm/person
ppl_val = flow_dt.to_unit([ppl_val], 'm3/s', 'cfm')[0]
else:
ppl_val = flow_dt.to_unit([ppl_val], 'm3/s', 'L/s')[0]
ventilation.flow_per_person = ppl_val
if area_unit == 3: # cfm/ft2
area_val = flow_int_dt.to_unit([area_val], 'm3/s-m2', 'cfm/ft2')[0]
else:
area_val = flow_int_dt.to_unit([area_val], 'm3/s-m2', 'L/s-m2')[0]
ventilation.flow_per_area = area_val
if exp_vals[29] != blank_val and float(exp_vals[29]) != 100:
ventilation.effectiveness_cooling = float(exp_vals[29]) / 100
if exp_vals[31] != blank_val and float(exp_vals[31]) != 100:
ventilation.effectiveness_heating = float(exp_vals[31]) / 100
if int(exp_vals[32]) != 1 and exp_vals[33] != blank_val:
ventilation.secondary_recirculation = float(exp_vals[33]) / 100
# process the infiltration
infiltration = None
inf_val, inf_unit = float(exp_vals[9]), int(exp_vals[10])
if inf_val != 0 and inf_unit in (3, 5):
if inf_unit == 3: # cfm/ft2
inf_val = flow_int_dt.to_unit([inf_val], 'm3/s-m2', 'cfm/ft2')[0]
else:
inf_val = flow_int_dt.to_unit([inf_val], 'm3/s-m2', 'L/s-m2')[0]
infiltration = Infiltration(inf_id, inf_val)
# process the exhaust air
exhaust = None
ea_val, ea_unit = float(exp_vals[24]), int(exp_vals[25])
if ea_val != 0 and ea_unit in (4, 5, 8, 9, 10, 11, 14, 15):
exhaust = ExhaustAir(exhaust_id)
if ea_unit in (4, 5): # cfm
ea_val = flow_dt.to_unit([ea_val], 'm3/s', 'cfm')[0]
exhaust.flow_per_fixture = ea_val
elif ea_unit in (8, 9): # cfm/ft2
ea_val = flow_int_dt.to_unit([ea_val], 'm3/s-m2', 'cfm/ft2')[0]
exhaust.flow_per_area = ea_val
elif ea_unit in (10, 11): # L/s
ea_val = flow_dt.to_unit([ea_val], 'm3/s', 'L/s')[0]
exhaust.flow_per_fixture = ea_val
else: # L/s-m2
ea_val = flow_int_dt.to_unit([ea_val], 'm3/s-m2', 'L/s-m2')[0]
exhaust.flow_per_area = ea_val
return infiltration, ventilation, exhaust
[docs]
def setpoint_from_exp(exp_str):
"""Get a Honeybee Setpoint from a TRACE 700 ThermostatTemplate.
Args:
exp_str: A TRACE 700 ThermostatTemplate entry string.
Returns:
A Honeybee Setpoint object.
"""
# deconstruct the string and get the identifier
exp_vals = exp_str.split(';')
set_id = clean_ep_string(exp_vals[0])
# process the thermostat setpoints
setpoint = None
clg_set, htg_set = float(exp_vals[1]), float(exp_vals[3])
c_set_unit, h_set_unit = int(exp_vals[2]), int(exp_vals[4])
if clg_set >= htg_set:
if c_set_unit == 1: # F
clg_set = temp_dt.to_unit([clg_set], 'C', 'F')[0]
if h_set_unit == 1: # F
htg_set = temp_dt.to_unit([htg_set], 'C', 'F')[0]
heat_sch = ScheduleRuleset.from_constant_value(
'{} Heating Setpoint'.format(set_id), htg_set, temperature
)
cool_sch = ScheduleRuleset.from_constant_value(
'{} Cooling Setpoint'.format(set_id), clg_set, temperature
)
setpoint = Setpoint(set_id, heat_sch, cool_sch)
return setpoint
[docs]
def program_from_exp(room_exp_str, internal_loads, airflows, setpoints):
"""Get a complete Honeybee ProgramType from the EXP string of a RoomTemplate.
Args:
exp_str: A TRACE 700 RoomTemplate entry string.
internal_loads: A dictionary of Honeybee internal loads objects with the
identifiers of the TRACE 700 InternalLoadTemplate as keys and
tuples of 3 Honeybee load objects as values (people, lighting, equipment).
airflows: A dictionary of Honeybee airflow objects with the identifiers
of the TRACE 700 AirflowTemplate as keys and tuples of 3
airflow objects as values (infiltration, ventilation, exhaust).
setpoints: A dictionary of Honeybee Setpoint objects with the identifiers
of the TRACE 700 ThermostatTemplate as keys and Honeybee Setpoint
objects as values.
Returns:
A Honeybee ProgramType object from the RoomTemplate string.
"""
# deconstruct the string and get the identifier
exp_vals = room_exp_str.split(';')
program = ProgramType(clean_ep_string(exp_vals[0]))
def_templates = ('Default', 'Metric Default')
msg_t = 'Failed to find {} "{}" in the EXP file.'
# get the internal loads
if exp_vals[1] not in def_templates:
try:
people, lighting, equipment = internal_loads[exp_vals[1]]
program.people = people
program.lighting = lighting
if isinstance(equipment, ElectricEquipment):
program.electric_equipment = equipment
elif isinstance(equipment, GasEquipment):
program.gas_equipment = equipment
except KeyError:
raise ValueError(msg_t.format('InternalLoadTemplate', exp_vals[1]))
# get the airflows
if exp_vals[2] not in def_templates:
try:
infiltration, ventilation, exhaust = airflows[exp_vals[2]]
program.infiltration = infiltration
program.ventilation = ventilation
program.exhaust = exhaust
except KeyError:
raise ValueError(msg_t.format('AirflowTemplate', exp_vals[2]))
# get the setpoint
if exp_vals[3] not in def_templates:
try:
program.setpoint = setpoints[exp_vals[3]]
except KeyError:
raise ValueError(msg_t.format('ThermostatTemplate', exp_vals[3]))
return program
[docs]
def programs_from_exp(exp_file_content):
"""Get a list of Honeybee ProgramTypes from the content of an EXP file.
Args:
exp_file_content: Text string for the contents of an EXP file.
Returns:
A list of Honeybee ProgramTypes.
"""
# load the individual templates from the file contents
file_lines = exp_file_content.split('\n')
internal_loads, airflows, setpoints = {}, {}, {}
loads_section, airflows_section, setpoints_section = False, False, False
for line in file_lines:
if line.startswith('T.InternalLoadTemplate'):
loads_section, airflows_section, setpoints_section = True, False, False
continue
elif line.startswith('T.AirflowTemplate'):
loads_section, airflows_section, setpoints_section = False, True, False
continue
elif line.startswith('T.ThermostatTemplate'):
loads_section, airflows_section, setpoints_section = False, False, True
continue
elif line.startswith('T.'):
loads_section, airflows_section, setpoints_section = False, False, False
if loads_section:
internal_loads[line.split(';')[0]] = internal_loads_from_exp(line)
elif airflows_section:
airflows[line.split(';')[0]] = airflow_from_exp(line)
elif setpoints_section:
setpoints[line.split(';')[0]] = setpoint_from_exp(line)
# create the individual programs from the templates
programs = []
programs_section = False
for line in file_lines:
if line.startswith('T.RoomTemplate'):
programs_section = True
continue
elif line.startswith('T.'):
programs_section = False
if programs_section:
if line.split(';')[0] == 'Default':
continue # ignore the default template with no properties
programs.append(program_from_exp(line, internal_loads, airflows, setpoints))
return programs
[docs]
def material_from_exp(exp_str):
"""Get an EnergyMaterial or EnergyMaterialNoMass from a TRACE 700 MATR_MATERIAL.
Args:
exp_str: A TRACE 700 MATR_MATERIAL entry string.
Returns:
A Honeybee EnergyMaterial or EnergyMaterialNoMass object.
"""
# deconstruct the string and get the identifier
exp_vals = exp_str.split(';')
mat_id = clean_ep_string(exp_vals[1])
mat_name = clean_ep_string(exp_vals[4])
# first check if the material has no mass
# get the thickness and convert it to the correct units
thickness, t_unit = float(exp_vals[5]), int(exp_vals[6])
if t_unit == 0:
thickness = distance_dt.to_unit([thickness], 'm', 'ft')[0]
elif t_unit == 1:
thickness = distance_dt.to_unit([thickness], 'm', 'in')[0]
elif t_unit == 3:
thickness = distance_dt.to_unit([thickness], 'm', 'cm')[0]
elif t_unit == 4:
thickness = distance_dt.to_unit([thickness], 'm', 'mm')[0]
# use no mass whenever there is an r-value specified or thickness is < 1 mm
r_value, r_unit = float(exp_vals[13]), int(exp_vals[14])
if r_value != 0:
if r_unit == 1:
r_value = rvalue_dt.to_unit([r_value], 'm2-K/W', 'h-ft2-F/Btu')[0]
mat = EnergyMaterialNoMass(mat_id, r_value)
mat.display_name = mat_name
return mat
elif thickness < 0.001:
mat = EnergyMaterialNoMass(mat_id, 0.001)
mat.display_name = mat_name
return mat
# it is a material with mass
# get the conductivity and convert it to the correct units
conductivity, c_unit = float(exp_vals[7]), int(exp_vals[8])
if c_unit == 1:
conductivity = conductivity_dt.to_unit([conductivity], 'W/m-K', 'Btu/h-ft-F')[0]
# get the density and convert it to the correct units
density, d_unit = float(exp_vals[9]), int(exp_vals[10])
if d_unit == 0:
density = density_dt.to_unit([density], 'kg/m3', 'lb/ft3')[0]
elif d_unit == 2:
density = density_dt.to_unit([density], 'kg/m3', 'g/cm3')[0]
# get the specific_heat and convert it to the correct units
sht, s_unit = float(exp_vals[11]), int(exp_vals[12])
if s_unit == 1:
sht = specific_heat_capacity_dt.to_unit([sht], 'J/kg-K', 'Btu/lb-F')[0]
elif s_unit == 0:
sht = specific_heat_capacity_dt.to_unit([sht], 'J/kg-K', 'kJ/kg-K')[0]
mat = EnergyMaterial(mat_id, thickness, conductivity, density, sht)
mat.display_name = mat_name
return mat
[docs]
def opaque_construction_from_exp(exp_str, materials):
"""Get an OpaqueConstruction from a TRACE 700 CNST_WALL, CNST_ROOF, CNST_FLOOR, etc.
Args:
exp_str: A TRACE 700 CNST_WALL, CNST_ROOF, CNST_FLOOR, CNST_EXFLOOR,
or CNST_PART entry string.
materials: A dictionary with identifiers of materials as keys and Python
material objects as values.
Returns:
A Honeybee OpaqueConstruction object.
"""
# deconstruct the string and get the identifier
exp_vals = exp_str.split(';')
con_id = clean_ep_string(exp_vals[1])
custom_mat_id = '{} Material'.format(con_id)
# collect all of the material layers
mat_layers, layers_available = [], True
for mat_id in exp_vals[4:14]:
if mat_id != 'None' and mat_id not in ('A0', 'E0'):
try:
mat_layers.append(materials[mat_id])
except KeyError:
# TRACE fails to export some materials because their ID is beyond 8 bits
layers_available = False
# if layers were not included in the EXP, try to build a construction from properties
# to make a full material, we will assume a thickness of 0.15 m unless weight == 0
if not layers_available:
# compute the density and specific heat capacity
thickness = 0.15 # assumed thickness in meters
weight, w_unit = float(exp_vals[31]), int(exp_vals[32])
if w_unit == 1:
weight = weight * 4.88243 # convert to kg/m2
denisty = weight / thickness # convert kg/m2 to kg/m3
sht, s_unit = float(exp_vals[33]), int(exp_vals[34])
if s_unit == 1:
sht = specific_heat_capacity_dt.to_unit([sht], 'J/kg-K', 'Btu/lb-F')[0]
# get the construction R-value in SI units
u_factor, u_unit = float(exp_vals[35]), int(exp_vals[36])
if u_unit == 1:
u_factor = uvalue_dt.to_unit([u_factor], 'W/m2-K', 'Btu/h-ft2-F')[0]
int_emiss = float(exp_vals[52])
_ext_r = 1 / 23 # exterior heat coefficient in m2-K/W
_int_r = 1 / (3.6 + (4.4 * int_emiss / 0.84)) # interior film
r_value = (1 / u_factor) - _ext_r - _int_r
r_value = 0.001 if r_value < 0.001 else r_value # odd assumption about air films
# return either a no mass or energy material
if weight == 0:
mat_layers = [EnergyMaterialNoMass(custom_mat_id, r_value)]
else:
u_value = 1 / r_value
conductivity = u_value * thickness
mat_layers = [EnergyMaterial(custom_mat_id, thickness, conductivity, denisty, sht)]
# get the exterior and interior layers
ext_mat = mat_layers[0]
int_mat = mat_layers[-1]
ext_mat.unlock()
int_mat.unlock()
# assign the properties of solar and visible absorption
int_mat.solar_absorptance = float(exp_vals[51])
int_mat.visible_reflectance = float(exp_vals[53])
ext_mat.solar_absorptance = float(exp_vals[39])
# assign the properties of infrared absorption
int_mat.thermal_absorptance = float(exp_vals[52])
ext_mat.thermal_absorptance = float(exp_vals[50])
# assign the exterior roughness
roughness_map = {
0: 'VeryRough', 1: 'Rough', 2: 'MediumRough', 3: 'MediumSmooth',
4: 'Smooth', 5: 'VerySmooth'
}
ext_mat.roughness = roughness_map[int(exp_vals[-2])]
return OpaqueConstruction(con_id, mat_layers)
[docs]
def window_construction_from_exp(exp_str):
"""Get a WindowConstruction (or OpaqueConstruction) from a TRACE 700 GLAS_GLASS.
Args:
exp_str: A TRACE 700 GLAS_GLASS entry string.
Returns:
A Honeybee WindowConstruction object (or an OpaqueConstruction object if
the GLAS_GLASS refers to a door construciton with zero solar transmittance).
"""
# deconstruct the string and get the critical properties
exp_vals = exp_str.split(';')
con_id = clean_ep_string(exp_vals[0])
mat_id = '{} Material'.format(con_id)
u_factor, u_unit = float(exp_vals[4]), int(exp_vals[5])
if u_unit == 1:
u_factor = uvalue_dt.to_unit([u_factor], 'W/m2-K', 'Btu/h-ft2-F')[0]
sc = float(exp_vals[3])
is_door = int(exp_vals[178])
# sense whether we will be returning an OpaqueConstruction for a door
if is_door == 2 and sc == 0: # it's an opaque construction
r_val = 1 / u_factor
int_emiss = float(exp_vals[12])
_ext_r = 1 / 23 # exterior heat coefficient in m2-K/W
_int_r = 1 / (3.6 + (4.4 * int_emiss / 0.84)) # interior film
r_val = r_val - _ext_r - _int_r
r_val = 0.001 if r_val < 0.001 else r_val # odd assumption about air films
door_mat = EnergyMaterialNoMass(mat_id, r_val)
door_mat.thermal_absorptance = float(exp_vals[12])
door_mat.solar_reflectance = float(exp_vals[11])
door_mat.visible_reflectance = float(exp_vals[9])
return OpaqueConstruction(con_id, [door_mat])
# create a simple galzing material from the proeprties
shgc = sc * 0.87 # convert from SC to SHGC
win_mat = EnergyWindowMaterialSimpleGlazSys(mat_id, u_factor, shgc)
win_mat.vt = float(exp_vals[9])
return WindowConstruction(con_id, [win_mat])
[docs]
def construction_set_from_exp(exp_str, constructions):
"""Get a ConstructionSet from a TRACE 700 ConstructionTemplate.
Args:
exp_str: A TRACE 700 ConstructionTemplate entry string.
constructions: A dictionary with identifiers of constructions as keys
and Pythonconstruction objects as values.
Returns:
A Honeybee ConstructionSet object.
"""
# deconstruct the string and create a base ConstructionSet with defaults
exp_vals = exp_str.split(';')
cs_id = clean_ep_string(exp_vals[0])
con_set = ConstructionSet(cs_id)
# get all of the standard constructions
con_set.floor_set.ground_construction = constructions[clean_ep_string(exp_vals[1])]
con_set.floor_set.exterior_construction = constructions[clean_ep_string(exp_vals[1])]
con_set.roof_ceiling_set.exterior_construction = constructions[clean_ep_string(exp_vals[4])]
con_set.aperture_set.skylight_construction = constructions[clean_ep_string(exp_vals[7])]
con_set.wall_set.exterior_construction = constructions[clean_ep_string(exp_vals[11])]
con_set.aperture_set.window_construction = constructions[clean_ep_string(exp_vals[14])]
con_set.aperture_set.operable_construction = constructions[clean_ep_string(exp_vals[14])]
con_set.wall_set.interior_construction = constructions[clean_ep_string(exp_vals[18])]
# assign the door construction depending on whether its glass or not
door_con_id = exp_vals[27]
if door_con_id != '': # TRACE fails to write door constructions into the EXP
door_con = constructions[clean_ep_string(door_con_id)]
if isinstance(door_con, OpaqueConstruction):
con_set.door_set.exterior_construction = door_con
else:
con_set.door_set.exterior_glass_construction = door_con
return con_set
[docs]
def construction_sets_from_exp(exp_file_content):
"""Get a list of Honeybee ConstructionSets from the content of an EXP file.
Args:
exp_file_content: Text string for the contents of an EXP file.
Returns:
A tuple with three elements.
- materials: A list of Honeybee materials.
- constructions: A list of Honeybee OpaqueConstructions and WindowConstructions.
- construction_sets: A list of Honeybee ConstructionSets.
"""
file_lines = exp_file_content.split('\n')
# load the individual materials from the file contents
materials, mat_section = {}, False
for line in file_lines:
if line.startswith('T.MATR_MATERIAL'):
mat_section = True
continue
elif line.startswith('T.'):
mat_section = False
if mat_section:
materials[line.split(';')[1]] = material_from_exp(line)
# load the individual opaque constructions from the file contents
con_types = ('T.CNST_EXFLOOR', 'T.CNST_FLOOR', 'T.CNST_PART', 'T.CNST_ROOF', 'T.CNST_WALL')
constructions, con_section = {}, False
for line in file_lines:
if any(line.startswith(ct) for ct in con_types):
con_section = True
continue
elif line.startswith('T.'):
con_section = False
if con_section:
o_con = opaque_construction_from_exp(line, materials)
constructions[line.split(';')[1]] = o_con
for mat in o_con.materials:
if mat.identifier not in materials:
materials[mat.identifier] = mat
# load the individual window constructions from the file contents
w_con_section = False
for line in file_lines:
if line.startswith('T.GLAS_GLASS'):
w_con_section = True
continue
elif line.startswith('T.'):
w_con_section = False
if w_con_section:
w_con = window_construction_from_exp(line)
constructions[line.split(';')[0]] = w_con
for mat in w_con.materials:
materials[mat.identifier] = mat
# create the construction sets
construction_sets = []
con_set_section = False
for line in file_lines:
if line.startswith('T.ConstructionTemplate'):
con_set_section = True
continue
elif line.startswith('T.'):
con_set_section = False
if con_set_section:
if line.split(';')[0] in ('Default', 'Metric Default'):
continue # TRACE 700 defaults do not have constructions in EXP
construction_sets.append(construction_set_from_exp(line, constructions))
return list(materials.values()), list(constructions.values()), construction_sets