Source code for honeybee_doe2.programtype

"""honeybee-doe2 program type translators."""
from __future__ import division

from collections import defaultdict

from honeybee_energy.programtype import ProgramType
from honeybee.typing import clean_string

from .util import switch_statement_id, resolve_defaults, \
        child_objects_from_parent
from .load import people_to_inp, lighting_to_inp, electric_equipment_to_inp, \
    infiltration_to_inp, setpoint_to_inp, ventilation_to_inp, \
    people_from_inp, lighting_from_inp, electric_equipment_from_inp, \
    infiltration_from_inp, setpoint_from_inp, ventilation_from_inp, \
    SPACE_KEYS, ZONE_KEYS, SCHEDULE_KEYS, MECH_AIRFLOW_KEYS
from .schedule import build_schedule_dict, resolve_schedule
SCH_KEY_SET = set(SCHEDULE_KEYS)


[docs] def program_type_to_inp(program_type, switch_dict=None): """Translate a ProgramType into a dictionary used to write INP switch statements. Args: program_type: A honeybee-energy ProgramType definition. switch_dict: An optional dictionary with INP keywords as keys (such as PEOPLE-SCHEDULE or AREA/PERSON or LIGHTING-W/AREA). The values of the dictionary should be lists of switch statement text strings, such as 'case "conf": #SI("Small Off Occ", "SPACE", "PEOPLE-SCHEDULE")'. Specifying an input dictionary here can be used to build up switch statements for all program types across a model. Returns: An dictionary with INP keywords as keys (such as PEOPLE-SCHEDULE or AREA/PERSON). The values of the dictionary are lists of switch statement text strings, such as 'case "conf": #SI("Small Off Occ", "SPACE", "PEOPLE-SCHEDULE")'. """ # set up the switch statement dictionary to be filled switch_dict = switch_dict if switch_dict is not None else {} prog_uid = switch_statement_id(program_type.identifier) base_switch = 'case "{}": '.format(prog_uid) def _format_schedule(sch_key, sch_uid, obj_type='SPACE'): """Format schedules in the way they are written into switch statements.""" return '{}#SI({}, "{}", "{}")'.format(base_switch, sch_uid, obj_type, sch_key) def _add_to_switch_dict(keyword, value): """Add a key: value pair to the switch dictionary with a check.""" try: switch_dict[keyword].append(value) except KeyError: # the first time this key was encountered in the dict switch_dict[keyword] = [value] # write the people into the dictionary ppl_kwd, ppl_val = people_to_inp(program_type.people) for key, val in zip(ppl_kwd, ppl_val): if key in SCH_KEY_SET: _add_to_switch_dict(key, _format_schedule(key, val, 'SPACE')) else: _add_to_switch_dict(key, '{}{}'.format(base_switch, val)) # write the lighting into the dictionary lgt_kwd, lgt_val = lighting_to_inp(program_type.lighting) for key, val in zip(lgt_kwd, lgt_val): if key == 'LIGHTING-SCHEDULE': key = 'LIGHTING-SCHEDUL' # there's a typo in DOE-2 that was never fixed _add_to_switch_dict(key, _format_schedule(key, val, 'SPACE')) else: _add_to_switch_dict(key, '{}{}'.format(base_switch, val)) # write the equipment into the dictionary eq_kwd, eq_val = electric_equipment_to_inp(program_type.electric_equipment) for key, val in zip(eq_kwd, eq_val): if key in SCH_KEY_SET: _add_to_switch_dict(key, _format_schedule(key, val, 'SPACE')) else: _add_to_switch_dict(key, '{}{}'.format(base_switch, val)) # write the infiltration into the dictionary inf_kwd, inf_val = infiltration_to_inp(program_type.infiltration) for key, val in zip(inf_kwd, inf_val): if key in SCH_KEY_SET: _add_to_switch_dict(key, _format_schedule(key, val, 'SPACE')) elif key == 'INF-METHOD': continue # DOE-2 does not like when we define this key else: _add_to_switch_dict(key, '{}{}'.format(base_switch, val)) # write the setpoint into the dictionary stp_kwd, stp_val = setpoint_to_inp(program_type.setpoint) for key, val in zip(stp_kwd, stp_val): if key in SCH_KEY_SET: _add_to_switch_dict(key, _format_schedule(key, val, 'ZONE')) else: _add_to_switch_dict(key, '{}{}'.format(base_switch, val)) # write the ventilation into the dictionary vt_kwd, vt_val = ventilation_to_inp(program_type.ventilation) for key, val in zip(vt_kwd, vt_val): if key in SCH_KEY_SET: _add_to_switch_dict(key, _format_schedule(key, val, 'ZONE')) else: _add_to_switch_dict(key, '{}{}'.format(base_switch, val)) # if the user_data of the ProgramType has Mech AirFlow keys, add them if program_type.user_data is not None: for air_key in MECH_AIRFLOW_KEYS: if air_key in program_type.user_data: val = program_type.user_data[air_key] _add_to_switch_dict(air_key, '{}{}'.format(base_switch, val)) return switch_dict
[docs] def program_type_from_inp(cmd_dict, lib_file_path=None): """Create ProgramType objects from a parsed INP command dictionary. Spaces are grouped by their C-ACTIVITY-DESC attribute. For each unique activity description, a representative space and its linked zone are used to resolve SET-DEFAULT fallback values and build a ProgramType containing People, Lighting, ElectricEquipment, Infiltration, Setpoint, and Ventilation objects. Args: cmd_dict: The command dictionary returned by ``command_dict_from_inp``. lib_file_path: Optional path to eQUEST library file (eQ_Lib.dat) for resolving library schedules. If None, library schedules won't be resolved. Returns: A list of honeybee-energy ProgramType objects, one per unique C-ACTIVITY-DESC found in the model's spaces. """ spaces = cmd_dict.get('SPACE', {}) zones = cmd_dict.get('ZONE', {}) systems = cmd_dict.get('SYSTEM', {}) defaults = cmd_dict.get('DEFAULTS', {}) day_schs = cmd_dict.get('DAY-SCHEDULE-PD', {}) wk_schs = cmd_dict.get('WEEK-SCHEDULE-PD', {}) annual_schs = cmd_dict.get('SCHEDULE-PD', {}) # Get the DEFAULTS dicts for SPACE and ZONE space_defaults = defaults.get(('SPACE', ''), {}) zone_defaults = defaults.get(('ZONE', 'CONDITIONED'), {}) # Map zone name to parent system attrs for MIN-AIR-SCH lookup zone_to_system = {} for sys_name, sys_attrs in systems.items(): sys_zones = child_objects_from_parent( cmd_dict, sys_name, 'SYSTEM', 'ZONE') sys_type = sys_attrs.get('TYPE', '').strip('"') sys_defaults = defaults.get(('SYSTEM', sys_type), {}) for z_name in sys_zones: # Grabbing just MIN-AIR-SCH zone_to_system[z_name] = resolve_defaults( sys_attrs, sys_defaults, ('MIN-AIR-SCH',)) # Map space to linked zone space_to_zone = {} space_to_zone_name = {} for z_name, z_attrs in zones.items(): spc_ref = str(z_attrs.get('SPACE', '')).strip('"') if spc_ref: space_to_zone[spc_ref] = resolve_defaults( z_attrs, zone_defaults, ZONE_KEYS) space_to_zone_name[spc_ref] = z_name # Group spaces by C-ACTIVITY-DESC activity_groups = defaultdict(list) for spc_name, spc_attrs in spaces.items(): activity = (str(spc_attrs.get('C-ACTIVITY-DESC', '')) .replace('*', '') .strip()) if activity: activity_groups[activity].append((spc_name, spc_attrs)) program_types = [] for activity, spc_list in activity_groups.items(): # use the first space as the program type attrs spc_name, spc_attrs = spc_list[0] # resolve defaults if any resolved_space = resolve_defaults(spc_attrs, space_defaults, SPACE_KEYS) # find the linked zone attrs resolved_zone = space_to_zone.get(spc_name) if resolved_zone is None: # No linked zone - apply defaults to empty dict resolved_zone = resolve_defaults({}, zone_defaults, ZONE_KEYS) # Get MIN-AIR-SCH from parent SYSTEM if available zone_name = space_to_zone_name.get(spc_name) parent_system = zone_to_system.get(zone_name, {}) if zone_name else {} # Add C-ACTIVITY-DESC from the space so switch solving works if 'C-ACTIVITY-DESC' not in resolved_zone: resolved_zone = dict(resolved_zone) resolved_zone['C-ACTIVITY-DESC'] = spc_attrs.get('C-ACTIVITY-DESC', '') # Build schedule dict for any schedules referenced in the space/zone schedule_dict = build_schedule_dict(day_schs, wk_schs, annual_schs) # Resolve schedules for space loads people_sch = resolve_schedule( resolved_space.get('PEOPLE-SCHEDULE'), schedule_dict, lib_file_path) lighting_sch = resolve_schedule( resolved_space.get('LIGHTING-SCHEDULE') or resolved_space.get('LIGHTING-SCHEDUL'), schedule_dict, lib_file_path) equip_sch = resolve_schedule( resolved_space.get('EQUIP-SCHEDULE'), schedule_dict, lib_file_path) inf_sch = resolve_schedule( resolved_space.get('INF-SCHEDULE'), schedule_dict, lib_file_path) # Resolve zone level schedules heat_sch = resolve_schedule( resolved_zone.get('HEAT-TEMP-SCH'), schedule_dict, lib_file_path) cool_sch = resolve_schedule( resolved_zone.get('COOL-TEMP-SCH'), schedule_dict, lib_file_path) # Resolve MIN-AIR-SCH from parent SYSTEM min_air_sch_name = ( parent_system.get('MIN-AIR-SCH') or resolved_zone.get('MIN-FLOW-SCH')) min_air_sch = resolve_schedule( min_air_sch_name, schedule_dict, lib_file_path) # Build Program Type loads with resolved schedules people = people_from_inp(resolved_space, people_sch) lighting = lighting_from_inp(resolved_space, lighting_sch) equipment = electric_equipment_from_inp(resolved_space, equip_sch) infiltration = infiltration_from_inp(resolved_space, inf_sch) # Skip setpoint if schedules Always On if (heat_sch.identifier != 'Always On' and cool_sch.identifier != 'Always On'): setpoint = setpoint_from_inp(resolved_zone, heat_sch, cool_sch) else: setpoint = None ventilation = ventilation_from_inp(resolved_zone, min_air_sch) prog_id = clean_string(activity) prog = ProgramType(prog_id) prog.display_name = activity if people is not None: prog.people = people if lighting is not None: prog.lighting = lighting if equipment is not None: prog.electric_equipment = equipment if infiltration is not None: prog.infiltration = infiltration if setpoint is not None: prog.setpoint = setpoint if ventilation is not None: prog.ventilation = ventilation program_types.append(prog) return program_types
[docs] def switch_dict_to_space_inp(switch_dict): """Translate a switch statement dictionary into INP strings for the SPACE. Args: switch_dict: An dictionary with INP keywords as keys (such as PEOPLE-SCHEDULE or AREA/PERSON or LIGHTING-W/AREA). The values of the dictionary should be lists of switch statement text strings, such as 'case "conf": #SI("Small Off Occ", "SPACE", "PEOPLE-SCHEDULE")'. Returns: A text string to be written into an INP file. This should go at the top of the description of Floors / Spaces. """ # loop through the space keys and build a list of all switch statement keys all_switch_strs = [] for s_key in SPACE_KEYS: try: if len(s_key) > 16: # switch statements limit characters to 16 s_key = s_key[:16] switch_progs = switch_dict[s_key] switch_strs = ['SET-DEFAULT FOR SPACE'] switch_strs.append(' {} ='.format(s_key)) switch_strs.append('{switch(#L("C-ACTIVITY-DESC"))') switch_strs.extend(switch_progs) switch_strs.append('default: no_default') switch_strs.append('endswitch}') switch_strs.append('..\n') all_switch_strs.append('\n'.join(switch_strs)) except KeyError: pass # none of the programs types have this space key # add something to set the INF-METHOD for all spaces inf_str = 'SET-DEFAULT FOR SPACE\n' \ ' INF-METHOD = AIR-CHANGE\n' \ '..\n' all_switch_strs.append(inf_str) return '\n'.join(all_switch_strs)
[docs] def switch_dict_to_zone_inp(switch_dict): """Translate a switch statement dictionary into INP strings for the ZONE. Args: switch_dict: An dictionary with INP keywords as keys (such as HEAT-TEMP-SCH or DESIGN-HEAT-T or OUTSIDE-AIR-FLOW). The values of the dictionary should be lists of switch statement text strings, such as 'case "conf": #SI("Small Off HtgStp", "SPACE", "HEAT-TEMP-SCH")'. Returns: A text string to be written into an INP file. This should go at the top of the description of HVAC Systems / Zones. """ # loop through the space keys and build a list of all switch statement keys all_switch_strs = [] for s_key in ZONE_KEYS: try: switch_progs = switch_dict[s_key] switch_strs = ['SET-DEFAULT FOR ZONE', ' TYPE = CONDITIONED'] switch_strs.append(' {} ='.format(s_key)) switch_strs.append('{switch(#LR("SPACE", "C-ACTIVITY-DESC"))') switch_strs.extend(switch_progs) switch_strs.append('default: no_default') switch_strs.append('endswitch}') switch_strs.append('..\n') all_switch_strs.append('\n'.join(switch_strs)) except KeyError: pass # none of the programs types have this space key return '\n'.join(all_switch_strs)