Source code for honeybee_doe2.load

"""honeybee-doe2 load translators."""
from __future__ import division

from ladybug.datatype.area import Area
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 honeybee.typing import clean_doe2_string

from honeybee_energy.load.people import People
from honeybee_energy.load.lighting import Lighting
from honeybee_energy.load.equipment import ElectricEquipment
from honeybee_energy.load.infiltration import Infiltration
from honeybee_energy.load.setpoint import Setpoint
from honeybee_energy.load.ventilation import Ventilation
from honeybee_energy.lib.scheduletypelimits import fractional, temperature
from honeybee_energy.schedule.ruleset import ScheduleRuleset

from .config import RES_CHARS

# list of all keywords associated with different load types
PEOPLE_KEYS = ('AREA/PERSON', 'PEOPLE-SCHEDULE')
LIGHTING_KEYS = ('LIGHTING-W/AREA', 'LIGHTING-SCHEDULE', 'LIGHT-TO-RETURN',
                 'LIGHT-RAD-FRAC')
EQUIP_KEYS = ('EQUIPMENT-W/AREA', 'EQUIP-SCHEDULE',
              'EQUIP-SENSIBLE', 'EQUIP-LATENT', 'EQUIP-RAD-FRAC')
SOURCE_KEYS = ('SOURCE-TYPE', 'SOURCE-POWER', 'SOURCE-SCHEDULE',
               'SOURCE-SENSIBLE', 'SOURCE-RAD-FRAC', 'SOURCE-LATENT')
INFILTRATION_KEYS = ('INF-METHOD', 'INF-FLOW/AREA', 'INF-SCHEDULE')
SETPOINT_KEYS = ('DESIGN-HEAT-T', 'DESIGN-COOL-T', 'HEAT-TEMP-SCH', 'COOL-TEMP-SCH')
VENTILATION_KEYS = ('OA-FLOW/PER', 'OA-FLOW/AREA', 'OA-CHANGES', 'OUTSIDE-AIR-FLOW',
                    'MIN-FLOW-SCH')
MECH_AIRFLOW_KEYS = ('ASSIGNED-FLOW', 'FLOW/AREA', 'MIN-FLOW-RATIO', 'MIN-FLOW/AREA',
                     'HMAX-FLOW-RATIO')
SPACE_KEYS = PEOPLE_KEYS + LIGHTING_KEYS + EQUIP_KEYS + INFILTRATION_KEYS + \
    ('LIGHTING-SCHEDUL',)  # include DOE-2 typo for LIGHTING-SCHEDULE
ZONE_KEYS = SETPOINT_KEYS + VENTILATION_KEYS + MECH_AIRFLOW_KEYS
SCHEDULE_KEYS = (
    'PEOPLE-SCHEDULE', 'LIGHTING-SCHEDULE', 'EQUIP-SCHEDULE', 'SOURCE-SCHEDULE',
    'INF-SCHEDULE', 'HEAT-TEMP-SCH', 'COOL-TEMP-SCH', 'MIN-FLOW-SCH')

# TODO: Add methods to map honeybee_energy process loads to SOURCE-TYPE PROCESS
# TODO: Add methods to translate daylight sensors


def _clean_schedule_id(raw_value):
    """Strip quotes and parentheses from a schedule identifier string."""
    if raw_value is None:
        return 'Always On'
    s = str(raw_value).strip()
    for ch in ('"', "'", '(', ')'):
        s = s.replace(ch, '')
    return s.strip() or 'Always On'


