# coding=utf-8
"""OpenStudio ventilative cooling translators."""
from __future__ import division
import re
from honeybee.boundarycondition import Outdoors
from honeybee_energy.ventcool.control import VentilationControl
from honeybee_openstudio.openstudio import OSZoneVentilationWindandStackOpenArea, \
OSZoneVentilationDesignFlowRate, OSAirflowNetworkCrack, OSAirflowNetworkSimpleOpening, \
OSAirflowNetworkHorizontalOpening, OSAirflowNetworkReferenceCrackConditions, \
OSOutputVariable, OSEnergyManagementSystemSensor, OSEnergyManagementSystemActuator, \
OSEnergyManagementSystemProgram, OSEnergyManagementSystemProgramCallingManager
[docs]
def ventilation_opening_to_openstudio(opening, os_model):
"""Convert VentilationOpening to OpenStudio ZoneVentilationWindandStackOpenArea.
Args:
opening: The Honeybee VentilationOpening object to be translated
to OpenStudio. Note that this object must be assigned to a parent
Aperture with a parent Room in order to be successfully translated.
os_model: The OpenStudio model to which the ZoneVentilationWindandStackOpenArea
will be added.
"""
# check that a parent is assigned
assert opening.parent is not None, 'VentilationOpening must be assigned ' \
'to an Aperture or Door to translate to_openstudio.'
# get the VentilationControl object from the room
control = None
room = None
if opening.parent.has_parent:
if opening.parent.parent.has_parent:
room = opening.parent.parent.parent
if room.properties.energy.window_vent_control is not None:
control = room.properties.energy.window_vent_control
if control is None: # use default ventilation control
control = VentilationControl()
assert room is not None, 'VentilationOpening must have a parent Room to ' \
'translate to_openstudio.'
# process the properties on this object into IDF format
angle = opening.parent.horizontal_orientation() \
if opening.parent.normal.z != 1 else 0
angle = angle % 360
height = (opening.parent.geometry.max.z - opening.parent.geometry.min.z) * \
opening.fraction_height_operable
# create wind and stack object and set all of its properties
os_opening = OSZoneVentilationWindandStackOpenArea(os_model)
os_opening.setName('{}_Opening'.format(opening.parent.identifier))
os_opening.setOpeningArea(opening.parent.area * opening.fraction_area_operable)
os_opening.setHeightDifference(height)
os_opening.setEffectiveAngle(angle)
os_opening.setDischargeCoefficientforOpening(opening.discharge_coefficient)
if opening.wind_cross_vent:
os_opening.autocalculateOpeningEffectiveness()
else:
os_opening.setOpeningEffectiveness(0)
# set the properties of the ventilation control
os_opening.setMinimumIndoorTemperature(control.min_indoor_temperature)
os_opening.setMaximumIndoorTemperature(control.max_indoor_temperature)
os_opening.setMinimumOutdoorTemperature(control.min_outdoor_temperature)
os_opening.setMaximumOutdoorTemperature(control.max_outdoor_temperature)
os_opening.setDeltaTemperature(control.delta_temperature)
if control.schedule.identifier != 'Always On':
vent_sch = os_model.getScheduleByName(control.schedule.identifier)
if vent_sch.is_initialized():
os_vent_sch = vent_sch.get()
os_opening.setOpeningAreaFractionSchedule(os_vent_sch)
return os_opening
[docs]
def ventilation_fan_to_openstudio(fan, os_model):
"""Convert VentilationFan to OpenStudio ZoneVentilationDesignFlowRate."""
# create zone ventilation object and set identifier
os_fan = OSZoneVentilationDesignFlowRate(os_model)
os_fan.setName(fan.identifier)
if fan._display_name is not None:
os_fan.setDisplayName(fan.display_name)
# assign fan properties
os_fan.setDesignFlowRate(fan.flow_rate)
os_fan.setFanPressureRise(fan.pressure_rise)
os_fan.setFanTotalEfficiency(fan.efficiency)
os_fan.setVentilationType(fan.ventilation_type)
# set all of the ventilation control properties
os_fan.setMinimumIndoorTemperature(fan.control.min_indoor_temperature)
os_fan.setMaximumIndoorTemperature(fan.control.max_indoor_temperature)
os_fan.setMinimumOutdoorTemperature(fan.control.min_outdoor_temperature)
os_fan.setMaximumOutdoorTemperature(fan.control.max_outdoor_temperature)
os_fan.setDeltaTemperature(fan.control.delta_temperature)
# assign schedule if it exists
if fan.control.schedule.identifier != 'Always On':
vent_sch = os_model.getScheduleByName(fan.control.schedule.identifier)
if vent_sch.is_initialized():
os_vent_sch = vent_sch.get()
os_fan.setSchedule(os_vent_sch)
return os_fan
[docs]
def ventilation_sim_control_to_openstudio(vent_sim_control, os_model):
"""Convert VentilationSimulationControl to OpenStudio.
This method returns an AirflowNetworkReferenceCrackConditions that can
be used for the rest of the AFN setup.
"""
# create the AirflowNetworkSimulationControl object
os_v_sim_ctrl = os_model.getAirflowNetworkSimulationControl()
os_v_sim_ctrl.setName('Window Based Ventilative Cooling')
os_v_sim_ctrl.setAirflowNetworkControl(vent_sim_control.vent_control_type)
os_v_sim_ctrl.setBuildingType(vent_sim_control.building_type)
os_v_sim_ctrl.setAzimuthAngleofLongAxisofBuilding(vent_sim_control.long_axis_angle)
os_v_sim_ctrl.setBuildingAspectRatio(vent_sim_control.aspect_ratio)
# create the AirflowNetworkReferenceCrackConditions that other cracks reference
os_ref_crack = OSAirflowNetworkReferenceCrackConditions(os_model)
os_ref_crack.setName('Reference Crack Conditions')
os_ref_crack.setTemperature(vent_sim_control.reference_temperature)
os_ref_crack.setBarometricPressure(vent_sim_control.reference_pressure)
os_ref_crack.setHumidityRatio(vent_sim_control.reference_humidity_ratio)
return os_ref_crack
[docs]
def afn_crack_to_openstudio(afn_crack, os_model, os_reference_crack=None):
"""Convert Honeybee AFNCrack to OpenStudio AirflowNetworkCrack.
Args:
opening: The Honeybee VentilationOpening object to be translated
to OpenStudio. Note that this object must be assigned to a parent
Aperture with a parent Room in order to be successfully translated.
os_model: The OpenStudio model to which the AirflowNetworkSurface
will be added.
os_reference_crack: An optional AirflowNetworkReferenceCrackConditions
object to set the reference. If None, a default reference crack will
be created. (Default: None).
"""
flow_coefficient = afn_crack.flow_coefficient \
if afn_crack.flow_coefficient > 1.0e-09 else 1.0e-09
flow_exponent = afn_crack.flow_exponent
if os_reference_crack is None:
os_reference_crack = _default_reference_crack(os_model)
os_crack = OSAirflowNetworkCrack(
os_model, flow_coefficient, flow_exponent, os_reference_crack)
return os_crack
[docs]
def ventilation_opening_to_openstudio_afn(opening, os_model, os_reference_crack=None):
"""Convert Honeybee VentilationOpening to OpenStudio AirflowNetworkSimpleOpening.
The returned output may also be a AirflowNetworkHorizontalOpening or a
AirflowNetworkCrack if the opening is assigned to a parent Aperture or Door
that is horizontal and so it cannot be represented with AirflowNetworkSimpleOpening.
Args:
opening: The Honeybee VentilationOpening object to be translated
to OpenStudio. Note that this object must be assigned to a parent
Aperture with a parent Room in order to be successfully translated.
os_model: The OpenStudio model to which the AirflowNetworkSimpleOpening
will be added.
os_reference_crack: An optional AirflowNetworkReferenceCrackConditions
object to set the reference when the ventilation opening is being
translated to a large crack. This happens when the ventilation
opening is horizontal and in an outdoor Face. If None, a default
reference crack will be created. (Default: None).
Returns:
A tuple with two elements.
- os_opening -- The OpenStudio AFN SimpleOpening, HorizontalOpening
or Crack that represents the ventilation opening.
- opening_factor - A number for the opening factor to be assigned to
the parent OpenStudio AirflowNetworkSurface and incorporated into
the EMS program.
"""
# check that a parent is assigned
assert opening.parent is not None, 'VentilationOpening must be assigned ' \
'to an Aperture or Door to translate to_openstudio.'
# get the tilt and BC of the parent so that we can use the correct AFN object
srf_tilt = opening.parent.tilt
srf_bc = opening.parent.boundary_condition
# process the flow coefficient, flow exponent and fraction area operable
flow_coeff = opening.flow_coefficient_closed \
if opening.flow_coefficient_closed > 1.0e-09 else 1.0e-09
flow_exponent = opening.flow_exponent_closed
discharge_coeff = opening.discharge_coefficient
two_way_thresh = opening.two_way_threshold
opening_factor = opening.fraction_area_operable
# create an opening obj
if srf_tilt < 10 or srf_tilt > 170:
if isinstance(srf_bc, Outdoors):
# create a crack to represent an exterior in-operable horizontal skylight
opening_factor = None
if os_reference_crack is None:
os_reference_crack = _default_reference_crack(os_model)
os_opening = OSAirflowNetworkCrack(
os_model, flow_coeff, flow_exponent, os_reference_crack)
else:
# create a HorizontalOpening object to for the interior horizontal window
slope_ang = 90 - srf_tilt if srf_tilt < 10 else 90 - (180 - srf_tilt)
os_opening = OSAirflowNetworkHorizontalOpening(
os_model, flow_coeff, flow_exponent, slope_ang, discharge_coeff)
else:
# create the simple opening object for the Aperture or Door using default values
os_opening = OSAirflowNetworkSimpleOpening(
os_model, flow_coeff, flow_exponent, two_way_thresh, discharge_coeff)
os_opening.setName('{}_Opening'.format(opening.parent.identifier))
return os_opening, opening_factor
[docs]
def ventilation_control_to_openstudio_afn(
control, open_factors, os_sub_faces, os_zone_air_temp, os_model, room_id=''):
"""Convert Honeybee VentilationControl to OpenStudio EnergyManagementSystemProgram.
Args:
control: The Honeybee VentilationControl object to be translated to OpenStudio.
open_factors: A list of numbers for the opening factor of each Subface
to be controlled by the VentilationControl.
os_sub_faces: A list of OpenStudio SubSurface objects that have AFN
SimpleOpening or HorizontalOpening objects to be controlled by the EMS.
os_zone_air_temp: The OpenStudio EnergyManagementSystemSensor object for the
Zone Air Temperature that corresponds with the VentilationControl.
If this sensor does not yet exist in the model, the zone_temperature_sensor
function in this module can be used to create it.
os_model: The OpenStudio model to which the EnergyManagementSystemProgram
will be added.
room_id: An optional Room identifier to be used to ensure the names used
in the resulting EnergyManagementSystemProgram are unique to the
Room to which the VentilationControl is applied.
"""
# set up a schedule sensor if there's a schedule specified
sch_sen_id = None
if control.schedule.identifier != 'Always On':
vent_sch = os_model.getScheduleByName(control.schedule.identifier)
if vent_sch.is_initialized():
sch_var = OSOutputVariable('Schedule Value', os_model)
sch_var.setReportingFrequency('Timestep')
sch_var.setKeyValue(control.schedule.identifier)
sch_sensor = OSEnergyManagementSystemSensor(os_model, sch_var)
sch_sen_id = 'SensorSch{}'.format(re.sub('[^A-Za-z0-9]', '', room_id))
sch_sensor.setName(sch_sen_id)
# create the actuators for each of the operable windows
actuator_ids = []
for os_sub_f in os_sub_faces:
window_actuator = OSEnergyManagementSystemActuator(
os_sub_f, 'AirFlow Network Window/Door Opening',
'Venting Opening Factor')
act_id = 'OpenFactor{}'.format(re.sub('[^A-Za-z0-9]', '', os_sub_f.nameString()))
window_actuator.setName(act_id)
actuator_ids.append(act_id)
# create the first part of the EMS Program to open windows according to control logic
logic_statements = []
in_sen_id = os_zone_air_temp.nameString()
min_in = control.min_indoor_temperature
max_in = control.max_indoor_temperature
min_out = control.min_outdoor_temperature
max_out = control.max_outdoor_temperature
d_in_out = control.delta_temperature
if min_in != -100:
logic_statements.append('({} > {})'.format(in_sen_id, min_in))
if max_in != 100:
logic_statements.append('({} < {})'.format(in_sen_id, max_in))
if min_out != -100:
logic_statements.append('(Outdoor_Sensor > {})'.format(min_out))
if max_out != 100:
logic_statements.append('(Outdoor_Sensor < {})'.format(max_out))
if d_in_out != -100:
logic_statements.append('(({} - Outdoor_Sensor) > {})'.format(in_sen_id, d_in_out))
if sch_sen_id is not None:
logic_statements.append('({} > 0)'.format(sch_sen_id))
if len(logic_statements) == 0: # no logic has been provided; always open windows
complete_logic = 'IF (Outdoor_Sensor < 100)'
else:
complete_logic = 'IF {}'.format(' && '.join(logic_statements))
# initialize the program and add the complete logic
ems_program = OSEnergyManagementSystemProgram(os_model)
prog_name = 'WindowOpening{}'.format(re.sub('[^A-Za-z0-9]', '', room_id))
ems_program.setName(prog_name)
ems_program.addLine(complete_logic)
# loop through each of the actuators and open/close each window
for act_id, open_factor in zip(actuator_ids, open_factors):
ems_program.addLine('SET {} = {}'.format(act_id, open_factor))
ems_program.addLine('ELSE')
for act_id in actuator_ids:
ems_program.addLine('SET {} = 0'.format(act_id))
ems_program.addLine('ENDIF')
return ems_program
[docs]
def zone_temperature_sensor(os_zone, os_model):
"""Create an EnergyManagementSystemSensor for Zone Air Temperature."""
os_zone_name = os_zone.nameString()
in_var = OSOutputVariable('Zone Air Temperature', os_model)
in_var.setReportingFrequency('Timestep')
in_var.setKeyValue(os_zone_name)
in_air_sensor = OSEnergyManagementSystemSensor(os_model, in_var)
sensor_id = 'Sensor{}'.format(re.sub('[^A-Za-z0-9]', '', os_zone_name))
in_air_sensor.setName(sensor_id)
return in_air_sensor
[docs]
def outdoor_temperature_sensor(os_model):
"""Create an EnergyManagementSystemSensor for Site Outdoor Air DryBulb Temperature.
"""
out_var = OSOutputVariable('Site Outdoor Air Drybulb Temperature', os_model)
out_var.setReportingFrequency('Timestep')
out_var.setKeyValue('Environment')
out_air_sensor = OSEnergyManagementSystemSensor(os_model, out_var)
out_air_sensor.setName('Outdoor_Sensor')
return out_air_sensor
[docs]
def ventilation_control_program_manager(os_model):
"""Create an EMS Program Manager for all window opening."""
os_prog_manager = OSEnergyManagementSystemProgramCallingManager(os_model)
os_prog_manager.setName('Temperature_Controlled_Window_Opening')
os_prog_manager.setCallingPoint('BeginTimestepBeforePredictor')
return os_prog_manager
def _default_reference_crack(os_model):
"""Create an AFN Reference Crack with default characteristics."""
default_name = 'Reference Crack Conditions'
os_ref_crack = os_model.getAirflowNetworkReferenceCrackConditionsByName(default_name)
if os_ref_crack.is_initialized():
return os_ref_crack.get()
os_ref_crack = OSAirflowNetworkReferenceCrackConditions(os_model)
os_ref_crack.setTemperature(20)
os_ref_crack.setBarometricPressure(101325)
os_ref_crack.setHumidityRatio(0)
return os_ref_crack