[docs] def people_to_inp(people): """Translate a People definition into INP (Keywords, Values). Args: people: A honeybee-energy People definition. None is allowed. Returns: A tuple with two elements. - keywords: A tuple of text strings for keywords related to defining people for a Space. - values: A tuple of text strings that aligns with the keywords and denotes the value for each keyword. """ if people is None: return (), () ppl_den = Area().to_unit([people.area_per_person], 'ft2', 'm2')[0] ppl_den = round(ppl_den, 3) ppl_sch = clean_doe2_string(people.occupancy_schedule.identifier, RES_CHARS) ppl_sch = '"{}"'.format(ppl_sch) return PEOPLE_KEYS, (ppl_den, ppl_sch)
[docs] def people_from_inp(resolved, occupancy_schedule=None): """Create a People object from resolved INP attributes. Args: resolved: A dict of resolved INP key-value pairs Expected keys: AREA/PERSON, PEOPLE-SCHEDULE occupancy_schedule: Optional ScheduleRuleset for occupancy. If None, a constant placeholder schedule is created. Returns: A honeybee-energy People object, or None if the data is insufficient. """ area_per_person = resolved.get('AREA/PERSON') if area_per_person is None: return None # Convert ft2/person to m2/person area_si = Area().to_unit([float(area_per_person)], 'm2', 'ft2')[0] people_per_area = 1.0 / area_si if area_si > 0 else 0 sch_id = _clean_schedule_id(resolved.get('PEOPLE-SCHEDULE', 'Always On')) if occupancy_schedule is None: occupancy_schedule = ScheduleRuleset.from_constant_value( sch_id, 1, fractional) return People(sch_id, people_per_area, occupancy_schedule)
[docs] def lighting_to_inp(lighting): """Translate a Lighting definition into INP (Keywords, Values). Args: lighting: A honeybee-energy Lighting definition. None is allowed. Returns: A tuple with two elements. - keywords: A tuple of text strings for keywords related to defining lighting for a Space. - values: A tuple of text strings that aligns with the keywords and denotes the value for each keyword. """ if lighting is None: return (), () lpd = EnergyFlux().to_unit([lighting.watts_per_area], 'W/ft2', 'W/m2')[0] lpd = round(lpd, 3) lgt_sch = clean_doe2_string(lighting.schedule.identifier, RES_CHARS) lgt_sch = '"{}"'.format(lgt_sch) ret_fract = round(lighting.return_air_fraction, 3) rad_fract = round(lighting.radiant_fraction, 3) return LIGHTING_KEYS, (lpd, lgt_sch, ret_fract, rad_fract)
[docs] def electric_equipment_to_inp(electric_equip): """Translate an ElectricEquipment into INP (Keywords, Values). Args: electric_equip: A honeybee-energy ElectricEquipment definition. Returns: A tuple with two elements. - keywords: A tuple of text strings for keywords related to defining the equipment for a Space. - values: A tuple of text strings that aligns with the keywords and denotes the value for each keyword. """ if electric_equip is None: return (), () epd = EnergyFlux().to_unit([electric_equip.watts_per_area], 'W/ft2', 'W/m2')[0] epd = round(epd, 3) eqp_sch = clean_doe2_string(electric_equip.schedule.identifier, RES_CHARS) eqp_sch = '("{}")'.format(eqp_sch) sens_fract = 1 - electric_equip.latent_fraction - electric_equip.lost_fraction values = (epd, eqp_sch, sens_fract, electric_equip.latent_fraction, electric_equip.radiant_fraction) return EQUIP_KEYS, values
[docs] def hot_water_and_gas_to_inp(hot_water, gas_equip, room_floor_area): """Translate a ServiceHotWater and/or GasEquipment into INP (Keywords, Values). Args: hot_water: A honeybee-energy ServiceHotWater definition. None is allowed. None is allowed. gas_equip: gas_equip: A honeybee-energy GasEquipment definition. None is allowed. room_floor_area: The host Room floor area in square feet, which will be used to convert the hot water flow per unit floor area to an absolute load in BTU/h. Returns: A tuple with two elements. - keywords: A tuple of text strings for keywords related to defining the hot water SOURCE load for a Space. - values: A tuple of text strings that aligns with the keywords and denotes the value for each keyword. """ # first check whether anything is assigned if hot_water is None and gas_equip is None: return (), () # process the hot water and gas into absolute values in Btu/h shw_values, gas_values = None, None if hot_water is not None: flow_den = hot_water.flow_per_area # L/h-m2 flr_area = Area().to_unit([room_floor_area], 'm2', 'ft2')[0] # m2 total_flow = flow_den * flr_area # L/h delta_t = 50 # assume the water heater must heat water from 10C to 60C c_water = 4.186 # J/g-C, the specific heat of water shw_heat = total_flow * c_water * delta_t # J/h using Q = m * c * deltaT shw_heat = shw_heat / 3600. # Watts shw_power = round(Power().to_unit([shw_heat], 'Btu/h', 'W')[0], 3) shw_sch = clean_doe2_string(hot_water.schedule.identifier, RES_CHARS) shw_sch = '"{}"'.format(shw_sch) sens_fract = round(hot_water.sensible_fraction, 3) lat_fract = round(hot_water.latent_fraction, 3) shw_values = ('HOT-WATER', shw_power, shw_sch, sens_fract, 0.0, lat_fract) if gas_equip is not None: epd = EnergyFlux().to_unit([gas_equip.watts_per_area], 'Btu/h-ft2', 'W/m2')[0] total_power = round(epd * flr_area, 3) # Btu/h eqp_sch = clean_doe2_string(gas_equip.schedule.identifier, RES_CHARS) eqp_sch = '"{}"'.format(eqp_sch) sens_fract = round(1 - gas_equip.latent_fraction - gas_equip.lost_fraction, 3) rad_fract = round(gas_equip.radiant_fraction, 3) lat_fract = round(gas_equip.latent_fraction, 3) gas_values = ('GAS', total_power, eqp_sch, sens_fract, rad_fract, lat_fract) # if both were specified, format them into a single set of numbers if shw_values is not None and gas_values is not None: total_load = round(shw_values[1] + gas_values[1], 3) shw_weight = shw_values[1] / total_load gas_weight = gas_values[1] / total_load if gas_weight > shw_weight: values = ['GAS', total_load, gas_values[2]] else: values = ['HOT-WATER', total_load, shw_values[2]] for shw_v, gas_v in zip(shw_values[3:], gas_values[3:]): new_v = (shw_v * shw_weight) + (gas_v * gas_weight) values.append(round(new_v, 3)) else: values = shw_values if shw_values is not None else gas_values return SOURCE_KEYS, values
[docs] def infiltration_to_inp(infiltration): """Translate an Infiltration definition into INP (Keywords, Values). Args: infiltration: A honeybee-energy Infiltration definition. None is allowed. Returns: A tuple with two elements. - keywords: A tuple of text strings for keywords related to defining infiltration for a Space. - values: A tuple of text strings that aligns with the keywords and denotes the value for each keyword. """ if infiltration is None: return (), () inf_den = infiltration.flow_per_exterior_area inf_den = VolumeFlowRateIntensity().to_unit([inf_den], 'cfm/ft2', 'm3/s-m2')[0] inf_den = round(inf_den, 3) inf_sch = clean_doe2_string(infiltration.schedule.identifier, RES_CHARS) inf_sch = '"{}"'.format(inf_sch) return INFILTRATION_KEYS, ('AIR-CHANGE', inf_den, inf_sch)
[docs] def setpoint_to_inp(setpoint): """Translate a Setpoint definition into INP (Keywords, Values). Args: setpoint: A honeybee-energy Setpoint definition. None is allowed. Returns: A tuple with two elements. - keywords: A tuple of text strings for keywords related to defining setpoints for a Zone. - values: A tuple of text strings that aligns with the keywords and denotes the value for each keyword. """ if setpoint is None: # use some default setpoints return ('DESIGN-HEAT-T', 'DESIGN-COOL-T'), (72, 75) heat_setpt = round(setpoint.heating_setpoint * (9. / 5.) + 32., 2) cool_setpt = round(setpoint.cooling_setpoint * (9. / 5.) + 32., 2) heat_sch = clean_doe2_string(setpoint.heating_schedule.identifier, RES_CHARS) heat_sch = '"{}"'.format(heat_sch) cool_sch = clean_doe2_string(setpoint.cooling_schedule.identifier, RES_CHARS) cool_sch = '"{}"'.format(cool_sch) return SETPOINT_KEYS, (heat_setpt, cool_setpt, heat_sch, cool_sch)
[docs] def ventilation_to_inp(ventilation): """Translate a Ventilation definition into INP (Keywords, Values). Args: ventilation: A honeybee-energy Ventilation definition. None is allowed. Returns: A tuple with two elements. - keywords: A list of text strings for keywords related to defining ventilation for a Space. - values: A list of text strings that aligns with the keywords and denotes the value for each keyword. """ keywords, values = [], [] if ventilation is None: return keywords, values # check the flow per person ppl_den = ventilation.flow_per_person if ppl_den != 0: keywords.append('OA-FLOW/PER') ppl_den = VolumeFlowRate().to_unit([ppl_den], 'cfm', 'm3/s')[0] values.append(round(ppl_den, 3)) # check the flow per floor area vent_den = ventilation.flow_per_area if vent_den != 0: keywords.append('OA-FLOW/AREA') vent_den = VolumeFlowRateIntensity().to_unit([vent_den], 'cfm/ft2', 'm3/s-m2')[0] values.append(round(vent_den, 3)) # check the air changes per hour ach = ventilation.air_changes_per_hour if ach != 0: keywords.append('OA-CHANGES') values.append(round(ach, 3)) # check the flow per zone total_flow = ventilation.flow_per_zone if total_flow != 0: keywords.append('OUTSIDE-AIR-FLOW') total_flow = VolumeFlowRate().to_unit([total_flow], 'cfm', 'm3/s')[0] values.append(round(total_flow, 3)) # check the schedule vent_sch = ventilation.schedule if vent_sch is not None: keywords.append('MIN-FLOW-SCH') vent_sch = clean_doe2_string(vent_sch.identifier, RES_CHARS) values.append('"{}"'.format(vent_sch)) return keywords, values
[docs] def lighting_from_inp(resolved, schedule=None): """Create a Lighting object from resolved INP attributes. Args: resolved: A dict of resolved INP key-value pairs. Expected keys: LIGHTING-W/AREA, LIGHTING-SCHEDUL or LIGHTING-SCHEDULE, LIGHT-TO-RETURN, LIGHT-RAD-FRAC. schedule: Optional ScheduleRuleset for lighting. If None, a constant placeholder schedule is created. Returns: A honeybee-energy Lighting object, or None if data is insufficient. """ lpd = resolved.get('LIGHTING-W/AREA') if lpd is None: return None # Convert W/ft2 to W/m2 lpd_si = EnergyFlux().to_unit([float(lpd)], 'W/m2', 'W/ft2')[0] sch_id = _clean_schedule_id( resolved.get('LIGHTING-SCHEDUL') or resolved.get('LIGHTING-SCHEDULE')) if schedule is None: schedule = ScheduleRuleset.from_constant_value(sch_id, 1, fractional) ret_fract = float(resolved.get('LIGHT-TO-RETURN', 0)) rad_fract = float(resolved.get('LIGHT-RAD-FRAC', 0.32)) vis_fract = 0.25 if ret_fract + rad_fract + vis_fract > 1: vis_fract = 1 - ret_fract - rad_fract return Lighting(sch_id, lpd_si, schedule, ret_fract, rad_fract, vis_fract)
[docs] def electric_equipment_from_inp(resolved, schedule=None): """Create an ElectricEquipment object from resolved INP attributes. Args: resolved: A dict of resolved INP key-value pairs. Expected keys: EQUIPMENT-W/AREA, EQUIP-SCHEDULE, EQUIP-SENSIBLE, EQUIP-LATENT, EQUIP-RAD-FRAC. schedule: Optional ScheduleRuleset for equipment. If None, a constant placeholder schedule is created. Returns: A honeybee-energy ElectricEquipment object, or None if data is insufficient. """ epd = resolved.get('EQUIPMENT-W/AREA') if epd is None: return None epd_val = float(epd) if epd_val == 0: return None # Convert W/ft2 to W/m2 epd_si = EnergyFlux().to_unit([epd_val], 'W/m2', 'W/ft2')[0] sch_id = _clean_schedule_id(resolved.get('EQUIP-SCHEDULE')) if schedule is None: schedule = ScheduleRuleset.from_constant_value(sch_id, 1, fractional) rad_fract = float(resolved.get('EQUIP-RAD-FRAC', 0)) lat_fract = float(resolved.get('EQUIP-LATENT', 0)) sens_fract = float(resolved.get('EQUIP-SENSIBLE', 1)) lost_fract = max(0, 1 - sens_fract - lat_fract) return ElectricEquipment(sch_id, epd_si, schedule, rad_fract, lat_fract, lost_fract)
[docs] def infiltration_from_inp(resolved, schedule=None): """Create an Infiltration object from resolved INP attributes. Args: resolved: A dict of resolved INP key-value pairs. Expected keys: INF-METHOD, INF-FLOW/AREA, INF-SCHEDULE. schedule: Optional ScheduleRuleset for infiltration. If None, a constant placeholder schedule is created. Returns: A honeybee-energy Infiltration object, or None if data is insufficient. """ inf_den = resolved.get('INF-FLOW/AREA') if inf_den is None: return None # Convert cfm/ft2 to m3/s-m2 inf_si = VolumeFlowRateIntensity().to_unit( [float(inf_den)], 'm3/s-m2', 'cfm/ft2')[0] sch_id = _clean_schedule_id(resolved.get('INF-SCHEDULE')) if schedule is None: schedule = ScheduleRuleset.from_constant_value(sch_id, 1, fractional) return Infiltration(sch_id, inf_si, schedule)
[docs] def setpoint_from_inp(resolved, heating_schedule=None, cooling_schedule=None): """Create a Setpoint object from resolved INP attributes. Args: resolved: A dict of resolved INP key-value pairs. Expected keys: DESIGN-HEAT-T, DESIGN-COOL-T, HEAT-TEMP-SCH, COOL-TEMP-SCH. heating_schedule: Optional ScheduleRuleset for heating setpoint. If None, a constant schedule at design temp is created. cooling_schedule: Optional ScheduleRuleset for cooling setpoint. If None, a constant schedule at design temp is created. Returns: A honeybee-energy Setpoint object, or None if data is insufficient. """ heat_sch_id = _clean_schedule_id(resolved.get('HEAT-TEMP-SCH')) cool_sch_id = _clean_schedule_id(resolved.get('COOL-TEMP-SCH')) heat_t = resolved.get('DESIGN-HEAT-T') cool_t = resolved.get('DESIGN-COOL-T') has_sch = resolved.get('HEAT-TEMP-SCH') or resolved.get('COOL-TEMP-SCH') if heat_t is None and cool_t is None and not has_sch: return None # Convert F to C for setpoints heat_c = (float(heat_t) - 32) * 5 / 9 if heat_t is not None else 21 cool_c = (float(cool_t) - 32) * 5 / 9 if cool_t is not None else 24 if heating_schedule is None: heating_schedule = ScheduleRuleset.from_constant_value( heat_sch_id, heat_c, temperature) if cooling_schedule is None: cooling_schedule = ScheduleRuleset.from_constant_value( cool_sch_id, cool_c, temperature) return Setpoint(heat_sch_id, heating_schedule, cooling_schedule)
[docs] def ventilation_from_inp(resolved, schedule=None): """Create a Ventilation object from resolved INP attributes. Args: resolved: A dict of resolved INP key-value pairs. Expected keys: OA-FLOW/PER, OA-FLOW/AREA, OA-CHANGES, OUTSIDE-AIR-FLOW, MIN-FLOW-SCH. schedule: Optional ScheduleRuleset for min outdoor air. If None, a constant placeholder schedule is created. Returns: A honeybee-energy Ventilation object, or None if data is insufficient. """ flow_per = resolved.get('OA-FLOW/PER') flow_area = resolved.get('OA-FLOW/AREA') ach = resolved.get('OA-CHANGES') total_flow = resolved.get('OUTSIDE-AIR-FLOW') if all(v is None for v in (flow_per, flow_area, ach, total_flow)): return None # Convert cfm to m3/s for per-person flow fp_si = VolumeFlowRate().to_unit( [float(flow_per)], 'm3/s', 'cfm')[0] if flow_per else 0 # Convert cfm/ft2 to m3/s-m2 for per-area flow fa_si = VolumeFlowRateIntensity().to_unit( [float(flow_area)], 'm3/s-m2', 'cfm/ft2')[0] if flow_area else 0 ach_val = float(ach) if ach else 0 # Convert cfm to m3/s for per-zone flow fz_si = VolumeFlowRate().to_unit( [float(total_flow)], 'm3/s', 'cfm')[0] if total_flow else 0 sch_id = _clean_schedule_id(resolved.get('MIN-FLOW-SCH')) # Use provided schedule or create a placeholder vent_sch = schedule if vent_sch is None and resolved.get('MIN-FLOW-SCH') is not None: vent_sch = ScheduleRuleset.from_constant_value( sch_id, 1, fractional) return Ventilation(sch_id, fp_si, fa_si, fz_si, ach_val, vent_sch)